【发布时间】: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 问题。但也许你也可以在这个帖子中回答这个问题:)。问题是关于@IndexedEmbedded 的includeEmbeddedObjectId 参数。我想我知道这是做什么的,但我想得到你的确认。
我假设当我将其设置为true 时,父实体的 id 将包含在子实体的 lucene 文档中,对吗?假设这个父实体用于matching() 查询,该查询用作true/false 条件。那么假设搜索会更快是正确的吗,因为现在也可以在子实体的 lucene 文档中找到 id?
谢谢
【问题讨论】:
-
关于
includeEmbeddedObjectId:它包括嵌入对象中关联所针对的实体的ID。所以如果MyIndexedEntity有一个属性@ManyToOne Language language,你把@IndexedEmbedded(includeEmbeddedObjectId = true)放在那个属性上,Language实体的ID叫做languageId,你会在索引中得到一个新字段language.languageIdMyIndexedEntity。该字段将自动填充...语言ID。
标签: spring-boot elasticsearch lucene hibernate-search hibernate-5.x