Ir al contenido principal

Apache Lucene - Indexando Archivos

El presente articulo (uno de varios que espero publicar), muestra como hacer una pequeña introducción a la indexación con Apache Lucene.

Este ejemplo, esta basado en el libro Lucene In Action, pero fue adecuado para funcionar con la versión 3.0 en lugar de la versión 2.0 que se comenta en el libro.

Veamos el siguiente ejemplo y comentemos al respecto:


package org.crjug.lucene.example1;

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

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.store.SimpleFSDirectory;
import org.apache.lucene.util.Version;

/**
* Ejemplo sencillo de como utilizar el indexador.
*
* @author jsanca
*
*/
public class IndexerExample {

public static void main(String[] args) throws IOException {


if (2 != args.length) {

System.out.println("Use como: java "
+ IndexerExample.class.getName()
+ " ");
} else {


makeIndex(args[0], args[1]);

}
} // main.

public static void makeIndex (String sindexDir, String sdataDir) throws IOException {

File indexDir = null;
File dataDir = null;
int numDocsIndexed = 0;
IndexerExample indexer = new IndexerExample ();

indexDir = new File(sindexDir);
dataDir = new File(sdataDir);

numDocsIndexed = indexer.doIndex(indexDir, dataDir);

System.out.println("Numero de documentos indexados: "
+ numDocsIndexed);
} // makeIndex.

/**
* Indexa un directorio (dataDir) y almacena los segmentos del indice en (indexDir)
* @param indexDir
* @param dataDir
* @return Retorna el numero de documento indexados.
* @throws IOException
*/
private int doIndex(File indexDir, File dataDir) throws IOException {

int numDocsIndexed = 0;
IndexWriter indexWriter = null;
boolean createIndex = true;
// Determino si el directorio a indexar, es realmente
// un directorio y existe.
if (!dataDir.exists() || !dataDir.isDirectory()) {

throw new IOException(dataDir + ", no existe o no es un directorio");
}

indexWriter = new IndexWriter(
// Pueden ser directorios virtuales basado en RAM, BD o Archivos.
new SimpleFSDirectory(indexDir),
// El analizador de texto por defecto.
new StandardAnalyzer(Version.LUCENE_30),
// Deseo crear el indice.
createIndex,
// No me preocupa limitar el tamaNo de los campos.
IndexWriter.MaxFieldLength.UNLIMITED);

indexWriter.setUseCompoundFile(false);

// Indexo el directorio.
this.doIndexDirectory (indexWriter, dataDir);

// Obtengo los archivos indexado (aunque no esten merge)
numDocsIndexed = indexWriter.numDocs();
// Llamo al optimizador.
indexWriter.optimize();
// No voy a escribir, cierro el indice.
indexWriter.close();

return numDocsIndexed;
} // doIndex.

/**
* Indexa un directorio en particular.
* @param indexWriter
* @param dataDir
* @throws IOException
*/
private void doIndexDirectory(IndexWriter indexWriter, File dataDir) throws IOException {

File[] files = dataDir.listFiles(new FileFilter () {
// Filtramos para solo leer archivos txt y directorios
public boolean accept(File pathname) {

return pathname.isDirectory() || pathname.getName().endsWith(".txt");
}
});

// Recorro archivos y directorios.
for (File file : files) {

if (file.isDirectory()) { // si es un directorio, entro y lo recorro

this.doIndexDirectory(indexWriter, file);
} else {

// Si es un archivo lo leo e indexo.
this.doIndexFile (indexWriter, file);
}
}
} // doIndexDirectory.

/**
* Realiza la indexacion de un archivo plano.
* @param indexWriter
* @param file
* @throws IOException
*/
private void doIndexFile(IndexWriter indexWriter, File file) throws IOException {

Document document = null;
// Si no es un archivo oculto
// si existe
// y puedo leerlo.
if (!file.isHidden() && file.exists() && file.canRead()) {

System.out.println("Indexando: " + file.getCanonicalPath());

document = new Document ();

// Agrega un campo de texto indexado pero no almacenado.
document.add(new Field("contents", new FileReader (file)));

// Es almacenado pero no indexado, sirve viene como parte de los resultados
// pero no se desea hacer busquedas por el nombre del archivo.
document.add(new Field ("filename", file.getCanonicalPath(), Store.YES, Index.NO));

// Finalmente agrego el archivo al indice.
indexWriter.addDocument(document);
}
} // doIndexFile.
} // E:O:F:IndexerExample.



El ejemplo anterior, muestra la clase IndexerExample. El mismo en términos generales crea un indice invertido, indexando solo archivos extensión .txt, de una carpeta en particular pasada por parámetro y almacenando los segmentos del indice en otro directorio.

La linea 28, simplemente checkea que se envíen al main, dos parámetros, el primero es trata de la carpeta para almacenar el indice y la segunda, la carpeta con los archivos que deseo indexar; el sistema realiza indexación recursiva de directorio.

Seguidamente se invoca al método “makeIndex”, y simplemente se invoca al método de la clase “doIndex”, que retorna la cantidad de archivos indexados.

La siguiente linea, que nos debe interesar es la 76; la misma crea nuestra clase para escribir el indice “IndexWriter”; el primer argumento es un Directory object, el mismo puede tratarse de:

Un directorio en RAM, el cual puede ser útil para indexar unos datos en memoria y consultarlos. Esto por supuesto proporciona mayor velocidad al tener que evitar el acceso al disco duro para leer el archivo, aunque los S.O modernos cuentan con mecanismos de cache, que proporcionan un desempeño bastante aceptable.

También existen dos posibilidades mas, almacenarlo en un directorio en el disco duro (la opción que estamos utilizando) y en base de datos.

El siguiente argumento, se trata del analizador de texto, el mismo sirve para tokenizar, eliminar palabras con poca relevancia o aplicar algoritmos para la optimización del texto, en nuestro caso utilizamos una versión muy sencilla, pero existen analizadores para diferentes idioma o corpus de información para lucene, agregado como contribuciones.

El tercer argumento indica al IndexWriter que se desea o no crear un nuevo indice, en caso que sea false el argumento, el indice será actualizado y no remplazado o creado de cero.

Por ultimo, se indica si deseamos limitar o no, el tamaño de los campos a guardar.

Método DoIndexDirectory

Linea 109, esta linea simplemente obtiene todos los archivos con extensión “txt” o directorios.

A continuación en la linea 118, se iteran todos los archivos, en caso de tratarse de un directorio se indexan los elementos dentro del mismo, de lo contrario se indexa el contenido del archivo “txt”, utilizando para tal objetivo el método: “doIndexFile”.

Método doIndexFile

En la linea 143, se determina si el archivo no es oculto, existe y se puede leer (asunto de permisos)

A continuación creamos el objeto Documento (Document; en Lucene, los objetos indexado son tratados como documentos, el mismo se conceptualiza como una colección de campos, llave, valor, similar a un hash table)

Una vez creado el documento, agregamos al mismo dos campos; 'contents' y 'filename'; el primero es almacenado por defecto como un campo indexado, pero no guardado, lo que quiere decir que el texto indexado no se almacena para su posterior recuperación pero si quedan indexado su contenido, por su lado filename, simplemente se trata de meta información, no es indexada (osea que no se podrá buscar por este campo), pero se volverá parte de los resultado al buscar en contents.

Si seguimos este sencillo flujo y volvemos al método 'doIndex' notaremos los llamados a 'optimize' y 'close'. La invocación del primero permite que Lucene utilice algoritmos de optimizacion sobre el indice, similar a un VACUM en una base de datos, se realiza cuando existen muchas actualizaciones en la base de datos.

El método close, simplemente cierra el indice y probablemente libera algunos recursos que ya no necesitamos utilizar.

Así pues, después de recorrer todo este código y ejecutarlo utilizando alguna colección de archivos de texto veremos que se genera una serie de archivos binarios, en próximos artículos, mostraremos como utilizar estos archivos para hacer búsquedas utilizando varios tipos de consultas.

Comentarios

Entradas más populares de este blog

Validaciones con HTML5 sin necesidad de form.submit

Como parte de HTML5 existe la posibilidad de agregar información a los inputs de un form, para realizar validaciones; podemos indicar si queremos que sea requerido, con el tipo de datos; number, email, etc restringimos los valores que pueden ser agregados, podemos usar alguna mascara para validaciones, colocar mensajes de error custom, etc (en la red existen muchos ejemplos acerca de como customizar formularios). Ahora bien pongamos en contexto, tengo un formulario como este: <form name="managerForm"  id="managerForm">              <p>                  Name:                 <input id="managerNameText" required="required" placeholder="Write here the new manager name" size="40"/>              </p>             <p>                 Email:                 <input id="emailText" required="required" placeholder="myemail@myserver.com" type="email" />

Pasos para remover Postgresql 8.3 en MAC OS

Tomado de: http://forums.enterprisedb.com/posts/list/1437.page In Mac OSX: (Assuming Default Locations) Via uninstaller: 1) In the installation directory, there will be a uninstall-postgresql.app file will be there, executing (double clicking) that will uninstall the postgresql installation. Manual Uninstallation: 1) Stop the server sudo /sbin/SystemStarter stop postgresql-8.3 2) Remove menu shortcuts: sudo rm -rf /Applications/PostgreSQL 8.3 3) Remove the ini file sudo rm -rf /etc/postgres-reg.ini 4) Removing Startup Items sudo rm -rf /Library/StartupItems/postgresql-8.3 5) Remove the data and installed files sudo rm -rf /Library/PostgreSQL/8.3 6) Delete the user postgres sudo dscl . delete /users/postgres

Inventario anual de bebidas

Hola gente, Solo quería compartir mi inventario anual de bebidas (así conocer gustos), excluyendo algunas cervecillas que tengo por ahí guardadas, este es mi inventario: Ron: Flor de Cana 1 botella 5 anos. 2 botellas 7 anos una pacha 7 anos 2 botellas 12 anos 1 botella 18 anos Ron Zacapa 15 anos Centenario pachita 7 anos Centanario pachita 12 anos Bacardi limon Bacardi Razz Ron abuelo 7 anos Bacardi superior 1862 Ron Boltran XL Ron Centenario Garrafon Ron Jamaica Appleton 7 anos Ron Jamaica Appleton 12 anos (muchisimas gracias a Mayra :) Capitan Morgan Rum Jumbie, coconnut splash Ron coconut Malibu Ron Tequila Milagro Silver (muchisimas gracias a Pablito :) Sauza Gold Sauza Reposado Don Julio Reposado Vino Luigi Borer Malbec 2006 Casillero del Diablo, Caberut Sauviguon 2009 Vodka 2 botellas smirnoff y una smirnoff con sabor cranberry Cremas y otro licores Cahuita pacha Amaretto Barinet Licor de menta Licor de agave Rancho Escondido Bayleys 2 botellas (muchisimas gracias a Brian B :) Li