Indexación y búsquedas en PHP con Lucene
Lucene es una API realizada dentro del proyecto Apache, cuyo objetivo es indexar información y realizar búsquedas sobre ella. Inicialmente se realizó en Java pero existen traducciones a diferentes lenguajes. En el caso de PHP el Zend Framework incluye una implementación de esta API que incluso mantiene la compatibilidad de los índices que genera la API de Java. He estado realizando unas pruebas con esta API y la verdad es que es muy sencilla de utilizar y funciona realmente bien, aunque su rendimiento no es tan bueno como en Java.
Como hemos comentado Lucene ofrece capacidades de indexación y búsqueda. Es posible indexar de forma automática diferentes tipos de documentos como Word, Excel, o páginas web, pero permite de forma general indexar cualquier tipo de información pudiendo tener esta atributos independientes sobre los que luego podrán realizarse búsquedas. A la hora de realizar las búsquedas Lucene dispone de una sintaxis muy completa que permite realizar búsquedas de todo tipo obteniendo los resultados más adecuados.
Para poder utilizar Lucene en nuestras aplicaciones PHP el primer paso consiste en la instalación del Zend Framework, aunque dado que este framework está construido de forma modular, sería suficiente también con instalar la subcarpeta Search
del framework.
$ sudo apt-get install libzend-framework-php |
Si tenemos curiosidad por ver cómo funciona interiormente Lucene podemos ver los scripts de PHP del framework en /usr/share/php/libzend-framework-php/Zend/Search
.
Vamos a hacer un pequeño ejemplo de utilización de la API aplicándola a un CMS en el que gestionamos un conjunto de objetos de tipo Noticia. El primer paso consiste en crear el índice, indicándole un path del filesystem donde ubicarlo, preferiblemente fuera del path público del web.
$path = '/www/private/lucene'; if(!is_dir($path)){ $index = Zend_Search_Lucene::create($path); } |
La carpeta indicada (/www/private/lucene) no debería existir ya que se encarga la propia API de generarla. Si en algún momento quisiéramos eliminar todo el índice sería suficiente con borrar esta carpeta.
Vamos ahora a ver cómo indexar la información. Supongamos que el objeto noticia tiene los siguientes atributos: id de la noticia, fuente, título, entradilla y texto. Lucene permite indexar todos estos campos de forma independiente, de forma que luego puedan realizarse búsquedas por estos atributos. La siguiente imagen muestra los diferentes tipos de campo y sus características. La columna Stored indica si el valor de campo se almacena, de forma que cuando se realicen búsquedas se pueda obtener esa información, Indexed indica si el campo se va a indexar, Tokenized indica si la información del campo debe tratarse como un conjunto de tokens. La columna Binary indica si el campo es de tipo binario, lo cual hace que el valor se almacene pero no se indexe ni genere tokens.
Veamos cómo realizar la indexación de una noticia mediante el siguiente código PHP. Las primeras líneas se encargan de comprobar si el índice existe, creándolo cuando es necesario, y abriéndolo cuando ya existe.
$path = '/www/private/lucene'; if(!is_dir($path)){ $index = Zend_Search_Lucene::create($path); } else{ $index = Zend_Search_Lucene::open($path); } $doc = new Zend_Search_Lucene_Document(); $doc->addField(Zend_Search_Lucene_Field::Keyword('type', 'noticia')); $doc->addField(Zend_Search_Lucene_Field::Keyword('id_noticia', $noticia->id)); $doc->addField(Zend_Search_Lucene_Field::Keyword('fuente', $noticia->fuente)); $doc->addField(Zend_Search_Lucene_Field::Text('titulo', $noticia->titulo)); $doc->addField(Zend_Search_Lucene_Field::Text('entradilla', $noticia->entradilla)); $doc->addField(Zend_Search_Lucene_Field::UnStored('texto', $noticia->texto)); $index->addDocument($doc); |
Mediante Zend_Search_Lucene_Document
se genera un documento al que se le añaden los campos necesarios para realizar la indexación de la noticia. Los primeros campos que se incluyen son el id_noticia y la fuente, que se incluyen como campos de tipo Keyword
, de forma que pueda luego obtenerse una noticia por su id, o todas las noticias generadas por una fuente. Se incluye también un campo de nombre tipo, para poder recuperar también todas las noticias de forma conjunta. Los campos título y entradilla se incluyen como Text
para que sean indexados y almacenados. La diferencia entre los campos Keyword
y Text
es que en estos últimos el contenido del campo se divide en tokens (palabras) que son indexadas de forma separada. El campo texto se incluye como tipo de campo UnStored
de forma que sus tokens son indexados pero no se almacena el contenido del campo.
Una vez que la noticia ha sido indexada ya es posible realizar búsquedas en el índice. Lucene dispone de una sintaxis muy completa que puede ser utilizada directamente para recuperar información, pero dispone también de una API específicamente diseñada para generar consultas desde código.
La siguiente consulta obtiene todas las noticias de la agencia EFE y muestra su id, título y entradilla. No es posible mostrar el texto de la noticia porque este campo lo hemos definido como UnStored, por lo que su valor no ha sido almacenado.
$hits = $index->find('fuente:EFE'); foreach($hits as $hit){ echo $hit->id_noticia; echo $hit->titulo; echo $hit->entradilla; } |
Lucene asigna un identificador interno a cada documento indexado, el cual puede utilizarse para eliminar un documento del índice. El siguiente ejemplo elimina del índice una noticia a partir de su identificador de noticia.
$term = new Zend_Search_Lucene_Index_Term($noticia->id, 'id_noticia'); $query = new Zend_Search_Lucene_Search_Query_Term($term); $hits = $index->find($query); if(count($hits) > 0){ // debería ser un solo registro foreach ($hits as $hit) { $index->delete($hit->id); $index->commit(); } } |
Lo primero que se hace es realizar una búsqueda del documento indexado a partir de su id utilizando la API de queries. A continuación se comprueba si se ha obtenido algún resultado y si es así se solicita el borrado del documento indexado a partir de su id en lucene.
Hay que tener en cuenta que los borrados no se hacen efectivos hasta que no se llama $index->optimize()
, el cual borra la información de los documentos borrados y reordena los id internos. Esto implica que no se deben utilizar los id internos de lucene como forma de enlazar con los documentos indexados, ya que estos identificadores pueden cambiar.
Buen artículo amigo,
A continuación adjunto un post que explica cómo encapsular la funcionalidad de las consultas lucene en una clase PHP. Ánimo y saludos
http://programarivm.com/2012/01/internacionaliza-i18n-tus-aplicaciones-php-de-tamano-pequeno-o-mediano-de-la-forma-mas-rapida-y-sencilla/
creo que te has equivocado de url, la que indicas habla de i18n