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

xtupzzrd  于 2022-11-07  发布在  Lucene
关注(0)|答案(1)|浏览(160)

我使用hibsearch构造了一个lucene查询,它返回包含(部分)搜索字符串的字符串值。其次,如果language id也匹配,并且deleted标志没有设置为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;
}

获取匹配字符串条件

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

创建语言 predicate

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对字段进行注解来强制将其视为文本字段。但我不想这样做。因此我的问题是:如何对布尔值、日期和数字等非文本字段执行匹配查询?
EDIT:如果我用@FieldBridge(impl= implementation.class)'注解过滤所需的所有字段,并且index参数必须总是设置为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,这是正确的吗?
谢谢

nafvub8i

nafvub8i1#

在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,但不那么冗长,也不那么古怪。请参阅Hibernate Search 6中的Search DSL文档,特别是 predicate DSL。

相关问题