如何在针对超过 1 个实体构建针对 运行 的通用 Lucene 查询时排除或忽略字段
How to exclude or ignore field when building a generic Lucene Query to run against more than 1 Entity
我们正在将 java 应用程序从使用 SOLR/Lucene 转换为 Elasticsearch 5.6.6。
在 1 个特定区域,我们之前会构建 1 个 Lucene Search BooleanQuery 并 运行 它针对 3 个不同的实体,寻找匹配项。在您 运行 实际查询之前,我们不需要指定将使用它的实体。
BooleanQuery luceneQuery = myQueryBuilder.buildMyQuery();
session.createFullTextQuery(luceneQuery, entityOne);
session.createFullTextQuery(luceneQuery, entityTwo);
session.createFullTextQuery(luceneQuery, entityThree);
上面 [luceneQuery] 中的一个子查询在 taxId 上搜索,entityOne 没有(没有 taxId 索引字段)但其他 2 个实体有。然而,一切都很好,没有例外,我相信它只是忽略了 unknown/un-indexed 字段,不确定它是如何工作的,但确实如此。
现在我们正在转换到 Elasticsearch DSL,我们需要预先提供实体,所以我(无论好坏)针对每个实体构建 3 次不同的查询,如下所示:
QueryBuilder entityOneQB = session.getSearchFactory().buildQueryBuilder().forEntity(EntityOne.class).get();
QueryBuilder entityTwoQB = session.getSearchFactory().buildQueryBuilder().forEntity(EntityTwo.class).get();
QueryBuilder entityThreeQB = session.getSearchFactory().buildQueryBuilder().forEntity(EntityThree.class).get();
// Create 3 exact (except for which entity they point at) queries
Query entityOneQuery = myQueryBuilder.buildMyQuery(entityOne);
Query entityTwoQuery = myQueryBuilder.buildMyQuery(entityTwo);
Query entityThreeQuery = myQueryBuilder.buildMyQuery(entityThree);
其中 buildMyQuery() 有许多子查询,但处理 taxId 的子查询类似于:
qb.bool().should(
qb.keyword()
.onField("taxId")
.matching(taxId)
.createQuery()
);
但是,现在,由于 entityOne 没有作为索引的 taxId column/field,createQuery()
抛出异常:
SearchException: Unable to find field taxId in EntityOne
我的问题是:
如果实体没有该字段,有什么方法可以告诉 Lucene 忽略该字段?
如果没有,有没有办法使用传入的 QueryBuilder 来确定实体是什么,这样,在 taxId 子查询代码中,我基本上可以说 if (entityType == EntityOne) {return null;}
这样这个特定的子查询不会包含在整个查询中?
Is there some way to tell Lucene to ignore the field if the entity doesn't have it?
澄清一下:实现 DSL 并抛出异常的是 Hibernate Search,而不是 Lucene。 Lucene 是底层技术,并没有执行太多验证。
如果您的目标是在单个结果列表中检索所有三个实体,并且如果不同实体类型中具有相同名称的字段配置相似(例如字段 "name" 出现在实体 1 和 2 中,但是具有相同的分析器),您可以简单地构建一个查询并在该查询中检索所有三种类型。您将必须:
- 确保在构建单个 Lucene 查询时,始终使用实际定义目标字段的实体类型的查询构建器:例如,如果定位
taxId
,您可以使用查询构建器EntityTwo
或 EntityThree
,但不是 EntityOne
。是的,没错:您可以在单个查询中混合使用多个查询构建器,只要具有相同名称的字段在所有目标实体中的配置相似即可。
- 以这种方式构建
FullTextQuery
:session.createFullTextQuery(luceneQuery, EntityOne.class, EntityTwo.class, EntityThree.class);
。
If not, is there some way, using the passed in QueryBuilder to determine what the entity is, so that, within the taxId subquery code, I can basically say if (entityType == EntityOne) {return null;} so that this particular sub-query won't be included in the overall query?
不,没有。不过,您可以向您的方法添加一个参数以传递实体类型:buildMyQuery(Class<?> type, QueryBuilder queryBuilder)
而不是 buildMyQuery(QueryBuilder queryBuilder)
.
我们正在将 java 应用程序从使用 SOLR/Lucene 转换为 Elasticsearch 5.6.6。
在 1 个特定区域,我们之前会构建 1 个 Lucene Search BooleanQuery 并 运行 它针对 3 个不同的实体,寻找匹配项。在您 运行 实际查询之前,我们不需要指定将使用它的实体。
BooleanQuery luceneQuery = myQueryBuilder.buildMyQuery();
session.createFullTextQuery(luceneQuery, entityOne);
session.createFullTextQuery(luceneQuery, entityTwo);
session.createFullTextQuery(luceneQuery, entityThree);
上面 [luceneQuery] 中的一个子查询在 taxId 上搜索,entityOne 没有(没有 taxId 索引字段)但其他 2 个实体有。然而,一切都很好,没有例外,我相信它只是忽略了 unknown/un-indexed 字段,不确定它是如何工作的,但确实如此。
现在我们正在转换到 Elasticsearch DSL,我们需要预先提供实体,所以我(无论好坏)针对每个实体构建 3 次不同的查询,如下所示:
QueryBuilder entityOneQB = session.getSearchFactory().buildQueryBuilder().forEntity(EntityOne.class).get();
QueryBuilder entityTwoQB = session.getSearchFactory().buildQueryBuilder().forEntity(EntityTwo.class).get();
QueryBuilder entityThreeQB = session.getSearchFactory().buildQueryBuilder().forEntity(EntityThree.class).get();
// Create 3 exact (except for which entity they point at) queries
Query entityOneQuery = myQueryBuilder.buildMyQuery(entityOne);
Query entityTwoQuery = myQueryBuilder.buildMyQuery(entityTwo);
Query entityThreeQuery = myQueryBuilder.buildMyQuery(entityThree);
其中 buildMyQuery() 有许多子查询,但处理 taxId 的子查询类似于:
qb.bool().should(
qb.keyword()
.onField("taxId")
.matching(taxId)
.createQuery()
);
但是,现在,由于 entityOne 没有作为索引的 taxId column/field,createQuery()
抛出异常:
SearchException: Unable to find field taxId in EntityOne
我的问题是:
如果实体没有该字段,有什么方法可以告诉 Lucene 忽略该字段?
如果没有,有没有办法使用传入的 QueryBuilder 来确定实体是什么,这样,在 taxId 子查询代码中,我基本上可以说
if (entityType == EntityOne) {return null;}
这样这个特定的子查询不会包含在整个查询中?
Is there some way to tell Lucene to ignore the field if the entity doesn't have it?
澄清一下:实现 DSL 并抛出异常的是 Hibernate Search,而不是 Lucene。 Lucene 是底层技术,并没有执行太多验证。
如果您的目标是在单个结果列表中检索所有三个实体,并且如果不同实体类型中具有相同名称的字段配置相似(例如字段 "name" 出现在实体 1 和 2 中,但是具有相同的分析器),您可以简单地构建一个查询并在该查询中检索所有三种类型。您将必须:
- 确保在构建单个 Lucene 查询时,始终使用实际定义目标字段的实体类型的查询构建器:例如,如果定位
taxId
,您可以使用查询构建器EntityTwo
或EntityThree
,但不是EntityOne
。是的,没错:您可以在单个查询中混合使用多个查询构建器,只要具有相同名称的字段在所有目标实体中的配置相似即可。 - 以这种方式构建
FullTextQuery
:session.createFullTextQuery(luceneQuery, EntityOne.class, EntityTwo.class, EntityThree.class);
。
If not, is there some way, using the passed in QueryBuilder to determine what the entity is, so that, within the taxId subquery code, I can basically say if (entityType == EntityOne) {return null;} so that this particular sub-query won't be included in the overall query?
不,没有。不过,您可以向您的方法添加一个参数以传递实体类型:buildMyQuery(Class<?> type, QueryBuilder queryBuilder)
而不是 buildMyQuery(QueryBuilder queryBuilder)
.