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);
...
我有一个使用 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);
...