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

时间:2015-05-08 23:28:54

标签: java lucene full-text-search hibernate-search

我使用hibernate search和lucene进行全文搜索,因为我可以在特定字段中成功搜索给定的模型实体。

但是,我不是一次只搜索一种类型的实体,而是希望实现“通用”搜索,同时搜索不同的实体类型,将搜索短语与每个不同实体类型上的相应字段相匹配,然后将结果与搜索词的相关性排序,而不考虑实体类型。

例如,假设我有不同的实体,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和/或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,返回Bar结果,但我不知道如何修改实际查询以首先搜索Bar实体。

我想解决这个问题的另一种方法是做单独的查询,一个用于Foo实体,一个用于Bar实体,然后合并两个结果集并按“匹配相关性得分”排序 - 但我可以'找到如何获得结果的分数!

修改 上述方法可能不起作用 - 事实证明,您可以通过预测获得结果的分数,但the docs表示单独查询的分数无法进行有意义的比较:

  

FullTextQuery.SCORE:返回查询中的文档分数。对于给定查询,分数可以方便地将一个结果与另一个结果进行比较,但在比较不同查询的结果时无用。

道歉,如果我在这里覆盖了很好的地方,但我一直在寻找错误的地方几个小时,并且在文档中找不到任何有用的东西,这是令人沮丧的,因为我想这是Lucene是一个相当常见的用例。

2 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:0)

受到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);

...