TigerZF
🌐Español

61.10. Buenas prácticas

61.10.1. Nombres de campos

No existen limitaciones para los nombres de campos en Zend_Search_Lucene.

No obstante, es una buena idea no usar los nombres 'id' y 'score' para evitar ambigüedad en los nombres de las propiedades de QueryHit.

Las propiedades id y score de Zend_Search_Lucene_Search_QueryHit siempre se refieren al id interno del documento de Lucene y a la puntuación del resultado. Si el documento indexado tiene los mismos campos almacenados, debe usar el método getDocument() para acceder a ellos:

$hits = $index->find($query);

foreach ($hits as $hit) {
    // Get 'title' document field
    $title = $hit->title;

    // Get 'contents' document field
    $contents = $hit->contents;

    // Get internal Lucene document id
    $id = $hit->id;

    // Get query hit score
    $score = $hit->score;

    // Get 'id' document field
    $docId = $hit->getDocument()->id;

    // Get 'score' document field
    $docId = $hit->getDocument()->score;

    // Another way to get 'title' document field
    $title = $hit->getDocument()->title;
}

61.10.2. Rendimiento de la indexación

El rendimiento de la indexación es un compromiso entre los recursos usados, el tiempo de indexación y la calidad del índice.

La calidad del índice está completamente determinada por el número de segmentos del índice.

Cada segmento de índice es una porción de datos totalmente independiente. Por eso, los índices que contienen más segmentos necesitan más memoria y tiempo para la búsqueda.

La optimización del índice es un proceso que combina varios segmentos en uno nuevo. Un índice totalmente optimizado contiene un único segmento.

La optimización completa del índice se puede realizar con el método optimize():

$index = Zend_Search_Lucene::open($indexPath);

$index->optimize();

La optimización del índice trabaja con flujos de datos y no requiere mucha memoria, pero sí requiere recursos de procesador y tiempo.

Los segmentos de un índice de Lucene no son actualizables por naturaleza (la operación de actualización requiere que el archivo del segmento se reescriba por completo). Por lo tanto, añadir nuevos documentos a un índice siempre genera un nuevo segmento. Esto, a su vez, disminuye la calidad del índice.

Después de cada generación de segmento se ejecuta un proceso de auto-optimización del índice que consiste en combinar segmentos parciales.

Hay tres opciones para controlar el comportamiento de la auto-optimización (vea la sección Optimización del índice):

  • MaxBufferedDocs es el número de documentos que se pueden almacenar en memoria antes de que se genere un nuevo segmento y se escriba en el disco duro.

  • MaxMergeDocs es el número máximo de documentos combinados por el proceso de auto-optimización en un nuevo segmento.

  • MergeFactor determina con qué frecuencia se realiza la auto-optimización.

[Note] Nota

Todas estas opciones son propiedades del objeto Zend_Search_Lucene, no propiedades del índice. Afectan únicamente al comportamiento del objeto actual Zend_Search_Lucene y pueden variar entre distintos scripts.

MaxBufferedDocs no tiene ningún efecto si solo indexa un documento por ejecución de script. Por otro lado, es muy importante para la indexación por lotes. Valores mayores aumentan el rendimiento de la indexación, pero también requieren más memoria.

Simplemente no hay forma de calcular el mejor valor para el parámetro MaxBufferedDocs porque depende del tamaño medio de los documentos, del analizador en uso y de la memoria permitida.

Una buena manera de encontrar el valor adecuado es realizar varias pruebas con el documento más grande que espera añadir al índice [18] . Es una buena práctica no usar más de la mitad de la memoria permitida.

MaxMergeDocs limita el tamaño del segmento (en términos de documentos). Por lo tanto, también limita el tiempo de auto-optimización al garantizar que el método addDocument() no se ejecute más de un cierto número de veces. Esto es muy importante para las aplicaciones interactivas.

Reducir el parámetro MaxMergeDocs también puede mejorar el rendimiento de la indexación por lotes. La auto-optimización del índice es un proceso iterativo y se realiza de abajo hacia arriba. Los segmentos pequeños se combinan en un segmento más grande, que a su vez se combinan en segmentos aún más grandes, y así sucesivamente. La optimización completa del índice se logra cuando solo queda un gran archivo de segmento.

Los segmentos pequeños generalmente disminuyen la calidad del índice. Muchos segmentos pequeños también pueden provocar el error "Too many open files" determinado por las limitaciones del sistema operativo [19].

en general, la optimización del índice en segundo plano debería realizarse para el modo de indexación interactivo, y MaxMergeDocs no debería ser demasiado bajo para la indexación por lotes.

MergeFactor afecta la frecuencia de auto-optimización. Valores más bajos aumentan la calidad de los índices no optimizados. Valores más grandes aumentan el rendimiento de la indexación, pero también aumentan el número de segmentos combinados. Esto de nuevo puede provocar el error "Too many open files".

MergeFactor agrupa los segmentos del índice por su tamaño:

  1. No mayor que MaxBufferedDocs.

  2. Mayor que MaxBufferedDocs, pero no mayor que MaxBufferedDocs*MergeFactor.

  3. Mayor que MaxBufferedDocs*MergeFactor, pero no mayor que MaxBufferedDocs*MergeFactor*MergeFactor.

  4. ...

Zend_Search_Lucene comprueba durante cada llamada a addDocument() si combinar algunos segmentos puede mover el segmento recién creado al siguiente grupo. Si es así, entonces se realiza la combinación.

Así, un índice con N grupos puede contener MaxBufferedDocs + (N-1)*MergeFactor segmentos y contiene al menos MaxBufferedDocs*MergeFactor(N-1) documentos.

Esto proporciona una buena aproximación del número de segmentos en el índice:

NumberOfSegments <= MaxBufferedDocs + MergeFactor*log MergeFactor (NumberOfDocuments/MaxBufferedDocs)

MaxBufferedDocs está determinado por la memoria permitida. Esto permite obtener el factor de combinación apropiado para lograr un número razonable de segmentos.

Ajustar el parámetro MergeFactor es más efectivo para el rendimiento de la indexación por lotes que MaxMergeDocs. Pero también es más grueso. Así que use la estimación anterior para ajustar MergeFactor, y luego pruebe con MaxMergeDocs para obtener el mejor rendimiento de indexación por lotes.

61.10.3. El índice durante el cierre

La instancia de Zend_Search_Lucene realiza cierto trabajo al finalizar si se añadieron documentos al índice pero no se escribieron en un nuevo segmento.

También puede desencadenar un proceso de auto-optimización.

El objeto índice se cierra automáticamente cuando él, y todos los objetos QueryHit devueltos, salen de ámbito.

Si el objeto índice se almacena en una variable global, entonces solo se cierra al final de la ejecución del script [20].

El procesamiento de excepciones de PHP también se cierra en ese momento.

Esto no impide el proceso normal de cierre del índice, pero puede impedir un diagnóstico de error preciso si ocurre algún error durante el cierre.

Hay dos formas de evitar este problema.

La primera es forzar la salida de ámbito:

$index = Zend_Search_Lucene::open($indexPath);

...

unset($index);

Y la segunda es realizar una operación de confirmación (commit) antes del final de la ejecución del script:

$index = Zend_Search_Lucene::open($indexPath);

$index->commit();

Esta posibilidad también se describe en la sección "Avanzado. Uso del índice como propiedad estática".

61.10.4. Recuperar documentos por id único

Es una práctica común almacenar algún id único de documento en el índice. Algunos ejemplos incluyen la url, la ruta o el id de base de datos.

Zend_Search_Lucene proporciona un método termDocs() para recuperar documentos que contengan los términos especificados.

Esto es más eficiente que usar el método find():

// Retrieving documents with find() method using a query string
$query = $idFieldName . ':' . $docId;
$hits  = $index->find($query);
foreach ($hits as $hit) {
    $title    = $hit->title;
    $contents = $hit->contents;
    ...
}
...

// Retrieving documents with find() method using the query API
$term = new Zend_Search_Lucene_Index_Term($docId, $idFieldName);
$query = new Zend_Search_Lucene_Search_Query_Term($term);
$hits  = $index->find($query);
foreach ($hits as $hit) {
    $title    = $hit->title;
    $contents = $hit->contents;
    ...
}

...

// Retrieving documents with termDocs() method
$term = new Zend_Search_Lucene_Index_Term($docId, $idFieldName);
$docIds  = $index->termDocs($term);
foreach ($docIds as $id) {
    $doc = $index->getDocument($id);
    $title    = $doc->title;
    $contents = $doc->contents;
    ...
}

61.10.5. Uso de memoria

Zend_Search_Lucene es un módulo relativamente intensivo en el uso de memoria.

Utiliza memoria para almacenar en caché cierta información y optimizar el rendimiento de la búsqueda y la indexación.

La memoria necesaria varía según los diferentes modos.

El índice del diccionario de términos se carga durante la búsqueda. En realidad es cada 128º [21] término del diccionario completo.

Por lo tanto, el uso de memoria aumenta si tiene un número elevado de términos únicos. Esto puede suceder si utiliza frases no tokenizadas como valores de campo o indexa un gran volumen de información no textual.

Un índice no optimizado consta de varios segmentos. Esto también aumenta el uso de memoria. Los segmentos son independientes, por lo que cada segmento contiene su propio diccionario de términos y su propio índice de diccionario de términos. Si un índice consta de N segmentos, puede aumentar el uso de memoria hasta N veces en el peor de los casos. Realice la optimización del índice para combinar todos los segmentos en uno y evitar este consumo de memoria.

La indexación utiliza la misma memoria que la búsqueda, más la memoria para almacenar en búfer los documentos. La cantidad de memoria utilizada puede gestionarse con el parámetro MaxBufferedDocs.

La optimización del índice (total o parcial) utiliza un procesamiento de datos en flujo y no requiere mucha memoria.

61.10.6. Codificación

Zend_Search_Lucene trabaja internamente con cadenas UTF-8. Por lo tanto, todas las cadenas devueltas por Zend_Search_Lucene están codificadas en UTF-8.

No debería preocuparse por la codificación si trabaja con datos puramente ASCII, pero debería tener cuidado si ese no es el caso.

Una codificación incorrecta puede provocar avisos de error en el momento de la conversión de codificación o pérdida de datos.

Zend_Search_Lucene ofrece una amplia gama de posibilidades de codificación para los documentos indexados y las consultas analizadas.

La codificación puede especificarse explícitamente como parámetro opcional de los métodos de creación de campos:

$doc = new Zend_Search_Lucene_Document();
$doc->addField(Zend_Search_Lucene_Field::Text('title',
                                              $title,
                                              'iso-8859-1'));
$doc->addField(Zend_Search_Lucene_Field::UnStored('contents',
                                                  $contents,
                                                  'utf-8'));

Esta es la mejor manera de evitar la ambigüedad en la codificación utilizada.

Si se omite el parámetro opcional de codificación, entonces se utiliza la configuración regional (locale) actual. La configuración regional actual puede contener datos de codificación de caracteres además de la especificación de idioma:

setlocale(LC_ALL, 'fr_FR');
...

setlocale(LC_ALL, 'de_DE.iso-8859-1');
...

setlocale(LC_ALL, 'ru_RU.UTF-8');
...

Se utiliza el mismo enfoque para establecer la codificación de la cadena de consulta.

Si no se especifica la codificación, se utiliza la configuración regional actual para determinarla.

La codificación puede pasarse como parámetro opcional si la consulta se analiza explícitamente antes de la búsqueda:

$query =
    Zend_Search_Lucene_Search_QueryParser::parse($queryStr, 'iso-8859-5');
$hits = $index->find($query);
...

La codificación por defecto también puede especificarse con el método setDefaultEncoding():

Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding('iso-8859-1');
$hits = $index->find($queryStr);
...

La cadena vacía implica la 'configuración regional actual'.

Si se especifica la codificación correcta, puede ser procesada correctamente por el analizador. El comportamiento real depende de qué analizador se utilice. Consulte la sección Conjunto de caracteres para más detalles.

61.10.7. Mantenimiento del índice

Debe quedar claro que Zend_Search_Lucene, al igual que cualquier otra implementación de Lucene, no constituye una "base de datos".

Los índices no deben utilizarse para el almacenamiento de datos. No ofrecen funcionalidad de copia de seguridad/restauración parcial, journaling, registro de transacciones y muchas otras características asociadas con los sistemas de gestión de bases de datos.

No obstante, Zend_Search_Lucene intenta mantener los índices en un estado consistente en todo momento.

La copia de seguridad y restauración del índice debe realizarse copiando el contenido de la carpeta del índice.

Si se produce corrupción del índice por cualquier motivo, el índice corrupto debe restaurarse o reconstruirse por completo.

Por lo tanto, es una buena idea hacer copias de seguridad de los índices grandes y almacenar registros de cambios para realizar operaciones de restauración manual y de reaplicación (roll-forward) si es necesario. Esta práctica reduce drásticamente el tiempo de restauración del índice.



[18] memory_get_usage() y memory_get_peak_usage() pueden usarse para controlar el uso de memoria.

[19] Zend_Search_Lucene mantiene cada archivo de segmento abierto para mejorar el rendimiento de la búsqueda.

[20] Esto también puede ocurrir si el índice o las instancias de QueryHit se referencian en alguna estructura de datos cíclica, ya que PHP recolecta como basura los objetos con referencias cíclicas solo al final de la ejecución del script.

[21] El formato de archivo de Lucene permite configurar este número, pero Zend_Search_Lucene no lo expone en su API. No obstante, aún tiene la posibilidad de configurar este valor si el índice se prepara con otra implementación de Lucene.