2011-09-30 21 views
7

Trong Solr (3.3), có thể tạo một chữ cái theo từng chữ có thể tìm kiếm thông qua một số EdgeNGramFilterFactory và cũng nhạy cảm với truy vấn cụm từ không?Solr: truy vấn cụm từ chính xác với một EdgeNGramFilterFactory

By Ví dụ, tôi đang tìm một lĩnh vực đó, nếu có chứa "contrat về CNTT", sẽ được tìm thấy nếu sử dụng các loại:

  • contrat
  • Informatique
  • Contr
  • informa
  • "contrat informatique"
  • "thông tin contrat" ​​

Hiện nay, tôi làm một cái gì đó như thế này:

<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> 

... nhưng nó không thành công trên cụm từ truy vấn.

Khi tôi nhìn vào máy phân tích lược đồ trong Solr admin, tôi thấy rằng "contrat Informatique" tạo ra các thẻ sau:

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

Vì vậy, các truy vấn làm việc với "contrat trong" (thẻ liên tiếp), nhưng không "contrat inf" (bởi vì hai thẻ này được tách ra).

Tôi khá chắc chắn bất kỳ loại gốc có thể làm việc với các truy vấn cụm từ, nhưng tôi không thể tìm thấy bộ mã thông báo phù hợp của bộ lọc để sử dụng trước EdgeNGramFilterFactory.

Trả lời

2

Như than ôi tôi không thể quản lý để sử dụng một PositionFilter đúng như Jayendra Patil gợi ý (PositionFilter làm cho bất kỳ truy vấn một OR truy vấn boolean), tôi sử dụng một phương pháp khác nhau.

Vẫn còn với EdgeNGramFilter, tôi đã thêm thực tế là mỗi từ khóa người dùng nhập là bắt buộc và vô hiệu hóa tất cả các cụm từ.

Vì vậy, nếu người dùng yêu cầu "cont info", biến đổi thành +cont +info. Đó là một chút permissive rằng một cụm từ đúng sẽ được, nhưng nó quản lý để làm những gì tôi muốn (và không trả lại kết quả với chỉ một thuật ngữ từ hai).

Điều duy nhất chống lại giải pháp này là các thuật ngữ có thể được hoán vị trong kết quả (vì vậy tài liệu có "thông tin tương phản" cũng sẽ được tìm thấy), nhưng nó không phải là một việc lớn.

+0

Xin chào, Xavier. Bạn có thể vui lòng giải thích làm thế nào bạn chuyển đổi "cont thông tin" để + cont + thông tin là có bất kỳ ra khỏi hộp util class cho điều này? Hoặc là điều này chỉ xác định các trích dẫn kép và chuyển đổi bằng tay? Tôi đang cố giải quyết vấn đề này: http: // stackoverflow.com/questions/37033381/solr-search-field-best-practices – wattale

+0

Đó là một thao tác thủ công, tìm kiếm các trích dẫn kép và thêm dấu cộng. Tôi không tìm thấy bất cứ điều gì có thể tự động hóa điều này cho tôi: -/ –

+0

Cảm ơn bạn đã trả lời xavier, Đối với tôi cũng sau khi thu thập dữ liệu quá nhiều nội dung không thể tìm thấy một giải pháp hộp. Tôi nghĩ tôi đang sáng tạo lại bánh xe bằng cách làm điều này bằng tay. Nhưng tôi đoán làm điều đó bằng tay là lựa chọn duy nhất có sẵn: | – wattale

1

Đây là những gì tôi đã suy nghĩ -
Đối với ngrams là cụm từ phù hợp với vị trí của mã thông báo được tạo cho mỗi từ nên giống nhau.
Tôi đã kiểm tra bộ lọc gram cạnh và nó tăng các mã thông báo và không tìm thấy bất kỳ thông số nào để ngăn chặn nó.
Có một bộ lọc vị trí có sẵn và điều này duy trì vị trí mã thông báo với cùng một mã thông báo như khi bắt đầu.
Vì vậy, nếu cấu hình sau được sử dụng tất cả các thẻ ở cùng một vị trí và khớp với truy vấn cụm từ (cùng một vị trí mã thông báo được khớp với cụm từ)
Tôi đã kiểm tra nó thông qua công cụ anaylsis và truy vấn phù hợp.

Vì vậy, bạn có thể muốn thử gợi ý: -

<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

Các ý tưởng là gọn gàng, nhưng dường như không hoạt động: -/Ngay cả khi tôi có các trận đấu thông qua công cụ phân tích quản trị, một truy vấn thực không trả về gì cả (có lẽ vì trong công cụ phân tích, cách nó làm nổi bật mã thông báo không bận tâm với cụm từ). Ngoài ra, [PositionFilter] (http://tinyurl.com/solr-positionfilter) làm cho truy vấn _boolean_ như đã nói trên wiki, vì vậy "contrat informatique" hoặc thậm chí "+ contrat + informatique" trả về tài liệu với "contrat" ​​nhưng cũng không có "informatique" là toán tử mặc định là OR. Một sự thay thế có thể sẽ là biến đổi truy vấn trong + contrat + informatique, tôi nghĩ vậy. –

4

Tìm kiếm cụm từ chính xác không hoạt động do tham số truy vấn slop = 0 theo mặc định. Tìm kiếm cụm từ '"Hello World"', tìm kiếm cụm từ có vị trí tuần tự. Tôi muốn EdgeNGramFilter có thông số để kiểm soát vị trí đầu ra, điều này có vẻ như cũ question.

Bằng cách đặt tham số qs thành một số giá trị rất cao (nhiều hơn khoảng cách tối đa giữa ngram), bạn có thể nhận lại cụm từ. Điều này giải quyết một phần vấn đề cho phép các cụm từ, nhưng không chính xác, hoán vị sẽ được tìm thấy là tốt. Vì vậy mà tìm kiếm cho "contrat về CNTT" sẽ phù hợp với văn bản như "... Hợp đồng bị bỏ rơi. Informatique ..."

enter image description here

Để hỗ trợ chính xác cụm từ truy vấn tôi kết thúc sử dụng separate fields for ngrams.

bước cần thiết:

Xác định kiểu trường riêng biệt cho các giá trị thông thường chỉ mục và gram:

<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> 

Nói với Solr để copy fields khi lập chỉ mục:

Bạn có thể xác định ngrams riêng phản ánh cho mỗi lĩnh vực:

<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"/> 

Hoặc bạn có thể đặt tất cả ngrams vào một lĩnh vực:

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

Lưu ý rằng bạn sẽ không thể tách rời tên lửa đẩy trong trường hợp này.

Và điều cuối cùng là chỉ định trường ngram và tên lửa đẩy trong truy vấn. Một cách là định cấu hình ứng dụng của bạn. Một cách khác là để xác định "gắn thêm" params trong solrconfig.xml

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

Tôi đã thực hiện một sửa chữa để EdgeNGramFilter nên vị trí trong vòng một mã thông báo không tăng lên nữa:

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; 
    } 
} 
Các vấn đề liên quan