【问题标题】:Hibernate Search Lucene. Suggestion but almost like SQL "LIKE"休眠搜索 Lucene。建议但几乎像 SQL "LIKE"
【发布时间】:2019-03-07 05:18:44
【问题描述】:

这是我第一次处理优化的搜索功能,我的部分熟练程度是在 android 开发的前端,但我愿意冒险尝试 hibernate-search。我确实了解 SQL“LIKE”查询的功能,它的作用及其限制,这就是我直接跳到休眠搜索(lucene)的原因,我的目标是根据输入(输入查询)提供自动建议.这就是我目前所得到的

@Indexed
@Table (name = "shop_table")
@Entity
@AnalyzerDef(name = "myanalyzer",
    tokenizer = @TokenizerDef(factory = KeywordTokenizerFactory.class), //
    filters = { //
            @TokenFilterDef(factory = LowerCaseFilterFactory.class),
            @TokenFilterDef(factory = WordDelimiterFilterFactory.class),
            @TokenFilterDef(factory = EdgeNGramFilterFactory.class, params = 
{ @Parameter(name = "maxGramSize", value = "1024") }),})
@Analyzer(definition = "myanalyzer")
public class Shop implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
enter code here

@Field(index = Index.YES, store = Store.YES, analyze = Analyze.YES)
@Column(name = "name")
private String name;

... other methods

我的查询

 Query lucenQuery = qb.keyword().onField("name").matching(searchTerm).createQuery();

它只是一个基本查询,我只专注于分析器配置以获得我想要的东西,它真的很困惑我应该关注哪个部分来实现我想要的东西,令牌化?过滤?还是查询本身? 无论如何,我已经将这 2 个短语编入索引。

"Apache Lychee Department" 
"Apache Strawberry Club Large"

当我处理/查询“Straw”时,它会给我 Apache Strawberry Club Large 但是当我处理/查询“Lychee”或“Apache Lychee”时,查询给了我两个?我只期待Apache荔枝系

我理解我所有配置的方式是/是

EdgeNGramFilterFactory (1024) 会给我一系列 1,024 的 EdgeNGrams 索引

LowerCaseFilterFactory 会给我所有小写的索引

WordDelimiterFilterFactory通过将查询作为一个单词对其进行过滤,并给我匹配的数据。

每个条目/数据都将被 KeywordTokenizerFactory 标记为关键字,并将被 EdgeNGram 索引为 1,024

我尝试查询一个短语,但仍然得到相同的输出

  Query luceneQuery = qb.phrase().onField("name").sentence(searchTerm).createQuery();

我的目标是有一个自动建议.. 或者至少从模仿 sql 的“LIKE”开始..

【问题讨论】:

    标签: hibernate lucene hibernate-search lexical-analysis


    【解决方案1】:

    您应该考虑两件事:

    • 默认情况下,当查询中有多个词条时,结果将包括与任何个词条匹配的文档,而不是所有个词条。
    • 默认情况下,您的查询将使用您在编制索引时使用的分析器进行分析。

    这尤其意味着您的查询“Lychee”将被分析为“L Lyc Lych Lyche Lychee”之类的内容(由于边缘 ngram 过滤器)。字符串“Apache Strawberry Club Large”之前被分析过,并且由于边缘 ngram 过滤器,术语“Large”被扩展为“L La Larl Large”。所以查询“Lychee”将匹配“Apache Strawberry Club Large”,因为它们都包含一个以 L 开头的单词...

    这显然是不受欢迎的行为。

    第一步是改变分析查询的方式,这样您就不会匹配完全不相关的文档。 基本上,您将需要定义另一个几乎相同的分析器,但没有“edge ngram”过滤器。然后您需要告诉 Hibernate Search 使用该分析器来分析您的查询。

    详情请见this answer

    作为第二步,如果所有包含的术语都存在于文档中,您需要使查询匹配。为此,最简单的解决方案是使用simple query string query 而不是关键字查询。

    替换这个:

    Query lucenQuery = qb.keyword().onField("name").matching(searchTerm).createQuery();
    

    有了这个:

    Query lucenQuery = qb.simpleQueryString().onField("name").withAndAsDefaultOperator().matching(searchTerm).createQuery();
    

    关键是调用.withAndAsDefaultOperator()

    此更改将产生其他一些影响,例如在输入字符串中启用特殊语法,因此我鼓励您阅读参考文档以了解 simpleQueryString 的确切含义。

    【讨论】:

    • 对不起,回复晚了,非常感谢您提供的信息丰富的帮助,我至少在正确的方向上。谢谢
    • 而且,我没有意识到 NGram 会为每个单词创建索引,虽然它只会为每个条目创建索引.. 没想到对于每个空格,它都会创建一个索引..跨度>
    • 你好,我成功了,我检查了你给我的链接,我不知道除了创建第一个分析器之外你可以创建一个单独的分析器,但是你介意检查我做的代码,如果我的方法正确吗?
    【解决方案2】:

    感谢@yrodiere,我做到了这一点

    @Indexed
    @Table (name = "shop_table")
    @Entity
    @AnalyzerDef(name = "edgeNgram",
        tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
        filters = {
                @TokenFilterDef(factory = LowerCaseFilterFactory.class),
                @TokenFilterDef(factory = EdgeNGramFilterFactory.class, params = 
                                          { @Parameter(name = "maxGramSize", value = "1024") }),
        })
    @AnalyzerDef(name = "search_query_analyzer",
        tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
        filters = {
                @TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
                @TokenFilterDef(factory = LowerCaseFilterFactory.class)
        })
    public class Shop implements Serializable {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    
    @Field(store = Store.YES, analyze = Analyze.YES)
    @Column(name = "name")
    @Analyzer(definition = "edgeNgram")
    private String name;
    
    public void setName(String name) {
        this.name = name;
    }
    
    public String getName() {
        return this.name;
    }
    }
    

    还有我的查询

      QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Shop.class)
                .overridesForField("name", "search_query_analyzer").get();
    
        Query lucenQuery = qb.simpleQueryString().onField("name").withAndAsDefaultOperator().matching(shopSearchTerm).createQuery();
    

    但我不确定我是否以正确的方式实施它..

    【讨论】:

    • 您应该对两个分析器都使用ASCIIFoldingFilterFactory,或者一个都不用。但除此之外,它看起来还不错。
    • 谢谢@yrodiere,会按照你的建议去做,我会更多地了解其他工厂的工作。谢谢你的大力帮助。这完成了我创建的移动应用程序所需的一切,只是有点麻烦我必须这样做,后端也是如此。非常感谢!
    猜你喜欢
    • 2021-07-28
    • 2012-08-17
    • 1970-01-01
    • 2014-08-31
    • 1970-01-01
    • 2015-02-01
    • 1970-01-01
    • 2019-02-26
    • 1970-01-01
    相关资源
    最近更新 更多