11

インデックス時またはクエリ時にフィールドをブーストする方法を理解しています。しかし、タイトルの先頭に近い用語に一致するスコアを上げるにはどうすればよいですか?

例:

Query = "lucene"

Doc1 title = "Lucene: Homepage"
Doc2 title = "I have a question about lucene?"

「lucene」が最初に近いので、最初のドキュメントのスコアを高くしたいと思います(今のところ用語freqを無視します)。

SpanQueryを使用して用語間の近接度を指定する方法はわかりますが、フィールド内の位置に関する情報を使用する方法がわかりません。

私はJavaでLucene4.1を使用しています。

4

2 に答える 2

12

SpanFirstQueryフィールドの先頭近くの用語に一致する、を使用します。すべてのスパンクエリは位置に依存しているため、luceneでインデックスを作成するときにデフォルトで有効になっています。

個別にテストしてみましょう。SpanTermQuery用語を見つけることができる最大の位置を指定する必要があります(私の例では1つ)。

SpanTermQuery spanTermQuery = new SpanTermQuery(new Term("title", "lucene"));
SpanFirstQuery spanFirstQuery = new SpanFirstQuery(spanTermQuery, 1);

2つのドキュメントを指定すると、。を使用して分析した場合、このクエリは「Lucene:Homepage」というタイトルの最初のドキュメントのみを検索しますStandardAnalyzer

これで、上記を通常のテキストクエリと組み合わせSpanFirstQueryて、最初のクエリだけをスコアに影響を与えることができます。BooleanQuery次のように、を使用してスパンクエリをshould句として配置すると、簡単に実行できます。

Term term = new Term("title", "lucene");
TermQuery termQuery = new TermQuery(term);
SpanFirstQuery spanFirstQuery = new SpanFirstQuery(new SpanTermQuery(term), 1);
BooleanQuery booleanQuery = new BooleanQuery();
booleanQuery.add(new BooleanClause(termQuery, BooleanClause.Occur.MUST));
booleanQuery.add(new BooleanClause(spanFirstQuery, BooleanClause.Occur.SHOULD));

同じことを達成するためのさまざまな方法がおそらくあります。おそらくCustomScoreQuery、スコアリングを実装するために、またはカスタムコードを使用することもありますが、これは私にとって最も簡単な方法のようです。

TermQuery私がそれをテストするために使用したコードは、最初に唯一、次に唯一SpanFirstQuery、そして最後に結合されたものを実行する次の出力(スコアを含む)を出力しますBooleanQuery

------ TermQuery --------
Total hits: 2
title: I have a question about lucene - score: 0.26010898
title: Lucene: I have a really hard question about it - score: 0.22295055
------ SpanFirstQuery --------
Total hits: 1
title: Lucene: I have a really hard question about it - score: 0.15764984
------ BooleanQuery: TermQuery (MUST) + SpanFirstQuery (SHOULD) --------
Total hits: 2
title: Lucene: I have a really hard question about it - score: 0.26912516
title: I have a question about lucene - score: 0.09196242

完全なコードは次のとおりです。

public static void main(String[] args) throws Exception {

        Directory directory = FSDirectory.open(new File("data"));

        index(directory);

        IndexReader indexReader = DirectoryReader.open(directory);
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);

        Term term = new Term("title", "lucene");

        System.out.println("------ TermQuery --------");
        TermQuery termQuery = new TermQuery(term);
        search(indexSearcher, termQuery);

        System.out.println("------ SpanFirstQuery --------");
        SpanFirstQuery spanFirstQuery = new SpanFirstQuery(new SpanTermQuery(term), 1);
        search(indexSearcher, spanFirstQuery);

        System.out.println("------ BooleanQuery: TermQuery (MUST) + SpanFirstQuery (SHOULD) --------");
        BooleanQuery booleanQuery = new BooleanQuery();
        booleanQuery.add(new BooleanClause(termQuery, BooleanClause.Occur.MUST));
        booleanQuery.add(new BooleanClause(spanFirstQuery, BooleanClause.Occur.SHOULD));
        search(indexSearcher, booleanQuery);
    }

    private static void index(Directory directory) throws Exception {
        IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_41, new StandardAnalyzer(Version.LUCENE_41));

        IndexWriter writer = new IndexWriter(directory, config);

        FieldType titleFieldType = new FieldType();
        titleFieldType.setIndexOptions(FieldInfo.IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
        titleFieldType.setIndexed(true);
        titleFieldType.setStored(true);

        Document document = new Document();
        document.add(new Field("title","I have a question about lucene", titleFieldType));
        writer.addDocument(document);

        document = new Document();
        document.add(new Field("title","Lucene: I have a really hard question about it", titleFieldType));
        writer.addDocument(document);

        writer.close();
    }

    private static void search(IndexSearcher indexSearcher, Query query) throws Exception {
        TopDocs topDocs = indexSearcher.search(query, 10);

        System.out.println("Total hits: " + topDocs.totalHits);

        for (ScoreDoc hit : topDocs.scoreDocs) {
            Document result = indexSearcher.doc(hit.doc);
            for (IndexableField field : result) {
                System.out.println(field.name() + ": " + field.stringValue() +  " - score: " + hit.score);
            }
        }
    }
于 2013-03-01T20:04:24.490 に答える
0

「LuceneInAction2」という本から

"Luceneは、組み込みのクエリPayloadTermQueryをパッケージorg.apache.lucene.search.payloadsで提供します。このクエリは、指定された用語を含むすべてのドキュメントに一致し、の実際の発生(スパン)を追跡するという点でSpanTermQueryと同じです。一致します。

ただし、各用語の出現時に表示されるペイロードに基づいてスコアリング係数を提供できるようにすることで、さらに進んでいきます。これを行うには、次のように、scorePayloadメソッドを定義する独自のSimilarityクラスを作成する必要があります。

public class BoostingSimilarity extends DefaultSimilarity {
public float scorePayload(int docID, String fieldName,
int start, int end, byte[] payload,
int offset, int length) {
....
}

上記のコードの「開始」は、ペイロードの開始位置に他なりません。ペイロードは用語に関連付けられています。したがって、開始位置は用語にも適用されます(少なくともそれが私が信じていることです)。

上記のコードを使用しますが、ペイロードを無視すると、スコアリングの場所の「開始」位置にアクセスでき、その開始値に基づいてスコアを上げることができます。

例:新しいスコア=元のスコア*(1.0f /開始位置)

上記がうまくいくことを願っています。他の効率的な解決策を見つけたら、ここに投稿してください。

于 2013-03-01T19:59:16.197 に答える