簡體   English   中英

Hibernate 具有自動完成和模糊功能的搜索

[英]Hibernate Search with Autocomplete and Fuzzy-Functionality

我正在嘗試創建StingUtils containsIgnoreCase()方法的 Hibernate 搜索表示以及模糊搜索匹配

假設用戶寫了字母“p”,他們將獲得所有包含字母“p”的匹配項(無論該字母位於相應匹配項的開頭、中間還是結尾)。

當它們形成諸如“Peter”之類的詞時,它們也應該接收到模糊匹配,例如“Petar”、“Petaer”和“Peder”。

我正在使用此處出色答案中提供的自定義查詢和索引分析器,因為我需要minGramSize為 1 以允許自動完成功能,同時我還希望多字用戶輸入由空格分隔,例如“EUR彼得的帳戶”,可以在不同的情況下(下或上)。

因此,用戶應該能夠鍵入“AND”並接收上述示例作為匹配項。

目前,我正在使用以下查詢:

  org.apache.lucene.search.Query fuzzySearchByName = qb.keyword().fuzzy()
                                                   .withEditDistanceUpTo(1).onField("name")
                                                   .matching(userInput).createQuery();
  booleanQuery.add(fuzzySearchByName, BooleanClause.Occur.MUST);

但是,完全匹配的案例不會出現在搜索結果中:

如果我們輸入“petar”,我們會得到以下結果:

  1. Petarr (非精確匹配)
  2. Petaer (非精確匹配)

... 4. PETAR完全匹配

同樣適用於“peter”的用戶輸入,其中第一個結果是“Petero”,第二個是“Peter”(第二個應該是第一個)。

我還需要在多詞查詢中只包含完全匹配 - 例如,如果我開始編寫“ Account for... ”,我希望所有匹配的結果都包含短語“ Account for ”,並最終包含基於模糊相關的術語那個短語(基本上與前面顯示的 containsIgnoreCase() 方法相同,只是試圖添加模糊支持)

然而,我猜這與minGramSize的 1 和WhitespaceTokenizerFactory相矛盾?

但是,完全匹配的案例不會出現在搜索結果中:

只需使用兩個查詢而不是一個:

編輯:您還需要為自動完成和“精確”匹配設置兩個單獨的字段; 在底部查看我的編輯。

  org.apache.lucene.search.Query exactSearchByName = qb.keyword().onField("name")
                                                   .matching(userInput).createQuery();
  org.apache.lucene.search.Query fuzzySearchByName = qb.keyword().fuzzy()
                                                   .withEditDistanceUpTo(1).onField("name")
                                                   .matching(userInput).createQuery();
  org.apache.lucene.search.Query searchByName = qb.boolean().should(exactSearchByName).should(fuzzySearchByName).createQuery();
  booleanQuery.add(searchByName, BooleanClause.Occur.MUST);

這將完全近似地匹配包含用戶輸入的文檔,因此這將匹配與您的示例相同的文檔。 但是,包含用戶輸入的文檔將完全匹配兩個查詢,而僅包含類似內容的文檔將僅匹配模糊查詢。 結果,完全匹配將具有更高的分數並最終在結果列表中更高。

如果完全匹配不夠高,請嘗試向exactSearchByName查詢添加提升:

  org.apache.lucene.search.Query exactSearchByName = qb.keyword().onField("name")
                                                   .matching(userInput)
                                                   .boostedTo(4.0f)
                                                   .createQuery();

然而,我猜這與 1 的 minGramSize 和 WhitespaceTokenizerFactory 相矛盾?

如果您想匹配包含出現在用戶輸入中的任何單詞(但不一定是所有單詞)的文檔,並將包含更多單詞的文檔放在結果列表中的較高位置,請執行我上面解釋的操作。

如果要匹配包含完全相同順序的所有單詞的文檔,請使用KeywordTokenizerFactory (即不進行標記化)。

如果您想以任何順序匹配包含所有單詞的文檔,那么......這不太明顯。 Hibernate 搜索(尚未)中不支持該功能,因此您基本上必須自己構建查詢。 我已經看到的一個 hack 是這樣的:

Analyzer analyzer = fullTextSession.getSearchFactory().getAnalyzer( "myAnalyzer" );

QueryParser queryParser = new QueryParser( "name", analyzer );
queryParser.setOperator( Operator.AND ); // Match *all* terms
Query luceneQuery = queryParser.parse( userInput );

...但這不會產生模糊查詢。 如果你想要模糊查詢,你可以嘗試覆蓋 QueryParser 的自定義子類中的一些方法。 我沒有嘗試過,但它可能會起作用:

public final class FuzzyQueryParser extends QueryParser {
    private final int maxEditDistance;
    private final int prefixLength;

    public FuzzyQueryBuilder(String fieldName, Analyzer analyzer, int maxEditDistance, int prefixLength) {
        super( fieldName, analyzer );
        this.maxEditDistance = maxEditDistance;
        this.prefixLength = prefixLength;
    }

    @Override
    protected Query newTermQuery(Term term) {
        return new FuzzyQuery( term, maxEditDistance, prefixLength );
    }
}

編輯: minGramSize 為 1 時,您將獲得很多非常頻繁的術語:從單詞開頭提取的單個或兩個字符的術語。 這可能會導致許多不需要的匹配項得分很高(因為這些術語很頻繁)並且可能會淹沒完全匹配項。

首先,您可以嘗試將相似度(〜評分公式)設置為org.apache.lucene.search.similarities.BM25Similarity ,它更適合忽略非常頻繁的術語。 有關設置,請參見此處 這應該會提高使用相同分析儀的評分。

其次,您可以嘗試設置兩個字段而不是一個:一個用於模糊自動完成,另一個用於非模糊、完整匹配。 這可能會提高精確匹配的分數,因為用於精確匹配的字段索引的無意義術語將更少。 只需這樣做:

@Field(name = "name", analyzer = @Analyzer(definition = "text")
@Field(name = "name_autocomplete", analyzer = @Analyzer(definition = "edgeNgram")
private String name;

分析器“文本”只是您鏈接的答案中的分析器“edgeNGram_query”; 只是重命名它。

繼續編寫兩個查詢而不是如上所述的一個,但請確保針對兩個不同的字段:

  org.apache.lucene.search.Query exactSearchByName = qb.keyword().onField("name")
                                                   .matching(userInput).createQuery();
  org.apache.lucene.search.Query fuzzySearchByName = qb.keyword().fuzzy()
                                                   .withEditDistanceUpTo(1).onField("name_autocomplete")
                                                   .matching(userInput).createQuery();
  org.apache.lucene.search.Query searchByName = qb.boolean().should(exactSearchByName).should(fuzzySearchByName).createQuery();
  booleanQuery.add(searchByName, BooleanClause.Occur.MUST);

當然,不要忘記在這些更改之后重新索引。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM