Hibernate Search / Lucene - 使用单个查询搜索和排名不相关的实体

Hibernate Search / Lucene - Search for and rank unrelated entities with a single query

我有一个使用 hibernate search 和 lucene 的全文搜索,因为我可以跨特定字段成功搜索给定的模型实体。

但是,我不想一次搜索一种类型的实体,而是想实施 'universal' 搜索,同时搜索不同的实体类型,将搜索短语与每个不同实体的适当字段相匹配实体类型,然后根据与搜索词的相关性对结果进行排名,而不考虑实体类型。

例如,假设我有不同的实体,Foo 和 Bar

@Entity
@Indexed
@AnalyzerDef(
  name="fulltext",
  tokenizer=@TokenizerDef(factory=StandardTokenizerFactory.class),
  filters={
    @TokenFilterDef(factory=LowerCaseFilterFactory.class),
    @TokenFilterDef(factory=SnowballPorterFilterFactory.class, 
      params={@Parameter(name="language", value="English") })
  }
)
public class Foo {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Integer fooId;

  @Column
  @Field 
  @Analyzer(definition="fulltext") 
  private String fieldA;

  ...

@Entity
@Indexed
public class Bar {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Integer barId;

  @Column
  @Field 
  @Analyzer(definition="fulltext") 
  private String fieldB;

  @Column
  @Field 
  @Analyzer(definition="fulltext") 
  private String fieldC;

  ...

所以我想搜索 "some text" 并匹配 Foo.fieldA 和 Bar.fieldB and/or Bar.fieldC

我当前使用的搜索类型特定于特定实体,例如:

fullTextSession = Search.getFullTextSession(hibernateSession);
Query query = fullTextSession.createFullTextQuery(
                fullTextSession
                  .getSearchFactory()
                  .buildQueryBuilder()
                  .forEntity(Foo.class)
                  .get()
                  .keyword()
                  .onFields("fieldA")
                  .matching("some text")
                  .createQuery(),
              Foo.class);
 query.list() // gets ranked list of Foo entities matching "some text"

很明显,上面的Lucene查询是针对Foo实体的,甚至Foo.fieldA

那么,是否可以修改 Lucene 查询以同时包含 Bar 结果,匹配 Bar.fieldB 和 Bar.fieldC 字段?

我知道 fullTextSession.createFullTextQuery(fulltextSession, Class...) 方法也将接受 Bar.class 到 return Bar 结果,但我不知道如何修改实际查询以搜索 Bar 实体第一名。


我正在考虑解决这个问题的另一种方法是进行单独的查询,一个用于 Foo 实体,一个用于 Bar 实体,然后合并两个结果集并按 'match relevance score' 对它们进行排序 -但我也找不到如何获得结果分数!

编辑 上述方法可能行不通 - 事实证明您可以通过预测获得结果的分数,但 the docs 声明无法对来自不同查询的分数进行有意义的比较:

FullTextQuery.SCORE: returns the document score in the query. Scores are handy to compare one result against an other for a given query but are useless when comparing the result of different queries.


抱歉,如果我在这里覆盖了很多人,但我一直在错误的地方搜索了好几个小时,但在文档中找不到任何有用的东西,这让我很沮丧这是 Lucene 的一个相当常见的用例。

您可以编写两个查询并使用 Occur.SHOULD 通过 BooleanQuery 组合它们。然后使用 createFullTextQuery(booleanQuery, Foo.class, Bar.class); 搜索两种类型的实体。

受 Hardy 回答的启发,我使用了一个带有两个子句的 BooleanQuery,两个子句都带有一个 Occur.SHOULD,它们有效地充当了 OR。这会产生所需的查询行为。

代码如下:

...

fullTextSession = Search.getFullTextSession(hibernateSession);
String searchPhrase = "some text";

org.apache.lucene.search.Query fooQuery =
  fullTextSession
    .getSearchFactory()
    .buildQueryBuilder()
    .forEntity(Foo.class)
    .get()
    .keyword()
    .onFields("fieldA")
    .matching(searchPhrase)
    .createQuery();

org.apache.lucene.search.Query barQuery =
  fullTextSession
    .getSearchFactory()
    .buildQueryBuilder()
    .forEntity(Bar.class)
    .get()
    .keyword()
    .onFields("fieldB", "fieldC")
    .matching(searchPhrase)
    .createQuery();

BooleanQuery query = new BooleanQuery();
query.add(new BooleanClause(fooQuery, BooleanClause.Occur.SHOULD));
query.add(new BooleanClause(barQuery, BooleanClause.Occur.SHOULD));

Query hibernateQuery = 
        fullTextSession.createFullTextQuery(query, Foo.class, Bar.class);

...