miércoles, 19 de febrero de 2014

Integrar elasticsearch en una aplicación Rails con Mongoid

Realizar una búsqueda sobre campos de texto siempre es costosa. En el caso de que esta búsqueda se haga sobre una gran cantidad de registros, la cosa se empeora.

Si en nuestra aplicación Rails estamos usando una base de datos MongoDB con Mongoid, la primera opción y más sencilla es usar la gema 'mongoid_search' o similar. Esta gema crea un campo _keywords para cada documento donde mete un array de palabras clave, que es sobre el que va a realizar la búsqueda.




Cuando el número de documentos sobre el que trabajamos aumenta y empezamos a hablar de millones, conviene pasar a un motor de búsqueda como tal. En mi caso he barajado tres posibilidades:
Sphinx, Solr y elasticsearch. A priori no tenía ninguna preferencia, ya que hasta ahora sólo había trabajado con tecnologías de este tipo a modo de consumidor.
Tras buscar en distintos foros, llegué a la conclusión de que, a pesar de ser la más reciente (menos madura), el motor de elasticsearch parece que ofrece lo mismo que los otros dos y además cubre otros aspectos pendientes.

Lo que me interesaba principalmente era una sencilla integración con Rails y MongoDB, ya que no me era necesario realizar búsquedas trabajosas, ni tengo estructuras demasiado complejas.
Para realizar esta integración, lo primero es tener instalado elasticsearch. Para OSX:

$ brew install elasticsearch

Una vez que lo tenemos instalado, pasaremos a integrar elasticsearch en la aplicación. Esto lo he realizado con la gema searchkick. La incluimos en el Gemfile:

gem "searchkick"

Añadimos la línea searchkick dentro del modelo (Product) y desde la consola de rails ejecutamos: <<Product.reindex>> o bien el comando << rake searchkick:reindex CLASS=Products>> desde la Termina.

Ahora añadimos un formulario de búsqueda en nuestra aplicación, e implementamos la acción search para realizar la búsqueda:
results = Product.search "garbanzo", fields: [:name, :brand], limit: 10, offset: 50
Al hacer map sobre results, obtendremos los datos buscados:

results.map(&:name)

Y de esta forma tenemos implementado un buscador simple sobre un conjunto grande de documentos de MongoDB. La documentación, tanto de ES como de la gema, muestra ejemplos y documentación de muchas más funciones y posibilidades (términos sugeridos, autocompletar, ...), por lo que recomiendo que le echéis un vistazo.