我使用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是一个相当常见的用例。
答案 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);
...