Как индексировать и искать текстовые файлы в Lucene 3.0.2?

Я новичок в Lucene, и у меня возникают проблемы с созданием простого кода для запроса коллекции текстовых файлов.

Я попробовал этот пример, но несовместим с новой версией Lucene.

UDPATE: Это мой новый код, но он все еще не работает.

Ответ 1

Lucene - довольно большая тема с большим количеством классов и методов для покрытия, и вы, как правило, не можете ее использовать, не понимая по крайней мере некоторых базовых понятий. Если вам нужен быстродоступный сервис, используйте Solr. Если вам нужен полный контроль над Lucene, продолжайте чтение. Я расскажу о некоторых основных концепциях и классах Lucene, которые их представляют. (Информацию о том, как читать текстовые файлы в памяти, читайте, например, эту статью).

Что бы вы ни делали в Lucene - индексирование или поиск - вам нужен анализатор. Цель анализатора состоит в том, чтобы токенизировать (разбить на слова) и основыть (получить основание слова) свой текст ввода. Он также выбрасывает наиболее частые слова типа "a", "the" и т.д. Вы можете найти анализаторы для более чем 20 языков, или вы можете использовать SnowballAnalyzer и передать язык в качестве параметра.
Чтобы создать экземпляр SnowballAnalyzer для английского, вы это:

Analyzer analyzer = new SnowballAnalyzer(Version.LUCENE_30, "English");

Если вы собираетесь индексировать тексты на разных языках и хотите автоматически выбирать анализатор, вы можете использовать tika LanguageIdentifier.

Вам нужно сохранить свой индекс где-нибудь. Для этого есть две основные возможности: индекс в памяти, который является простым в использовании, и индекс диска, который является наиболее распространенным.
Используйте любую из следующих двух строк:

Directory directory = new RAMDirectory();   // RAM index storage
Directory directory = FSDirectory.open(new File("/path/to/index"));  // disk index storage

Если вы хотите добавить, обновить или удалить документ, вам понадобится IndexWriter:

IndexWriter writer = new IndexWriter(directory, analyzer, true, new IndexWriter.MaxFieldLength(25000));

Любой документ (текстовый файл в вашем случае) представляет собой набор полей. Чтобы создать документ, в котором будет храниться информация о вашем файле, используйте это:

Document doc = new Document();
String title = nameOfYourFile;
doc.add(new Field("title", title, Field.Store.YES, Field.Index.ANALYZED));  // adding title field
String content = contentsOfYourFile;
doc.add(new Field("content", content, Field.Store.YES, Field.Index.ANALYZED)); // adding content field
writer.addDocument(doc);  // writing new document to the index
Конструктор

Field принимает имя поля, текст и как минимум еще два параметра. Во-первых, это флаг, который показывает, должна ли Lucene сохранять это поле. Если он равен Field.Store.YES, у вас будет возможность вернуть весь текст из индекса, иначе будет храниться только индексная информация об этом.
Второй параметр показывает, должна ли Lucene индексировать это поле или нет. Используйте Field.Index.ANALYZED для любого поля, которое вы собираетесь искать.
Обычно вы используете оба параметра, как показано выше.

Не забудьте закрыть IndexWriter после выполнения задания:

writer.close();

Поиск немного сложнее. Вам понадобится несколько классов: Query и QueryParser, чтобы сделать запрос Lucene из строки, IndexSearcher для фактического поиска, TopScoreDocCollector для хранения результатов (он передается в IndexSearcher в качестве параметра) и ScoreDoc для повторения результатов. Следующий фрагмент показывает, как все это составлено:

IndexSearcher searcher = new IndexSearcher(directory);
QueryParser parser = new QueryParser(Version.LUCENE_30, "content", analyzer);
Query query = parser.parse("terms to search");
TopScoreDocCollector collector = TopScoreDocCollector.create(HOW_MANY_RESULTS_TO_COLLECT, true);
searcher.search(query, collector);

ScoreDoc[] hits = collector.topDocs().scoreDocs;
// `i` is just a number of document in Lucene. Note, that this number may change after document deletion 
for (int i = 0; i < hits.length; i++) {
    Document hitDoc = searcher.doc(hits[i].doc);  // getting actual document
    System.out.println("Title: " + hitDoc.get("title"));
    System.out.println("Content: " + hitDoc.get("content"));
    System.out.println();
}

Обратите внимание, что второй аргумент в конструкторе QueryParser - это поле по умолчанию, то есть поле, которое будет искать, если не задан определитель. Например, если ваш запрос "title: term", Lucene будет искать слово "term" в поле "title" всех документов, но если ваш запрос является просто "термином", если будет искать в поле по умолчанию, в этом случае - "содержимое". Для получения дополнительной информации см. Синтаксис запросов Lucene.
QueryParser также принимает анализатор в качестве последнего аргумента. Это должен быть тот же анализатор, что и для индексации вашего текста.

Последнее, что вы должны знать, это первый параметр TopScoreDocCollector.create. Это всего лишь число, которое представляет, сколько результатов вы хотите собрать. Например, если он равен 100, Lucene будет собирать только первые (по количеству) 100 результатов и отбрасывать остальные. Это всего лишь акт оптимизации - вы получаете лучшие результаты, и если вас не устраивает, вы повторяете поиск с большим числом.

Наконец, не забудьте закрыть поисковик и каталог, чтобы не потерять системные ресурсы:

searcher.close();
directory.close();

EDIT: Также см. класс демонов IndexFiles из Источники Lucene 3.0.

Ответ 2

package org.test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;


import org.apache.lucene.queryParser.*;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;

public class LuceneSimple {

 private static void addDoc(IndexWriter w, String value) throws IOException {
  Document doc = new Document();
  doc.add(new Field("title", value, Field.Store.YES, Field.Index.ANALYZED));
  w.addDocument(doc);
 }



 public static void main(String[] args) throws CorruptIndexException, LockObtainFailedException, IOException, ParseException {

     File dir = new File("F:/tmp/dir");

  StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);

  Directory index = new RAMDirectory();
  //Directory index = FSDirectory.open(new File("lucDirHello") );


  IndexWriter w = new IndexWriter(index, analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED);

  w.setRAMBufferSizeMB(200);

  System.out.println(index.getClass() + " RamBuff:" + w.getRAMBufferSizeMB() );

  addDoc(w, "Lucene in Action");
     addDoc(w, "Lucene for Dummies");
     addDoc(w, "Managing Gigabytes");
     addDoc(w, "The Art of Computer Science");
     addDoc(w, "Computer Science ! what is that ?");


     Long N = 0l;

     for( File f : dir.listFiles() ){
      BufferedReader br = new BufferedReader( new FileReader(f) );
      String line = null;
      while( ( line = br.readLine() ) != null ){
       if( line.length() < 140 ) continue;      
       addDoc(w, line);
       ++N;
      }
      br.close();
     }

     w.close();

     // 2. query
     String querystr = "Computer";

     Query q = new QueryParser( Version.LUCENE_30, "title", analyzer ).parse(querystr);


     //search
     int hitsPerPage = 10;

     IndexSearcher searcher = new IndexSearcher(index, true);

     TopScoreDocCollector collector = TopScoreDocCollector.create(hitsPerPage, true);

     searcher.search(q, collector);

     ScoreDoc[] hits = collector.topDocs().scoreDocs;

     System.out.println("Found " + hits.length + " hits.");
     for(int i=0;i<hits.length;++i) {
       int docId = hits[i].doc;
       Document d = searcher.doc(docId);
       System.out.println((i + 1) + ". " + d.get("title"));
     }


     searcher.close();

 }

}