【问题标题】:How to match numeric and boolean values in a lucene query如何在 lucene 查询中匹配数值和布尔值
【发布时间】:2021-12-25 03:00:43
【问题描述】:

我正在使用休眠搜索来构造一个 lucene 查询,该查询返回包含(部分)搜索字符串的字符串值。接下来,如果语言 id 也匹配并且已删除标志未设置为 true,则查询必须仅返回字符串值。我为此制作了以下代码。但问题是它没有返回任何东西。

private Query getQueryWithBooleanClauses(Class entityClass, String searchString, Long preferredLanguageId, FullTextEntityManager fullTextEntityManager, String firstField, String... additionalFields) {
    QueryBuilder queryBuilder = getQueryBuilder(entityClass, fullTextEntityManager);
    Query containsSearchString = getMatchingStringCondition(searchString, queryBuilder, firstField, additionalFields);
    BooleanQuery isPreferredOrDefaultLanguageTranslation = getLanguageCondition(preferredLanguageId);
    BooleanQuery finalQuery = new BooleanQuery.Builder()
            .add(new TermQuery(new Term("parentDeleted", "false")), BooleanClause.Occur.MUST)
            .add(new TermQuery(new Term("parentApproved", "true")), BooleanClause.Occur.MUST)
            .add(new TermQuery(new Term("childDeleted", "false")), BooleanClause.Occur.MUST)
            .add(isPreferredOrDefaultLanguageTranslation, BooleanClause.Occur.MUST)
            .add(containsSearchString, BooleanClause.Occur.MUST)
            .build();
    return finalQuery;
}

getMatchingStringCondition

private Query getMatchingStringCondition(String searchString, QueryBuilder queryBuilder, String firstField, String... additionalFields) {
    log.info(MessageFormat.format("{0}*", searchString));
    return queryBuilder.simpleQueryString()
            .onFields(firstField, additionalFields)
            .withAndAsDefaultOperator()
            .matching(MessageFormat.format("{0}*", searchString))
            .createQuery();
}

getLanguageCondition

private BooleanQuery getLanguageCondition(Long preferredLanguageId) {
    return new BooleanQuery.Builder()
            .add(createLanguagePredicate(preferredLanguageId), BooleanClause.Occur.SHOULD)
            .add(createLanguagePredicate(languageService.getDefaultLanguage().getId()), BooleanClause.Occur.SHOULD)
            .build();
}

创建语言谓词

private Query createLanguagePredicate(Long languageId){
    return new TermQuery(new Term("language.languageId", languageId.toString()));
}

查询执行方法

public List<AutoCompleteSuggestion> findAllBySearchStringAndDeletedIsFalse(Class entityClass, String searchString, Long preferredLanguageId){
    FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
    Query finalQuery = getQueryWithBooleanClauses(entityClass, searchString, preferredLanguageId, fullTextEntityManager, "parent.latinName", "translatedName");
    FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(finalQuery, entityClass);
    fullTextQuery.setProjection("parentId", "autoCompleteSuggestion", "childApproved"); //volgorde moet overeen komen met argumenten volgorde in AutoCompleteSuggestion constructor, zie convertToAutoCompleteSuggestionList
    fullTextQuery.setMaxResults(maxResults);
    fullTextQuery.getResultList();
return convertToAutoCompleteSuggestionList(fullTextQuery.getResultList());
}

此代码不会引发错误,但也不会返回任何内容。只有当我删除布尔和数字字段的所有布尔条件,只留下containsSearchString 条件时,查询才会返回任何内容。

根据这篇帖子Hibernate Search 5.0 Numeric Lucene Query HSEARCH000233 issue,发生这种情况是因为从 Hibernate 搜索开始,5 个数字字段不再被视为文本字段,您无法对数字字段执行匹配查询。

您可以通过使用@FieldBridge 注释字段来强制将字段视为文本字段。但我宁愿不这样做。所以我的问题是。如何对布尔值、日期和数字等非文本字段执行匹配查询?

编辑:如果我用@FieldBridge(impl= implementation.class)`注释过滤所需的所有字段,它就可以工作,而且索引参数必须始终设置为YES。

但是现在所有这些字段都将存储为字符串,这是不可取的。所以我仍然想知道是否有另一种更优雅的方式来应用过滤器。

编辑 2:

@yrodiere,当我从languageId 中删除@FieldBridge(impl = LongBridge.class) 并将.add(isPreferredOrDefaultLanguageTranslation, BooleanClause.Occur.MUST) 行替换为:

.add(queryBuilder.bool().must(queryBuilder.keyword().onField("language.languageId").matching(languageService.getDefaultLanguage().getId().toString()).createQuery()).createQuery(), BooleanClause.Occur.MUST)

我得到错误:

org.hibernate.search.exception.SearchException: HSEARCH000238: Cannot create numeric range query for field 'language.languageId', since values are not numeric (Date, int, long, short or double)

但是刚才我发现matching() 也接受Long 号码,所以我不必拨打toString() 就可以了。当matching() 使用Long 值时,我没有收到错误,但也没有返回任何内容。

只有当我使用new TermQuery(new Term("language.languageId", languageId.toString())) 而不是matching() 同时还使用LongBridge 作为languageId 时,才会返回任何内容。我是否错误地定义了matching() 查询?

我还有一个不同的问题,我想开始一个新的 SO 问题。但也许你也可以在这个帖子中回答这个问题:)。问题是关于@IndexedEmbeddedincludeEmbeddedObjectId 参数。我想我知道这是做什么的,但我想得到你的确认。

我假设当我将其设置为true 时,父实体的 id 将包含在子实体的 lucene 文档中,对吗?假设这个父实体用于matching() 查询,该查询用作true/false 条件。那么假设搜索会更快是正确的吗,因为现在也可以在子实体的 lucene 文档中找到 id?

谢谢

【问题讨论】:

  • 关于includeEmbeddedObjectId:它包括嵌入对象中关联所针对的实体的ID。所以如果MyIndexedEntity有一个属性@ManyToOne Language language,你把@IndexedEmbedded(includeEmbeddedObjectId = true)放在那个属性上,Language实体的ID叫做languageId,你会在索引中得到一个新字段language.languageId MyIndexedEntity。该字段将自动填充...语言ID。

标签: spring-boot elasticsearch lucene hibernate-search hibernate-5.x


【解决方案1】:

在 Hibernate Search 5 中,布尔值仍被索引为字符串。请参阅 org.hibernate.search.bridge.builtin.BooleanBridge。所以布尔字段不是问题的一部分。

如果你真的想自己创建数字查询,在 Hibernate Search 5 中你将不得不使用数字范围查询,例如:

private Query createLanguagePredicate(Long languageId){
    return org.apache.lucene.search.NumericRangeQuery.newLongRange("language.languageId", languageId, 
languageId, true, true);
}

话虽如此,为避免此类问题,您应该使用Hibernate Search DSL。然后,您将传递您在模型中使用的类型的值(此处为 Long),Hibernate Search 将自动创建正确的查询。

或者甚至更好,升级到Hibernate Search 6,它公开了一个不同的 API,但不那么冗长且怪癖更少。在documentation of the Search DSL in Hibernate Search 6 中亲自查看,尤其是predicate DSL

【讨论】:

  • @yorodiere 我已经在使用 Hibernate 搜索 DSL。在您的示例中,您使用的是数字范围查询。但我的问题涉及将language.languageId 的值与某个数字进行比较。当我尝试为此使用matching() 时,我得到一个例外,因为该字段是数字的,您只能在字符串字段上使用matching()。数字字段是否有 matching() 变体?
  • "你只能在字符串字段上使用matching()" => 这不是真的吗?至少不会使用 keyword() 查询,在这种情况下您应该使用它。如果您对与问题中显示的代码不同的代码有疑问,请将该代码添加到您的问题中。至少不看代码,我猜不出哪里出了问题。
  • 我在我的问题中添加了一些额外的信息,@yrodiere。它从EDIT 2: 开始。
  • 关于您的第二次编辑...您是否在更改映射(移除桥接)后确定reindex your data?因为更改字段的类型肯定需要重新索引。没有它,您很可能会得到您所得到的行为,即根本没有匹配项。
  • 现在一切正常@yrodiere。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2010-10-31
  • 1970-01-01
  • 2019-01-22
  • 2017-01-28
  • 2022-08-03
  • 1970-01-01
  • 1970-01-01
  • 2018-09-22
相关资源
最近更新 更多