2008-09-30 3 views
18

Я пытаюсь использовать Lucene Java 2.3.2 для реализации поиска по каталогу продуктов. Помимо обычных полей для продукта есть поле под названием «Категория». Продукт может попадать в несколько категорий. В настоящее время я использую FilteredQuery для поиска одного и того же слова поиска с каждой категорией, чтобы получить количество результатов для каждой категории.Использование Lucene для подсчета результатов в категориях

Это приводит к 20-30 внутренним поисковым вызовам для каждого запроса для отображения результатов. Это значительно замедляет поиск. Есть ли более быстрый способ добиться того же результата с помощью Lucene?

ответ

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); 
} 
+0

Это просто счет документов, помеченных каждым термином в поле «Категория», что вы могли бы сделать гораздо быстрее с terms.docFreq(). Недостаток - это пересечение с хитами из критериев поиска пользователя. – erickson 2008-12-05 05:54:49

8

Я не хватает репутации комментировать, но в ответ Matt перепела я уверен, что вы могли бы заменить это:

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

с этим:

int numDocs = terms.docFreq() 

, а затем избавиться от переменной тд вообще. Это должно сделать это еще быстрее.

+0

Вы будете там в кратчайшие сроки (комментирование) – mattlant 2008-10-01 18:15:48

+0

Я сделал это, но он подсчитывает все документы, в моем случае я хочу подсчитать категорию из набора результатов. например, если пользователь ищет «яблоко», то я хочу показать количество совпадений, найденных в категории электроники и фруктов. но ваше и матовое предложение подсчитывают все документы. Я думаю, мне нужно искать против моего искателя, а не читателя, но у поисковика нет TermDocs. – 2013-07-04 09:39:50

0

Так что давайте посмотрим, правильно ли я понял вопрос: учитывая запрос от пользователя, вы хотите показать, сколько совпадений для запроса в каждой категории. Верный?

Думайте об этом так: ваш запрос на самом деле originalQuery AND (category1 OR category2 or ...), кроме как общий балл, который вы хотите получить для каждой из категорий. К сожалению, интерфейс для сбора хитов в Lucene очень узкий, но дает вам общий балл для запроса. Но вы можете реализовать собственный счетчик/сборщик.

Посмотрите на источник для org.apache.lucene.search.DisjunctionSumScorer. Вы можете скопировать некоторые из них, чтобы написать пользовательский счетчик, который выполняет итерации по категориям, в то время как основной поиск продолжается. И вы можете сохранить Map<String,Long>, чтобы отслеживать совпадения в каждой категории.

9

Вот что я сделал, хотя это немного тяжелый на память:

Что вам нужно создать заранее кучу BitSet с, по одному для каждой категории, содержащий Идентификатор документа всех документов в категория. Теперь во время поиска вы используете HitCollector и проверяете идентификаторы doc для битсотов.

Вот код для создания битовых наборов:

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; 
} 

Это лишь один из способов сделать это. Вероятно, вы могли бы использовать 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; 
} 

То, что вы в конечном итоге с представляет собой массив, содержащий количество каждой категории в результатах поиска. Если вам также нужны результаты поиска, вы должны добавить TopDocCollector в свой хит-коллекционер (yo dawg ...). Или вы могли бы снова запустить поиск. 2 поисковых запроса лучше 30.

2

Sachin, я считаю, что вы хотите faceted search. Это не связано с Луценой. Я предлагаю вам попробовать использовать SOLR, который имеет faceting в качестве основной и удобной функции.

Смежные вопросы