Indexar en Solr

Indexado en Solr
Solr expone una serie de servicios a los que podemos lanzarle los documentos a indexar en forma de XML.

En este post, vamos a revisar el proceso con el que alimentamos Solr con los datos que extraemos de nuestra base de datos.

 

 

 

Estructura del fichero de indexado

Para entregarle los datos al buscador, debemos conformar nuestros documentos en el formato XML propio de Solr. La estructura es muy sencilla documentos que contienen campos. Todos los campos que indiquemos deberán estar definidos en el esquema, ya sea como campos normales o dinámicos. Si Solr recibe un campo que no conoce retorna una excepción.

En un mismo fichero podemos incluir tantos documentos como sea necesario.

<?xml version="1.0" encoding="UTF-8" ?>
<add>
    <doc>
        <field name="id">1</field>
        <field name="titulo">Tomates Verdes Fritos</field>
        <field name="genero">Drama</field>
    </doc>
    <doc>
        <field name="id">2</field>
        <field name="titulo">Los gemelos golpean dos veces</field>
        <field name="genero">Acción</field>
        <field name="genero">Comedia</field>
    </doc>
</add>

La codificación del fichero debe ser utf-8 y debemos evitar caracteres de control. Ya sea filtrando el contenido antes de volcarlo al fichero, o limpiando el fichero antes de indexarlo. Si el contenido de nuestra base de datos ha pasado en algún momento por un proceso de copiar y pegar es muy posible que contenga caracteres de control que no estamos viendo.

Podemos usar el comando tr para eliminar cada carácter de control prohibido o eliminar todos de golpe con él comando:

tr -d [:cntrl:] < fichero_sucio.xml > fichero_limpio.xml

Este comando elimina también él salto de línea, dejando él fichero ilegible para nuestros ojos, pero con un tamaño sensiblemente menor.

 

Añadir campos a un documento

Los campos se indicarán entre etiquetas doc, y deben haber sido creados en el esquema.

Si un campo no es requerido y presenta un null, es preferible no incluir el campo a poner un campo vacío.

<file name="genero" />

De la misma maner. Si un campo numérico presenta un null y está indexado. Deberemos indicar valor cero o “máximo”, según dicte nuestra funcionalidad.

 

Nota: No debemos olvidar usar CDATA cuando sea necesario para escapar los caracteres no permitidos en el XML.

 

Boost de documento y boost de campo

Es posible decirle a Solr que potencie el contenido de determinados documentos para hacerlos más relevantes. Para esto añadimos él atributo boost.

Esto no quiere decir que este documento aparezca en búsquedas para las qué no es relevante.

Para qué entendamos como funciona. Pensemos qué él orden natural de Solr es el score. Pongamos un ejemplo:

Una búsqueda dada asigna un valor de score para una palabra concreta de 0.643841. Si el documento o el campo que contiene la palabra en cuestión tuviera un boost=2.0. El score total asignado por tener la palabra, habría pasado a ser el doble 1.287682. El score total es la suma de los scores parciales, con lo que el efecto puede quedar diluido.

Boost sólo potencia, para forzar resultados hay otros métodos.

 

Borrar Registros

Tan importante como el proceso de indexado de los registros, es el de eliminación de aquellos que ya no deben aparecer en nuestro índice. Es habitual plantear una estrategia de indexado que tiene en cuenta todos los supuestos en los que un registro debe mandarse al buscador, pero que olvide algún supuesto en el que los registros deben darse de baja del índice.

Un caso típico en una aplicación de comercio electrónico, es indexar los productos en nuestra base de datos que cumplan una serie de condiciones.

Cuando incorporamos un producto a nuestra base de datos se lo enviamos al buscador para indexar si cumple las siguientes condiciones:

  • Marcado como activo
  • Stock > 0
  • precio > 0 (no es broma)

Seguro que no se nos olvida eliminar el registro del índice en caso de que se elimine un producto, pero debemos hacerlo también cuando ya no se den las condiciones de entrada. En caso contrario estaríamos dejando registros huérfanos en el índice.

El proceso de borrado en Solr es similar al de indexado se realiza mediante ficheros.

Podemos borrar por id:

<delete><id>1</id></delete>

o por query:

<delete><query>genero:Drama</query></delete>

En este caso concreto estamos usando un campo que es case sensitive. Debemos indicar el literal exacto, para que se cumpla la condición de borrado

Para eliminar todos los registros utilizaríamos la siguiente query:

<delete><query>*:*</query></delete>

También es posible hacer un fichero que mezcle los dos tipos de borrado:

<?xml version="1.0" encoding="UTF-8" ?>
<delete>
    <id>1</id>
    <id>3</id>
    <query>genero:Drama</query>
</delete>

Nota: Para hacer un fichero mixto de inserción/actualización y borrado. debemos añadir una etiqueta update que envuelva a las etiquetas add y delete.

<?xml version="1.0" encoding="UTF-8" ?>
<update>
    <add></add>
    <delete></delete>
</update>

 

Indexar el fichero

Una vez generado el fichero tenemos que mandárselo a Solr para indexarlo.

Carga de documentos en Solr

vi index.sh

Pegamos el siguiente contenido

#!/bin/bash
file=$1
core=$2
serverIp="127.0.0.1"
port="8983"
echo "Indexando ${file} en el core: ${core}"
curl http://${serverIp}:${port}/solr/${core}/update -H "Content-Type: text/xml" --data-binary @${file}
curl "http://${serverIp}:${port}/solr/${core}/update?stream.body=%3Ccommit/%3E"
sh index.sh /root/index_ejemplo_20120311907.xml ejemplo

El script recibe como parámetros el fichero a indexar y el core en el que procesarlo.

La segunda llamada a curl hace efectivos los cambios, más abajo veremos el comando con detalle.

 

Una vez procesado el fichero Solr retorna en resultado mediante una respuesta XML.

<?xml version="1.0" encoding="UTF-8"?>
<response>
<lst name="responseHeader"><int name="status">0</int><int name="QTime">199</int></lst>
</response>
<?xml version="1.0" encoding="UTF-8"?>
<response>
<lst name="responseHeader"><int name="status">0</int><int name="QTime">78</int></lst>
</response>

En nuestro caso como hemos lanzado dos peticiones una para indexar y otra con el commit recibimos una respuesta por cada una.

Curiosamente, cuando el fichero a indexar genera un error. En vez de devolver un XML, retorna una página html con el error:

<html>
<head>
<title>Apache Tomcat/7.0.26 - Error report</title>
<style>
<!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style>
</head>
<body>
<h1>HTTP Status 400 - ERROR: [doc=1] unknown field 'lol'</h1>
<HR size="1" noshade="noshade">
<p><b>type</b> Status report</p><p><b>message</b> <u>ERROR: [doc=1] unknown field 'lol'</u></p><p><b>description</b> <u>The request sent by the client was syntactically incorrect (ERROR: [doc=1] unknown field 'lol').</u></p>
<HR size="1" noshade="noshade"><h3>Apache Tomcat/7.0.26</h3>
</body>
</html>

Lo que a simple vista no nos dice mucho teniendo en cuenta que lo recibimos en la línea de comandos.

Error visto en el navegador

Error visto en el navegador

El mensaje nos indica el tipo de error y el identificador único del documento que contiene el error. Cuando mandamos a indexar una tanda de documentos y recibimos un error, los documentos anteriores al error se procesan correctamente. Pero no procesa más documentos desde ese punto.

 

Llamada por GET

Un método alternativo, es hacer una llamada directa a la url de actualización de la misma manera que hemos hecho al llamar al commit con curl. Este método es adecuado para actualizaciones en tiempo real; y la creación de ficheros para actualización en tandas.

http://localhost:8983/solr/update?stream.body=%3Cadd%3E%3Cdoc%3E%3Cfield%20name%3D%22id%22%20%3E1%3C%2Ffield%3E%3Cfield%20name%3D%22nombre%22%3ECosas%20curiosas%3C%2Ffield%3E%3C%2Fdoc%3E%3C%2Fadd%3E

Hay que tener en cuenta que debemos codificar el XML con urlEncode para pasarlos en la URL.

 

Otros comandos

Commit

Como hemos visto en el script de indexado hay dos llamadas a curl. La primera vuelca el fichero de manera que Solr recibe los datos pero no ejecuta los cambios. La segunda hace un “commit” y los cambios se hacen efectivos inmediatamente.

Hay dos parámetros opcionales commit=true y commitWithin=8000 que respectivamente hacen un commit inmediato o forzando un commit como máximo en los milisegundos indicados.

curl http://localhost:8983/solr/ejemplo/update?commit=true -H "Content-Type: text/xml" --data-binary @${file}

 

Rollback

Descarta los cambios realizados desde el último commit.

curl "http://localhost:8983/solr/ejemplo/update?stream.body=%3Crollback/%3E"

 

Optimize

Lucene organiza los datos en segmentos, esto hace que durante las operaciones de indexado y borrado se genere fragmentación en el índice. El comando optimize consolida los datos en un solo segmento.

El proceso de optimización del índice es un proceso pesado, que debe leer todo el contenido y escribirlo de nuevo. Puede programarse como un proceso nocturno o lanzarse manualmente un <optimize/> tras un indexado completo.

curl "http://localhost:8983/solr/ejemplo/update?stream.body=%3Coptimize%2F%3E"

 

En próximas entradas veremos como utilizar la web de administración para crear y depurar consultas.

 

 

Deja un comentario

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.plugin cookies

?>