7

Solr (3.3) では、フィールドを文字単位で検索可能にし、EdgeNGramFilterFactoryフレーズ クエリにも対応させることはできますか?

たとえば、「contrat informatique」が含まれている場合、ユーザーが次のように入力すると見つかるフィールドを探しています。

  • 契約
  • 情報
  • 制御
  • 情報
  • 「契約情報」
  • 「契約情報」

現在、私は次のようなものを作りました:

<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 admin のスキーマ アナライザーを見ると、「contrat informatique」によって次のトークンが生成されていることがわかります。

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

そのため、クエリは "contrat in" (連続したトークン) では機能しますが、"contrat inf" では機能しません (この 2 つのトークンが分離されているため)。

あらゆる種類のステミングがフレーズ クエリで機能すると確信していますが、EdgeNGramFilterFactory.

4

4 に答える 4

6

デフォルトではクエリ slop パラメータ = 0 のため、正確なフレーズ検索は機能しません。「Hello World」という語句を検索すると、連続した位置を持つ用語が検索されます。EdgeNGramFilter に出力の配置を制御するパラメーターがあればいいのにと思いますが、これは古い質問のようです。

qs パラメータを非常に高い値 (ngram 間の最大距離よりも大きい値) に設定すると、フレーズを取り戻すことができます。これにより、フレーズを許可する問題が部分的に解決されますが、正確ではなく、順列も検出されます。そのため、"contrat informatique" の検索は、"...contract alone. Informatique..." のようなテキストに一致します。

ここに画像の説明を入力

正確なフレーズ クエリをサポートするために、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 に指示します。

フィールドごとに個別の ngrams リフレクションを定義できます。

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

または、すべての ngram を 1 つのフィールドに入れることができます。

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

この場合、ブースターを分離することはできません。

最後に、クエリで ngrams フィールドとブースターを指定します。1 つの方法は、アプリケーションを構成することです。もう 1 つの方法は、solrconfig.xml で「追加」パラメーターを指定することです。

   <lst name="appends">
     <str name="qf">heap_ngrams</str>
   </lst>
于 2012-02-08T20:09:59.280 に答える
2

残念ながら、Jayendra Patil が提案したような権利を使用することができなかったのでPositionFilter(PositionFilter はクエリを OR ブールクエリにします)、別のアプローチを使用しました。

とはEdgeNGramFilterいえ、ユーザーが入力した各キーワードは必須であるという事実を追加し、すべてのフレーズを無効にしました。

したがって、ユーザーが を要求すると"cont info"、 に変換され+cont +infoます。真のフレーズはもう少し寛容ですが、私が望むことはできました (そして、2 つの用語のうちの 1 つの用語だけで結果を返すことはありません)。

この回避策に対する唯一の短所は、結果で用語が並べ替えられる可能性があることです (そのため、「情報コントラット」を含むドキュメントも検出されます) が、それほど大したことではありません。

于 2011-10-07T08:29:50.567 に答える
1

EdgeNGramFilterを修正して、トークン内の位置がもうインクリメントされないようにしました。

    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;
    }
}
于 2013-02-11T16:42:29.370 に答える
1

これが私が考えてい
たことです-ngramがフレーズ一致するためには、各単語に対して生成されたトークンの位置が同じでなければなりません。
エッジグラムフィルターをチェックしたところ、トークンがインクリメントされましたが、それを防ぐパラメーターが見つかりませんでした。
使用可能な位置フィルターがあり、これによりトークンの位置が最初と同じトークンに維持されます。
したがって、次の構成を使用すると、すべてのトークンが同じ位置にあり、フレーズ クエリと一致します (同じトークン位置がフレーズとして一致します)
。分析ツールで確認したところ、クエリが一致しました。

だからあなたはヒントを試してみたいかもしれません:-

<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>
于 2011-09-30T18:33:16.940 に答える