如何在 Lucene 查询中匹配数值和布尔值

How to match numeric and boolean values in a lucene query

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

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();
}

获取语言条件

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());
}

此代码不会抛出错误,但也不会 return 任何错误。只有当我删除布尔和数字字段的所有布尔条件时,只留下 containsSearchString 条件才会查询 return 任何东西。

根据此 post 发生这种情况是因为从 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 值时,我没有收到错误,但也没有 returned。

只有当我使用 new TermQuery(new Term("language.languageId", languageId.toString())) 而不是 matching() 并且同时对 languageId 使用 LongBridge 时,所有内容才会得到 returned。我是否错误地定义了 matching() 查询?

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

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

谢谢

布尔值在 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, which exposes a different API, but less verbose and with fewer quirks. See for yourself in the documentation of the Search DSL in Hibernate Search 6, in particular the predicate DSL