Hibernate搜索前缀

时间:2016-03-21 13:19:45

标签: java hibernate jpa hibernate-search

目前,我已经成功配置了一个基本的Hibernate Search索引,以便能够在我的JPA实体的各个字段中搜索完整的单词:

@Entity
@Indexed
class Talk {
    @Field String title
    @Field String summary
}

我的查询看起来像这样:

List<Talk> search(String text) {
    FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager)
    QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Talk).get()
    Query query = queryBuilder
            .keyword()
            .onFields("title", "summary")
            .matching(text)
            .createQuery()
    FullTextQuery jpaQuery = fullTextEntityManager.createFullTextQuery(query, Talk)
    return jpaQuery.getResultList()
}

现在我想微调这个设置,以便在我搜索&#34; test&#34;它仍然会找到标题或摘要包含&#34; test&#34;甚至作为另一个单词的前缀。所以题为'#34;单元测试&#34;或其摘要包含&#34;睾丸&#34;应该仍然出现在搜索结果中,而不仅仅是那些标题或摘要包含&#34; test&#34;作为一个完整的词。

我试图查看文档,但我无法弄清楚我是否应该改变我的实体被编入索引的方式,或者它是否与查询有关。请注意,我想做类似以下的事情,但是在几个字段上搜索很难:

 Query query = queryBuilder
            .keyword().wildcard()
            .onField("title")
            .matching(text + "*")
            .createQuery()

编辑: 根据哈代的回答,我按照这样的方式配置了我的实体:

@Indexed
@Entity
@AnalyzerDefs([
@AnalyzerDef(name = "ngram",
        tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
        filters = [
            @TokenFilterDef(factory = LowerCaseFilterFactory.class),
            @TokenFilterDef(factory = NGramFilterFactory.class,
                    params = [
                        @Parameter(name = "minGramSize",value = "3"),
                        @Parameter(name = "maxGramSize",value = "3")
                    ])
        ])
])
class Talk {
    @Field(analyzer=@Analyzer(definition="ngram")) String title
    @Field(analyzer=@Analyzer(definition="ngram")) String summary
}

感谢这种配置,当我搜索“艺术”时,我会谈到标题或摘要中包含“艺术”的词语。是(艺术家,手工艺品等)的一个子词。不幸的是,在那些之后我也会得到会谈,其中标题或摘要包含包含我的搜索词(艺术,放屁等)的子词的单词。可能有一些微调可以消除这些,但至少我现在得到的结果很快,而且它们处于合理的顺序。

3 个答案:

答案 0 :(得分:3)

你可以在这里做很多事情。在索引时间内通过适当的分析可以做很多事情。

例如,您想要应用适合您语言的词干分析器。对于英语,这通常是Snowball词干分析器。这个想法是,在索引编制过程中,所有单词都缩减为词干,例如测试测试到_test。这会让你有所作为。

您可以研究的另一件事是ngramm索引。根据您的描述,您也希望在无关词中找到匹配。这里的想法是索引每个单词的“子词”,以便以后可以找到它们。

关于分析器,您需要查看Hibernate Search文档的named analyzers部分。这里的关键是@AnalyzerDef注释。

在查询方面,您还可以应用一些“技巧”。实际上,您可以使用通配符查询,但是,如果您使用的是Hibernate Search查询DSL,则不能使用keyword查询,但需要使用wildcard查询。再次,查看Hibernate Search文档。

答案 1 :(得分:1)

您应该使用NgramEdgeNGram过滤索引,因为您在答案中已正确记录。但是您应该按照lucene文档中的建议使用不同的分析器进行查询(请参阅search_analyzer): https://www.elastic.co/guide/en/elasticsearch/guide/current/_index_time_search_as_you_type.html

这样,您的搜索查询就不会被标记为ngrams,而您的结果将更像SQL中的%text%text%

不幸的是,由于未知原因,Hibernate Search目前不支持search_analyzer字段规范。您只能使用特定的分析器进行索引,这也可用于搜索查询分析。

我计划自己实现此功能。

修改

您可以指定搜索时间分析器(search_analyzer),如下所示:

List<Talk> search(String text) {
    FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager)
    EntityContext entityContext = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Talk);

    entityContext.overridesForField("myField", "myNamedAnalyzerDef");

    QueryBuilder queryBuilder = ec.get()
    Query query = queryBuilder
            .keyword()
            .onFields("title", "summary")
            .matching(text)
            .createQuery()
    FullTextQuery jpaQuery = fullTextEntityManager.createFullTextQuery(query, Talk)
    return jpaQuery.getResultList()
}

我已经使用这种技术来有效地模拟Lucene search_analyzer属性。

答案 2 :(得分:0)

在Lucene 4.9版中,我使用了EnglishAnalyzer。我认为这是SnowballAnalyzer的英文版实现,但不是100%肯定。我用它来创建和搜索索引。使用它没有什么特别需要。

Analyzer analyzer = new EnglishAnalyzer(Version.LUCENE_4_9);
IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_4_9, analyzer);

analyzer = new EnglishAnalyzer(Version.LUCENE_4_9);
parser = new StandardQueryParser(analyzer);

您可以在Guided Code Search看到它的实际效果。这完全在Lucene附近运行。

Lucene可以集成到Hibernate搜索中,但我还没有尝试过这样做。我似乎很强大,但我不知道:见Apache Lucene™ Integration

我还读过lucene可以修补到SQL引擎中,但我也没有尝试过。示例:Indexing Databases with Lucene