0

オートコンプリートのために Lucene で PrefixQuery を使用しようとしています。うまくいくはずだと思ったものの簡単なテストを行いましたが、うまくいきません。いくつかの単純な文字列にインデックスを付け、KeywordAnalyzer を使用してそれらがトークン化されていないことを確認していますが、私の検索はまだ何にも一致しません。接頭辞の一致を取得するには、どのようにフィールドをインデックス化および検索する必要がありますか?

テスト用に作成した単体テストを次に示します。autocomplete メソッドと singleTerm メソッドを除くすべてがパスします。

package com.sample.index;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.KeywordAnalyzer;
import org.apache.lucene.analysis.PerFieldAnalyzerWrapper;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.util.HashMap;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;

public class TestIndexStuff {
    public static final String FIELD_AUTOCOMPLETE = "autocomplete";
    public static final String FIELD_NORMAL = "normal";
    private IndexSearcher searcher;
    private PerFieldAnalyzerWrapper analyzer;

    @Before
    public void init() throws IOException {
        RAMDirectory idx = new RAMDirectory();

        HashMap<String, Analyzer> fieldAnalyzers = new HashMap<String, Analyzer>();
        fieldAnalyzers.put(FIELD_AUTOCOMPLETE, new KeywordAnalyzer());
        analyzer = new PerFieldAnalyzerWrapper(new StandardAnalyzer(Version.LUCENE_35), fieldAnalyzers);
        IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_35, analyzer);

        IndexWriter writer = new IndexWriter(idx, config);
        addDocs(writer);
        writer.close();

        searcher = new IndexSearcher(IndexReader.open(idx));
    }

    private void addDocs(IndexWriter writer) throws IOException {
        for (String text : new String[]{"Fred Rogers", "Toni Reed Preckwinkle", "Randy Savage", "Kathryn Janeway", "Madonna", "Fred Savage"}) {
            Document doc = new Document();
            doc.add(new Field(FIELD_NORMAL, text, Field.Store.YES, Field.Index.ANALYZED));
            doc.add(new Field(FIELD_AUTOCOMPLETE, text, Field.Store.YES, Field.Index.NOT_ANALYZED));
            writer.addDocument(doc);
        }

    }

    @Test
    public void prefixParser() throws ParseException {
        Query prefixQuery = new QueryParser(Version.LUCENE_35, FIELD_AUTOCOMPLETE, analyzer).parse("Fre*");
        assertTrue(prefixQuery instanceof PrefixQuery);

        Query normalQuery = new QueryParser(Version.LUCENE_35, FIELD_AUTOCOMPLETE, analyzer).parse("Fred");
        assertFalse(normalQuery instanceof PrefixQuery);
    }

    @Test
    public void normal() throws ParseException, IOException {
        Query query = new QueryParser(Version.LUCENE_35, FIELD_NORMAL, analyzer).parse("Fred");
        TopDocs topDocs = searcher.search(query, 10);
        assertEquals(2, topDocs.totalHits);
    }

    @Test
    public void autocomplete() throws IOException, ParseException {
        Query query = new QueryParser(Version.LUCENE_35, FIELD_AUTOCOMPLETE, analyzer).parse("Fre*");
        TopDocs topDocs = searcher.search(query, 10);
        assertEquals(2, topDocs.totalHits);
    }

    @Test
    public void singleTerm() throws ParseException, IOException {
        Query query = new QueryParser(Version.LUCENE_35, FIELD_AUTOCOMPLETE, analyzer).parse("Mado*");
        TopDocs topDocs = searcher.search(query, 10);
        assertEquals(1, topDocs.totalHits);
    }
}

編集: @jpountz のおかげで変更後に完全なテストを表示するために、後でこれを読む人のために改訂されたコードを追加します。ただし、大文字と小文字を混在させるのではなく、小文字で索引付けすることにしました。また、単体テストを追加して、中間の用語が一致しないことを確認しました。これは、検索用語で始まるものにのみ一致する必要があるためです。

package com.sample.index;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.KeywordAnalyzer;
import org.apache.lucene.analysis.PerFieldAnalyzerWrapper;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.util.HashMap;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;

public class TestIndexStuff {
    public static final String FIELD_AUTOCOMPLETE = "autocomplete";
    public static final String FIELD_NORMAL = "normal";
    private IndexSearcher searcher;
    private PerFieldAnalyzerWrapper analyzer;

    @Before
    public void init() throws IOException {
        RAMDirectory idx = new RAMDirectory();

        HashMap<String, Analyzer> fieldAnalyzers = new HashMap<String, Analyzer>();
        fieldAnalyzers.put(FIELD_AUTOCOMPLETE, new KeywordAnalyzer());
        analyzer = new PerFieldAnalyzerWrapper(new StandardAnalyzer(Version.LUCENE_35), fieldAnalyzers);
        IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_35, analyzer);

        IndexWriter writer = new IndexWriter(idx, config);
        addDocs(writer);
        writer.close();

        searcher = new IndexSearcher(IndexReader.open(idx));
    }

    private void addDocs(IndexWriter writer) throws IOException {
        for (String text : new String[]{"Fred Rogers", "Toni Reed Preckwinkle", "Randy Savage", "Kathryn Janeway", "Madonna", "Fred Savage"}) {
            Document doc = new Document();
            doc.add(new Field(FIELD_NORMAL, text, Field.Store.YES, Field.Index.ANALYZED));
            doc.add(new Field(FIELD_AUTOCOMPLETE, text.toLowerCase(), Field.Store.YES, Field.Index.NOT_ANALYZED));
            writer.addDocument(doc);
        }

    }

    @Test
    public void prefixParser() throws ParseException {
        Query prefixQuery = new QueryParser(Version.LUCENE_35, FIELD_AUTOCOMPLETE, analyzer).parse("Fre*");
        assertTrue(prefixQuery instanceof PrefixQuery);

        Query normalQuery = new QueryParser(Version.LUCENE_35, FIELD_AUTOCOMPLETE, analyzer).parse("Fred");
        assertFalse(normalQuery instanceof PrefixQuery);
    }

    @Test
    public void normal() throws ParseException, IOException {
        Query query = new QueryParser(Version.LUCENE_35, FIELD_NORMAL, analyzer).parse("Fred");
        TopDocs topDocs = searcher.search(query, 10);
        assertEquals(2, topDocs.totalHits);
    }

    @Test
    public void autocomplete() throws IOException, ParseException {
        Query query = new QueryParser(Version.LUCENE_35, FIELD_AUTOCOMPLETE, analyzer).parse("Fre*");
        TopDocs topDocs = searcher.search(query, 10);
        assertEquals(2, topDocs.totalHits);
    }

    @Test
    public void beginningOnly() throws ParseException, IOException {
        Query query = new QueryParser(Version.LUCENE_35, FIELD_AUTOCOMPLETE, analyzer).parse("R*");
        TopDocs topDocs = searcher.search(query, 10);
        assertEquals(1, topDocs.totalHits);
    }

    @Test
    public void singleTerm() throws ParseException, IOException {
        Query query = new QueryParser(Version.LUCENE_35, FIELD_AUTOCOMPLETE, analyzer).parse("Mado*");
        TopDocs topDocs = searcher.search(query, 10);
        assertEquals(1, topDocs.totalHits);
    }
}
4

1 に答える 1

3

デフォルトでは、QueryParser は特別なクエリ (特にプレフィックス クエリ) の用語を小文字にします。これを無効にするには、QueryParser.setLowercaseExpandedTermsを参照してください。

交換

Query query = new QueryParser(Version.LUCENE_35, FIELD_AUTOCOMPLETE, analyzer).parse("Mado*");

    QueryParser qp = new QueryParser(Version.LUCENE_35, FIELD_AUTOCOMPLETE, analyzer);
    qp.setLowercaseExpandedTerms(false);
    Query query = qp.parse("Mado*");

テストを修正します。

于 2012-04-10T15:30:17.763 に答える