Recuperación de datos con Lucene

Lucene es una API de código abierto soportada por Apache. Es interesante y excelente herramienta  para agregarle la función de indexado y búsqueda a una aplicación.  Hace un tiempo que vengo usando esta librería para mis programas. Entenderlo e implementarlo es realmente muy fácil. Les voy a mostrar en unos simples pasos como hacerlo.

Indexar

Primero vamos a crear un indice, en este caso en memoria.

StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_40);
Directory index = new RAMDirectory();
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_40, analyzer);
IndexWriter w = new IndexWriter(index, config);
addDoc(w, "Lucene in Action", "193398817");
addDoc(w, "Lucene for Dummies", "55320055Z");
addDoc(w, "Managing Gigabytes", "55063554A");
addDoc(w, "The Art of Computer Science", "9900333X");
w.close();

El método addDoc() es lo que agrega los documentos al indice. El mismo es el siguiente:

private static void addDoc(IndexWriter w, String title, Stringisbn) throws IOException {
  Document doc = new Document();
  doc.add(new TextField("title", title, Field.Store.YES));
  doc.add(new StringField("isbn", isbn, Field.Store.YES));
  w.addDocument(doc);
}

El uso de TextField es para el campo que queremos obtener (mal dicho tokenizar), y StringField es par indicar si ese campo sera almacenado en el indice. En ese punto tenemos mas opciones para indicarle al campo, o FIELD, que también son muy importantes:

  • Index.ANALYZED: Para indicarle que el valor del campo sea posible buscarlo. Esta opción es útil para campos como bodym titlem abstract,  etc.
  • Index.NOT_ANALYZED: En este caso el indice no analiza el valor del campo. En su lugar trata el valor del campo como un simple token y permite que pueda ser buscado. Esta opción es útil para campos que no necesitan ser analizados pero si almacenados para las búsquedas, como por ejemplo URL, nombres personales, fechas, telefonos etc. Es una buena opción para cuando se van ha realizar búsquedas exactas.
  • Index.ANALYZED_NO_NORMS: Es una variante del Index.ANALYZED que no almacena ninguna información de normalizacion en el indice. Los registros de normalizacion guardan información como el tiempo de indexacion, el peso del termino que puede consumir mas memoria al momento de realizar una búsqueda.
  • Index.NOT_ANALYZED_NO_NORMS: Igual que Index.NOT_ANALYZED, pero tambien no almacena informacion de normalización. Esta opción es frecuentemente usada cuando se quiere salvar espacio de memoria durante las búsquedas.
  • Index.NO: No deja al valor del campo disponible para las búsquedas.

Query (Tirate un query papa)

Ya todos sabemos lo que es una query.  Leemos la query desde la entrada estándar, se “parsea” (se separa en tokens) y se crea la query propiamente dicha.

String querystr = args.length > 0 ? args[0] : "lucene";
Query q = new QueryParser(Version.LUCENE_40, "title", analyzer).parse(querystr);
Búsqueda en el índice

Using the Query we create a Searcher to search the index. Then a TopScoreDocCollector is instantiated to collect the top 10 scoring hits.

int hitsPerPage = 10;
IndexReader reader = IndexReader.open(index);
IndexSearcher searcher = new IndexSearcher(reader);
TopScoreDocCollector collector =TopScoreDocCollector.create(hitsPerPage, true);
searcher.search(q, collector);
ScoreDoc[] hits = collector.topDocs().scoreDocs;

Mostrando los resultados

Ahora podemos obtener los resultados de nuestra busqueda  mostrandole  al usuario los mismos.

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("isbn") + "\t" +d.get("title"));
}