如何在使用 HibernateSearch QueryDSL 构建的 Elasticsearch 查询中包含 prefixLength

How to include prefixLength in Elasticsearch query built using HibernateSearch QueryDSL

我们使用 Hibernate Search 5.10。3.Final 针对 Elasticsearch 5.6.6 服务器。

在创建要传递给 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

当术语 "rooster" 通过此方法发送时,它的 editDistance(模糊度)为 1,prefixLength 为 1。

检查日志并查看发送到 ES 的内容,我希望在 "fuzziness" 正下方看到 "prefix_length",但它不在那里:

{
  "query": {
    "bool": {
      "must": {
        "match": {
          "vendorNames.vendorName": {
            "query": "rooster",
            "fuzziness": 1
          }
        }
      }
    }
  }
  1. 为什么 FuzzyContext 允许设置 prefixLength 但不使用它?
  2. 尝试包含 prefixLength 是否真的值得提高 ES 性能(我测试了直接对 ES 的 REST 查询调用,无论是否使用 prefixLength 并且没有注意到响应时间差异)?
  3. 如何让 prefixLength 包含在发送到 ES 的实际查询中?

Why does FuzzyContext allow for setting the prefixLength but not use it?

这是 Elasticsearch 集成的一个错误,但直到现在才报告:谢谢!我们将尝试在下一个开发周期中修复它:HSEARCH-3545

Is it really worth the ES performance increase to try to include prefixLength (I tested REST query calls directly to ES both with and without prefixLength and didn't notice a response time difference)?

prefixLength 更多的是关于结果的相关性而不是性能。这个想法是,如果用户给我们一个 10 个字符长的词,我们可能会得到很多模糊匹配,其中大部分可能是不相关的。通过忽略前 5 个字符(例如),我们将关注单词末尾附近的模糊性,这可能不太相关(想想 "theory"/"theories"、"constituting"/"constitute", 等): 这样我们会得到更少的模糊匹配,但它们会更相关。

至少理论上是这样:)

How can I get prefixLength to be included in the actual query sent to 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, not just a fuzzy query),那么您唯一的解决方案是将 whole 查询编写为 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 中的内容。