[英]Autocomplete using Hibernate Search
我正在尝试为我的网站构建更好的自动完成功能。 我想为此使用Hibernate Search,但据我实验,它只能为我找到完整的单词。
所以,我的问题是:是否可以仅搜索某些字符?
例如。 用户键入3个字母,然后使用休眠搜索向他显示我的db对象中包含这3个字母的所有单词?
PS。 现在,我正在为此使用“赞”查询...但是我的数据库增长了很多,我还想将搜索功能扩展到另一个表上...
重大修改一年后,我得以改进发布的原始代码以产生此效果:
我的索引实体:
@Entity
@Indexed
@AnalyzerDef(name = "myanalyzer",
// Split input into tokens according to tokenizer
tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class), //
filters = { //
// Normalize token text to lowercase, as the user is unlikely to care about casing when searching for matches
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
// Index partial words starting at the front, so we can provide Autocomplete functionality
@TokenFilterDef(factory = NGramFilterFactory.class, params = { @Parameter(name = "maxGramSize", value = "1024") }),
// Close filters & Analyzerdef
})
@Analyzer(definition = "myanalyzer")
public class Compound extends DomainObject {
public static String[] getSearchFields(){...}
...
}
所有@Field
都被标记并存储在索引中; 工作所需的条件:
@Field(index = Index.TOKENIZED, store = Store.YES)
@Transactional(readOnly = true)
public synchronized List<String> getSuggestions(final String searchTerm) {
// Compose query for term over all fields in Compound
String lowerCasedSearchTerm = searchTerm.toLowerCase();
// Create a fullTextSession for the sessionFactory.getCurrentSession()
FullTextSession fullTextSession = Search.getFullTextSession(getSession());
// New DSL based query composition
SearchFactory searchFactory = fullTextSession.getSearchFactory();
QueryBuilder buildQuery = searchFactory.buildQueryBuilder().forEntity(Compound.class).get();
TermContext keyword = buildQuery.keyword();
WildcardContext wildcard = keyword.wildcard();
String[] searchfields = Compound.getSearchfields();
TermMatchingContext onFields = wildcard.onField(searchfields[0]);
for (int i = 1; i < searchfields.length; i++)
onFields.andField(searchfields[i]);
TermTermination matching = onFields.matching(input.toLowerCase());
Query query = matching.createQuery();
// Convert the Search Query into something that provides results: Specify Compound again to be future proof
FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(query, Compound.class);
fullTextQuery.setMaxResults(20);
// Projection does not work on collections or maps which are indexed via @IndexedEmbedded
List<String> projectedFields = new ArrayList<String>();
projectedFields.add(ProjectionConstants.DOCUMENT);
List<String> embeddedFields = new ArrayList<String>();
for (String fieldName : searchfields)
if (fieldName.contains("."))
embeddedFields.add(fieldName);
else
projectedFields.add(fieldName);
@SuppressWarnings("unchecked")
List<Object[]> results = fullTextQuery.setProjection(projectedFields.toArray(new String[projectedFields.size()])).list();
// Keep a list of suggestions retrieved by search over all fields
List<String> suggestions = new ArrayList<String>();
for (Object[] projectedObjects : results) {
// Retrieve the search suggestions for the simple projected field values
for (int i = 1; i < projectedObjects.length; i++) {
String fieldValue = projectedObjects[i].toString();
if (fieldValue.toLowerCase().contains(lowerCasedSearchTerm))
suggestions.add(fieldValue);
}
// Extract the search suggestions for the embedded fields from the document
Document document = (Document) projectedObjects[0];
for (String fieldName : embeddedFields)
for (Field field : document.getFields(fieldName))
if (field.stringValue().toLowerCase().contains(lowerCasedSearchTerm))
suggestions.add(field.stringValue());
}
// Return the composed list of suggestions, which might be empty
return suggestions;
}
最后,我正在做一些争吵来处理@IndexedEmbedded字段。 如果您没有这些,则可以简化整个代码,只需投影searchFields,而忽略文档和EmbeddedField处理。
和以前一样:希望这对下一个遇到此问题的人很有用。 如果有人对以上发布的代码有任何批评或改进,请随时进行编辑,并请告知我。
Edit3 :此代码所来自的项目自此是开源的。 以下是相关的类:
https://trac.nbic.nl/metidb/browser/trunk/metidb/metidb-core/src/main/java/org/metidb/domain/Compound.java
https://trac.nbic.nl/metidb/browser/trunk/metidb/metidb-core/src/main/java/org/metidb/dao/CompoundDAOImpl.java
https://trac.nbic.nl/metidb/browser/trunk/metidb/metidb-search/src/main/java/org/metidb/search/text/Autocompleter.java
您可以按照此处建议使用NGramFilter对该字段建立索引。 为了获得最佳结果,您应该使用Apache Solr的EdgeNgramFilter ,它从术语的开始边缘开始创建ngram,也可以在休眠搜索中使用。
蒂姆的回答很出色,并帮助我克服了困难的部分。 对我来说,它仅适用于单个单词查询。 如果有人希望它使它适用于短语搜索。 只需将所有“术语”实例替换为其相应的“短语”类即可。 这是蒂姆的代码的替代行,这些代码对我有用。
// New DSL based query composition
//org.hibernate.search.query.dsl
SearchFactory searchFactory = fullTextSession.getSearchFactory();
QueryBuilder buildQuery = searchFactory.buildQueryBuilder().forEntity(MasterDiagnosis.class).get();
PhraseContext keyword = buildQuery.phrase();
keyword.withSlop(3);
//WildcardContext wildcard = keyword.wildcard();
String[] searchfields = MasterDiagnosis.getSearchfields();
PhraseMatchingContext onFields = keyword.onField(searchfields[0]);
for (int i = 1; i < searchfields.length; i++)
onFields.andField(searchfields[i]);
PhraseTermination matching = onFields.sentence(lowerCasedSearchTerm);
Query query = matching.createQuery();
// Convert the Search Query into something that provides results: Specify Compound again to be future proof
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.