18

Lucene Java 2.3.2 を使用して、製品のカタログに検索を実装しようとしています。商品の通常のフィールドとは別に、「カテゴリー」と呼ばれるフィールドがあります。製品は複数のカテゴリに分類できます。現在、FilteredQuery を使用してすべてのカテゴリで同じ検索語を検索し、カテゴリごとの結果数を取得しています。

これにより、結果を表示するためにクエリごとに 20 ~ 30 回の内部検索呼び出しが発生します。これにより、検索が大幅に遅くなります。Lucene を使用して同じ結果をより速く達成する方法はありますか?

4

5 に答える 5

9

メモリが少し重いですが、これが私がやったことです:

必要なのはBitSet、カテゴリ内のすべてのドキュメントのドキュメント ID を含む、カテゴリごとに 1 つずつ、一連​​の を事前に作成することです。ここで、検索時にHitCollectorを使用し、BitSetに対してドキュメント ID をチェックします。

ビット セットを作成するコードは次のとおりです。

public BitSet[] getBitSets(IndexSearcher indexSearcher, 
                           Category[] categories) {
    BitSet[] bitSets = new BitSet[categories.length];
    for(int i=0; i<categories.length; i++)
    {
        Query query = categories[i].getQuery();
        final BitSet bitset = new BitSet()
        indexSearcher.search(query, new HitCollector() {
            public void collect(int doc, float score) {
                bitSet.set(doc);
            }
        });
        bitSets[i] = bitSet;
    }
    return bitSets;
}

これは、これを行う 1 つの方法にすぎません。カテゴリが十分に単純な場合は、完全な検索を実行する代わりにTermDocsを使用することもできますが、これはインデックスをロードするときに 1 回だけ実行する必要があります。

さて、検索結果のカテゴリを数えるときは、次のようにします。

public int[] getCategroryCount(IndexSearcher indexSearcher, 
                               Query query, 
                               final BitSet[] bitSets) {
    final int[] count = new int[bitSets.length];
    indexSearcher.search(query, new HitCollector() {
        public void collect(int doc, float score) {
            for(int i=0; i<bitSets.length; i++) {
                if(bitSets[i].get(doc)) count[i]++;
            }
        }
    });
    return count;
}

最終的に得られるのは、検索結果内のすべてのカテゴリの数を含む配列です。検索結果も必要な場合は、TopDocCollector をヒット コレクターに追加する必要があります (よろしいでしょうか...)。または、検索を再度実行することもできます。2 回の検索は 30 回よりも優れています。

于 2009-01-27T08:30:42.867 に答える
8

コメントするほどの評判はありません (!) が、Matt Quail の回答では、これを置き換えることができると確信しています。

int numDocs = 0;
td.seek(terms);
while (td.next()) {
    numDocs++;
}

これとともに:

int numDocs = terms.docFreq()

td 変数を完全に削除します。これにより、さらに高速になるはずです。

于 2008-10-01T18:11:52.327 に答える
2

サチン、あなたは多面的な検索を望んでいると思います。Lucene ではそのままでは使用できません。主要で便利な機能としてファセットを備えたSOLRを使用することをお勧めします。

于 2009-04-12T10:12:01.547 に答える
2

TermDocs iteratorを使用して、カテゴリに一致するすべてのドキュメントを調べることを検討してください。

このコード例は、各「カテゴリ」用語を調べ、その用語に一致するドキュメントの数をカウントします。

public static void countDocumentsInCategories(IndexReader reader) throws IOException {
    TermEnum terms = null;
    TermDocs td = null;


    try {
        terms = reader.terms(new Term("Category", ""));
        td = reader.termDocs();
        do {
            Term currentTerm = terms.term();

            if (!currentTerm.field().equals("Category")) {
                break;
            }

            int numDocs = 0;
            td.seek(terms);
            while (td.next()) {
                numDocs++;
            }

            System.out.println(currentTerm.field() + " : " + currentTerm.text() + " --> " + numDocs);
        } while (terms.next());
    } finally {
        if (td != null) td.close();
        if (terms != null) terms.close();
    }
}

このコードは、インデックスが大きい場合でもかなり高速に実行されるはずです。

そのメソッドをテストするコードを次に示します。

public static void main(String[] args) throws Exception {
    RAMDirectory store = new RAMDirectory();

    IndexWriter w = new IndexWriter(store, new StandardAnalyzer());
    addDocument(w, 1, "Apple", "fruit", "computer");
    addDocument(w, 2, "Orange", "fruit", "colour");
    addDocument(w, 3, "Dell", "computer");
    addDocument(w, 4, "Cumquat", "fruit");
    w.close();

    IndexReader r = IndexReader.open(store);
    countDocumentsInCategories(r);
    r.close();
}

private static void addDocument(IndexWriter w, int id, String name, String... categories) throws IOException {
    Document d = new Document();
    d.add(new Field("ID", String.valueOf(id), Field.Store.YES, Field.Index.UN_TOKENIZED));
    d.add(new Field("Name", name, Field.Store.NO, Field.Index.UN_TOKENIZED));

    for (String category : categories) {
        d.add(new Field("Category", category, Field.Store.NO, Field.Index.UN_TOKENIZED));
    }

    w.addDocument(d);
}
于 2008-09-30T12:09:27.137 に答える
0

質問を正しく理解しているかどうか見てみましょう。ユーザーからのクエリが与えられた場合、各カテゴリのクエリに一致するものがいくつあるかを表示したいとします。正しい?

次のように考えてみてください。クエリは、実際にはoriginalQuery AND (category1 OR category2 or ...)、各カテゴリの数値を取得する全体的なスコアを除いてです。残念ながら、Lucene でヒットを収集するためのインターフェイスは非常に狭く、クエリの全体的なスコアしか表示されません。ただし、カスタムのスコアラー/コレクターを実装できます。

org.apache.lucene.search.DisjunctionSumScorer のソースを見てください。その一部をコピーして、メインの検索が行われている間にカテゴリの一致を繰り返すカスタム スコアラーを作成できます。Map<String,Long>また、各カテゴリの一致を追跡することもできます。

于 2008-12-24T19:32:50.653 に答える