【问题标题】:How to exclude or ignore field when building a generic Lucene Query to run against more than 1 Entity构建通用 Lucene 查询以针对多个实体运行时如何排除或忽略字段
【发布时间】:2018-12-18 18:24:53
【问题描述】:

我们正在将我们的 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 个实体有。但是一切都很好,没有给出任何例外,我相信它只是忽略了未知/未索引的字段,不完全确定它是如何工作的,但确实如此。

现在我们正在转换为 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 作为索引列/字段,createQuery() 会引发异常:

 SearchException: Unable to find field taxId in EntityOne

我的问题是:

  1. 如果实体没有该字段,是否有办法告诉 Lucene 忽略该字段?

  2. 1234563 '不包含在整体查询中?

【问题讨论】:

    标签: java elasticsearch lucene hibernate-search


    【解决方案1】:

    如果实体没有该字段,是否有办法告诉 Lucene 忽略该字段?

    澄清一下:实现 DSL 并引发异常的是 Hibernate Search,而不是 Lucene。 Lucene 是底层技术,不会执行太多验证。

    如果您的目标是在单个结果列表中检索所有三个实体,并且不同实体类型中具有相同名称的字段配置类似(例如,字段“名称”出现在实体 1 和 2 中,但具有相同的分析器),您可以简单地构建一个查询并在该单个查询中检索所有三种类型。您必须:

    • 确保在构建单个 Lucene 查询时,始终使用实际定义了您的目标字段的实体类型的查询构建器:例如,如果目标为 taxId,您可以使用 EntityTwo 的查询构建器或EntityThree,但不是EntityOne。是的,没错:您可以在单个查询中混合使用多个查询构建器,只要在所有目标实体中具有相同名称的字段配置相同即可。
    • 以这种方式构建FullTextQuerysession.createFullTextQuery(luceneQuery, EntityOne.class, EntityTwo.class, EntityThree.class);

    如果没有,有没有办法,使用传入的 QueryBuilder 来确定实体是什么,这样,在 taxId 子查询代码中,我基本上可以说 if (entityType == EntityOne) {return null;} 这样这个特定的子查询不会包含在整个查询中?

    不,没有。不过,您可以向您的方法添加一个参数以传递实体类型:buildMyQuery(Class<?> type, QueryBuilder queryBuilder) 而不是 buildMyQuery(QueryBuilder queryBuilder)

    【讨论】:

    • 澄清一下,我了解到您正在将使用 Solr 的应用程序(不使用 Hibernate Search)迁移到 Hibernate Search + Elasticsearch。如果不是这种情况,并且您一直在使用 Hibernate Search,那么请提供您曾经和正在使用的 Hibernate Search 版本。
    • 尤里卡!我想知道为什么有必要在构建 QueryBuilder 时声明实体,然后在 createFullTextQuery 中再次声明实体。您可以基于 1 个实体构建查询构建器,然后基于另一个实体运行实际查询这一事实从未发生在我身上。我完全按照您在第一个要点中所说的做了(使用 entityTwo 构建查询,然后为所有 3 个实体运行它),Hibernate Search 再次感到高兴。
    • 很高兴它成功了。但是请注意,这仅在 EntityTwo 声明了您将在查询中提到的所有字段时才有效。如果 EntityOne 声明字段“fieldOnlyInEntityOne”,那么您最终会遇到完全相同的问题,并且您必须使用 EntityOne 的查询构建器来查询该字段,以及使用 EntityTwo 的查询构建器来查询其他字段。
    猜你喜欢
    • 1970-01-01
    • 2010-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-17
    • 2013-10-18
    • 1970-01-01
    • 2023-03-08
    相关资源
    最近更新 更多