我们在Elasticsearch 5.6.6服务器上使用Hibernate Search 5.10.3.Final。
在创建模糊查询以传递给FullTextSession :: createFullTextQuery时,我正在设置editDistance和prefixLength,但我从日志中注意到发送到Elasticsearch的实际查询不包含prefixLength。
此代码是从许多单独的方法中获取的,但这是基本的工作流程:
QueryBuilder qb = fts.getSearchFactory()
.buildQueryBuilder()
.forEntity(Vendor.class)
.get();
BooleanJunction namesBool = qb.bool();
String field = "vendorNames.vendorName";
String token = "rooster";
int editDistance = getEditDistance(token); //returns 1 for "rooster"
int prefixLength = getPrefixLength(token); //returns 1 for "rooster"
namesBool.must(
qb.keyword()
.fuzzy() //returns FuzzyContext
.withEditDistanceUpTo(editDistance)
.withPrefixLength(prefixLength)
.onField(field)
.matching(token)
.createQuery()
);
// ...
// calling FullTextSession::createFullTextQuery
当通过此方法发送术语“公鸡”时,它的editDistance(模糊性)为1,前缀长度为1。
检查日志并查看发送给ES的内容,我希望在“模糊”下面看到“prefix_length”,但它不存在:
{
"query": {
"bool": {
"must": {
"match": {
"vendorNames.vendorName": {
"query": "rooster",
"fuzziness": 1
}
}
}
}
}
为什么FuzzyContext允许设置prefixLength但不使用它?
这是Elasticsearch集成的一个错误,但直到现在才报告:谢谢!我们将在下一个开发周期中尝试修复它:HSEARCH-3545
尝试包含prefixLength是否真的值得ES性能提升(我使用和不使用prefixLength直接测试了REST查询调用,并且没有注意到响应时间差异)?
prefixLength
更多地是关于结果的相关性而不是性能。这个想法是,如果用户给我们一个10个字符长的单词,我们可能会得到很多模糊匹配,其中大部分都可能无关紧要。通过忽略前5个字符(例如),我们将把模糊性集中在单词的末尾附近,这可能不太相关(想想“理论”/“理论”,“构成”/“构成”等):这样我们将获得较少的模糊匹配,但它们将更具相关性。
至少那是理论:)
如何将prefixLength包含在发送给ES的实际查询中?
如果您不需要支持多个令牌,可以直接创建FuzzyQuery
:
BooleanJunction namesBool = qb.bool();
String field = "vendorNames.vendorName";
String token = "rooster";
int editDistance = getEditDistance(token); //returns 1 for "rooster"
int prefixLength = getPrefixLength(token); //returns 1 for "rooster"
namesBool.must(
new FuzzyQuery(new Term(field, token), editDistance, prefixLength)
);
此查询将被正确翻译。
如果您确实需要支持多个令牌(即您需要fuzzy match
query,而不仅仅是fuzzy
query),那么您唯一的解决方案是将整个查询编写为JSON并使用org.hibernate.search.elasticsearch.ElasticsearchQueries#fromJson
:
String field = "vendorNames.vendorName";
String token = "rooster";
int editDistance = getEditDistance(token); //returns 1 for "rooster"
int prefixLength = getPrefixLength(token); //returns 1 for "rooster"
QueryDescriptor queryDescriptor = ElasticsearchQueries.fromJson(
"{"
+ "\"query\": {"
+ "\"bool\": {"
+ "\"must\": {"
+ "\"match\": {"
+ "\"" + field + "\": {"
+ "\"query\": \"" + token + "\","
+ "\"fuzziness\": " + editDistance + ","
+ "\"prefix_length\": " + prefixLength
+ "}"
+ "}"
+ "}"
+ "}"
+ "}"
+ "}"
);
List<?> result = session.createFullTextQuery( queryDescriptor, MyEntity.class )
.list();
是的,它是满口的...我们正在改进Hibernate Search 6中的东西。