2011-09-30 137 views
7

在Solr(3.3)中,是否可以通過EdgeNGramFilterFactory可以搜索字段,並且對短語查詢也很敏感?Solr:使用EdgeNGramFilterFactory進行精確短語查詢

例如,我在尋找,如果包含 「contrat INFORMATIQUE」,會發現一個領域,如果用戶類型:

  • contrat
  • INFORMATIQUE
  • 對照
  • Informa公司
  • 「contrat informatique」
  • 「contrat info」

目前,我做了這樣的事情:

<fieldtype name="terms" class="solr.TextField"> 
    <analyzer type="index"> 
     <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/> 
     <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1"/> 
     <tokenizer class="solr.LowerCaseTokenizerFactory"/> 
     <filter class="solr.EdgeNGramFilterFactory" minGramSize="2" maxGramSize="15" side="front"/> 
    </analyzer> 
    <analyzer type="query"> 
     <charFilter class="solr.MappingCharFilterFactory" mapping="mapping-ISOLatin1Accent.txt"/> 
     <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1"/> 
     <tokenizer class="solr.LowerCaseTokenizerFactory"/> 
    </analyzer> 
</fieldtype> 

...但它未能對短語查詢。

當我看到在Solr管理模式分析,我發現「contrat INFORMATIQUE」產生的以下標記:

[...] contr contra contrat in inf info infor inform [...] 

所以查詢作品有「contrat在」(連續標記),但不「contrat inf」(因爲這兩個令牌是分開的)。

我很確定任何種類的詞幹都可以使用短語查詢,但是我找不到在EdgeNGramFilterFactory之前使用的正確標記詞的過濾器。

回答

2

由於唉我無法管理使用PositionFilter右像Jayendra帕蒂爾建議(PositionFilter作出任何查詢或布爾查詢),我用了一個不同的方法。

仍然與EdgeNGramFilter,我補充說,用戶輸入的每個關鍵字是強制性的,並禁用所有短語的事實。

因此,如果用戶要求"cont info",它會轉換爲+cont +info。真正的短語會更寬容一些,但它設法做我想做的事(並且不會返回只有兩個詞的結果)。

對此解決方法的唯一對策是術語可以在結果中進行置換(因此也會找到具有「informatique contrat」的文檔),但這並不是什麼大不了的事情。

+0

嗨,澤維爾。你能解釋一下,你是如何將「cont info」轉換爲+ cont + info的嗎?或者這只是識別雙引號和手動轉換? 我想解決這個問題:http:// stackoverflow。com/questions/37033381/solr-search-field-best-practices – wattale

+0

這是一個手動操作,查找雙引號並添加加號。我沒有發現任何可以自動執行此操作的內容: -/ –

+0

感謝xavier的回覆,對於我來說,抓取這麼多內容後也找不到開箱即用的解決方案。我認爲我正在通過手動方式重新發明輪子。但我想手動做它是唯一可用的選項:| – wattale

1

這是我在想什麼 -
爲了讓ngrams成爲詞組匹配,爲每個詞生成的標記的位置應該是相同的。
我檢查了邊克濾波器,它增加了令牌,並沒有找到任何參數來防止它。
有一個位置過濾器可用,這將維持令牌位置與開始時相同的標記。
因此,如果使用以下配置,所有令牌都處於相同的位置,並且它與短語查詢相匹配(相同的標記位置與短語相匹配)
我通過anaylsis工具檢查了它,並且查詢匹配。

所以,你可能想嘗試的提示: -

<analyzer type="index"> 
    <tokenizer class="solr.WhitespaceTokenizerFactory" /> 
    <charFilter class="solr.MappingCharFilterFactory" 
      mapping="mapping-ISOLatin1Accent.txt" /> 
    <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" 
      generateNumberParts="1" catenateWords="1" catenateNumbers="1" 
      catenateAll="0" splitOnCaseChange="1"/> 
    <filter class="solr.LowerCaseFilterFactory" /> 
    <filter class="solr.EdgeNGramFilterFactory" minGramSize="2" 
      maxGramSize="15" side="front"/> 
    <filter class="solr.PositionFilterFactory" /> 
</analyzer> 
+0

的想法很整潔,但似乎並不奏效: - 即使我通過管理分析工具獲得了匹配結果,真正的查詢也不會返回任何結果(可能是因爲在分析工具中,突出顯示令牌的方式不會影響詞​​組)。另外,[PositionFilter](http://tinyurl.com/solr-positionfilter)使得查詢_boolean_在維基上被說明,因此「contrat informatique」或甚至「+ contrat + informatique」將返回具有「contrat」的文檔,但也沒有作爲默認運營商的「informatique」是OR。我認爲,一種可能的選擇是轉換+ contrat + informatique中的查詢。 –

4

默認情況下,由於查詢slop參數= 0,所以無法正確搜索詞組。 搜索詞組''Hello World''它搜索連續位置的術語。 我希望EdgeNGramFilter有一個參數來控制輸出定位,這看起來像一箇舊的question

通過將qs參數設置爲某個非常高的值(超過ngrams之間的最大距離),您可以返回短語。這部分解決了允許短語但不是確切的排列的問題。 讓搜索「contrat INFORMATIQUE」將匹配像文本「...合同放棄了。INFORMATIQUE ......」

enter image description here

爲了支持確切短語查詢我最終使用separate fields for ngrams。需要

步驟:

定義單獨字段類型索引定期值和克:

<fieldType name="text" class="solr.TextField" omitNorms="false"> 
    <analyzer> 
    <tokenizer class="solr.StandardTokenizerFactory"/> 
    <filter class="solr.LowerCaseFilterFactory"/> 
    </analyzer> 
</fieldType> 

<fieldType name="ngrams" class="solr.TextField" omitNorms="false"> 
    <analyzer type="index"> 
    <tokenizer class="solr.StandardTokenizerFactory"/> 
    <filter class="solr.LowerCaseFilterFactory"/> 
    <filter class="solr.EdgeNGramFilterFactory" minGramSize="2" maxGramSize="15" side="front"/> 
    </analyzer> 
    <analyzer type="query"> 
    <tokenizer class="solr.StandardTokenizerFactory"/> 
    <filter class="solr.LowerCaseFilterFactory"/> 
    </analyzer> 
</fieldType> 

給solr的至copy fields當索引:

可以爲每個定義單獨的n元語法反射字段:

<field name="contact_ngrams" type="ngrams" indexed="true" stored="false"/> 
<field name="product_ngrams" type="ngrams" indexed="true" stored="false"/> 
<copyField source="contact_text" dest="contact_ngrams"/> 
<copyField source="product_text" dest="product_ngrams"/> 

或者你可以把所有的n-gram到一個領域:

<field name="heap_ngrams" type="ngrams" indexed="true" stored="false"/> 
<copyField source="*_text" dest="heap_ngrams"/> 

請注意,您不能在這種情況下分開的助推器。

最後一件事是在查詢中指定ngrams字段和助推器。 一種方法是配置您的應用程序。 另一種方法是指定「附加」在solrconfig.xml中

<lst name="appends"> 
    <str name="qf">heap_ngrams</str> 
    </lst> 
1

我做了一個修復程序EdgeNGramFilter所以令牌中的位置不再增加PARAMS:

public class CustomEdgeNGramTokenFilterFactory extends TokenFilterFactory { 
    private int maxGramSize = 0; 

    private int minGramSize = 0; 

    @Override 
    public void init(Map<String, String> args) { 
     super.init(args); 
     String maxArg = args.get("maxGramSize"); 
     maxGramSize = (maxArg != null ? Integer.parseInt(maxArg) 
       : EdgeNGramTokenFilter.DEFAULT_MAX_GRAM_SIZE); 

     String minArg = args.get("minGramSize"); 
     minGramSize = (minArg != null ? Integer.parseInt(minArg) 
       : EdgeNGramTokenFilter.DEFAULT_MIN_GRAM_SIZE); 

    } 

    @Override 
    public CustomEdgeNGramTokenFilter create(TokenStream input) { 
     return new CustomEdgeNGramTokenFilter(input, minGramSize, maxGramSize); 
    } 
} 
public class CustomEdgeNGramTokenFilter extends TokenFilter { 
    private final int minGram; 
    private final int maxGram; 
    private char[] curTermBuffer; 
    private int curTermLength; 
    private int curGramSize; 

    private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); 
    private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); 
    private final PositionIncrementAttribute positionIncrementAttribute = addAttribute(PositionIncrementAttribute.class); 

    /** 
    * Creates EdgeNGramTokenFilter that can generate n-grams in the sizes of the given range 
    * 
    * @param input {@link org.apache.lucene.analysis.TokenStream} holding the input to be tokenized 
    * @param minGram the smallest n-gram to generate 
    * @param maxGram the largest n-gram to generate 
    */ 
    public CustomEdgeNGramTokenFilter(TokenStream input, int minGram, int maxGram) { 
     super(input); 

     if (minGram < 1) { 
      throw new IllegalArgumentException("minGram must be greater than zero"); 
     } 

     if (minGram > maxGram) { 
      throw new IllegalArgumentException("minGram must not be greater than maxGram"); 
     } 

     this.minGram = minGram; 
     this.maxGram = maxGram; 
    } 

@Override 
public final boolean incrementToken() throws IOException { 
    while (true) { 
     int positionIncrement = 0; 
     if (curTermBuffer == null) { 
      if (!input.incrementToken()) { 
       return false; 
      } else { 
       positionIncrement = positionIncrementAttribute.getPositionIncrement(); 
       curTermBuffer = termAtt.buffer().clone(); 
       curTermLength = termAtt.length(); 
       curGramSize = minGram; 
      } 
     } 
     if (curGramSize <= maxGram) { 
      if (!(curGramSize > curTermLength   // if the remaining input is too short, we can't generate any n-grams 
        || curGramSize > maxGram)) {  // if we have hit the end of our n-gram size range, quit 
       // grab gramSize chars from front 
       int start = 0; 
       int end = start + curGramSize; 
       offsetAtt.setOffset(start, end); 
       positionIncrementAttribute.setPositionIncrement(positionIncrement); 
       termAtt.copyBuffer(curTermBuffer, start, curGramSize); 
       curGramSize++; 

       return true; 
      } 
     } 
     curTermBuffer = null; 
    } 
} 

    @Override 
    public void reset() throws IOException { 
     super.reset(); 
     curTermBuffer = null; 
    } 
}