java - 使用 Hibernate Search 自动完成

标签 java mysql hibernate autocomplete hibernate-search

我正在尝试为我的网站构建更好的自动完成功能。我想为此使用 Hibernate Search,但据我试验,它只能为我找到完整的单词。

所以,我的问题是:是否可以只搜索某些字符?

例如。用户键入 3 个字母并使用 hibernate 搜索向他显示包含这 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,而无需处理文档和嵌入字段。

和以前一样: 希望这对下一个遇到这个问题的人有用。如果有人对上面发布的代码有任何批评或改进,请随时编辑并告诉我。


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

关于java - 使用 Hibernate Search 自动完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5361941/

相关文章:

Java 二进制文件未从指定的类路径获取资源

php - 使用存储在 mysql 数据库中的 if 语句

java - Hibernate:为列表中的同一对象引用生成不同(唯一)ID

java - 定时器不允许用户编辑

Java-从 LDAP 检索 shiro 的权限

php - MYSQL - 剩余数量 - 带序列号的产品 - 多表

mysql join 也获取未连接的值

java - Hibernate 在保存时创建新行而不是更新

java - hibernate 中打开的 session 是否一定意味着我已连接到数据库?

java - Volley 在模拟器上运行但在设备上失败