使用Lucene计算类别中的结果

18 java lucene search filtering catalog

我正在尝试使用Lucene Java 2.3.2来实现对产品目录的搜索.除了产品的常规字段外,还有一个名为"类别"的字段.产品可以分为多个类别.目前,我使用FilteredQuery搜索每个类别的相同搜索词,以获得每个类别的结果数.

这导致每个查询20-30个内部搜索调用以显示结果.这大大减慢了搜索速度.使用Lucene有更快的方法来实现相同的结果吗?

its*_*dok 9

这就是我做的,虽然它对内存有点沉重:

你需要的是提前创建一堆BitSets,每个类别一个,包含一个类别中所有文档的doc id.现在,在搜索时,您使用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;
}
Run Code Online (Sandbox Code Playgroud)

这只是一种方法.如果您的类别足够简单,您可以使用TermDocs而不是运行完整搜索,但这应该只在您加载索引时运行一次.

现在,在计算搜索结果类别时,您可以这样做:

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;
}
Run Code Online (Sandbox Code Playgroud)

您最终得到的是一个数组,其中包含搜索结果中每个类别的计数.如果您还需要搜索结果,则应该向命中收集器添加TopDocCollector(yo dawg ...).或者,您可以再次运行搜索.2次搜索优于30次.


Row*_*wan 8

我没有足够的声誉来评论(!)但是在Matt Quail的回答中我很确定你可以替换它:

int numDocs = 0;
td.seek(terms);
while (td.next()) {
    numDocs++;
}
Run Code Online (Sandbox Code Playgroud)

有了这个:

int numDocs = terms.docFreq()
Run Code Online (Sandbox Code Playgroud)

然后完全摆脱td变量.这应该会更快.


Mat*_*ail 2

您可能需要考虑使用TermDocs 迭代器来查找与类别匹配的所有文档。

此示例代码遍历每个“类别”术语,然后计算与该术语匹配的文档数量。

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

即使对于大型索引,此代码也应该运行得相当快。

这是测试该方法的一些代码:

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);
}
Run Code Online (Sandbox Code Playgroud)