Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Ruby on Rails

Búsqueda Full-Text en Rails usando Elasticsearch

by
Length:MediumLanguages:

Spanish (Español) translation by Steven (you can also view the original English article)

En este artículo, te mostraré cómo implementar la búsqueda Full-Text (de texto completo) utilizando Ruby on Rails y Elasticsearch. Hoy en día, todo el mundo ingresa un término de búsqueda y obtiene sugerencias y resultados con el término de búsqueda resaltado. Si escribes mal lo que estás intentando buscar, tener una autocorrección también es una buena característica, como podemos ver en sitios web como Google o Facebook.

Implementar todas estas funciones utilizando solo una base de datos relacional como MySQL o Postgres no es sencillo. Por este motivo, estamos utilizando Elasticsearch, que puede considerarse como una base de datos específicamente creada y optimizada para la búsqueda. Es de código abierto y está desarrollado sobre Apache Lucene.

Una de las mejores características de Elasticsearch es que expone su funcionalidad mediante la API REST, por lo que hay bibliotecas que integran esa funcionalidad para la mayoría de los lenguajes de programación.

Introducción a Elasticsearch

Anteriormente, mencioné que Elasticsearch es como una base de datos para la búsqueda. Sería útil si estás familiarizado con algo de la terminología que lo rodea.

  • Field: Un campo es como un par key-value. El valor puede ser un valor simple (string, integer, date) o una estructura anidada como una matriz o un objeto. Un campo es similar a una columna en una tabla en una base de datos relacional.
  • Document: Un documento es una lista de campos. Es un documento JSON que se almacena en Elasticsearch. Es como una fila en una tabla en una base de datos relacional. Cada documento se almacena en un índice y tiene un tipo y una identificación única.
  • Type: Un tipo es como una tabla en una base de datos relacional. Cada tipo tiene una lista de campos que se pueden especificar para documentos de ese tipo.
  • Index: Un índice es el equivalente de una base de datos relacional. Contiene la definición para múltiples tipos y almacena múltiples documentos.

Una cosa a tener en cuenta aquí es que en Elasticsearch, cuando escribes un documento en un índice, los campos del documento se analizan, palabra por palabra, para que la búsqueda sea fácil y rápida. Elasticsearch también admite la geolocalización, por lo que puedes buscar documentos que se encuentran dentro de una cierta distancia de una ubicación determinada. Así es exactamente como Foursquare implementa la búsqueda.

Me gustaría mencionar que Elasticsearch se creó teniendo en cuenta una gran escalabilidad, por lo que es muy fácil crear un clúster con varios servidores y tener una alta disponibilidad, incluso si algunos servidores se caen. No voy a cubrir los detalles de cómo planificar y desplegar diferentes tipos de clústeres en este artículo.

Instalando Elasticsearch

Si estás utilizando Linux, posiblemente puedas instalar Elasticsearch desde uno de los repositorios. Está disponible en APT y YUM.

Si usas Mac, puedes instalarlo usando Homebrew: brew install elasticsearch. Después de instalar elasticsearch, verás la lista de carpetas relevantes en su terminal:

Elasticsearch folders

Para verificar que la instalación está funcionando, escribe elasticsearch en tu terminal para iniciarla. Luego ejecuta curl localhost:9200 en tu terminal, y deberías ver algo como:

Elasticsearch running

Instalar Elastic HQ

Elastic HQ es un complemento de monitoreo que podemos usar para administrar Elasticsearch desde el navegador, similar a phpMyAdmin para MySQL. Para instalarlo, simplemente ejecuta en tu terminal:

/usr/local/Cellar/elasticsearch/2.2.0_1/libexec/bin/plugin -install royrusso/elasticsearch-HQ

Una vez instalado, ingresa a http://localhost:9200/_plugin/hq en tu navegador:

Elastic HQ Plugin

Haz clic en Conectar y verás una pantalla que muestra el estado del clúster:

Elastic HQ Cluster Overview

En este momento, como es de esperar, aún no se han creado índices o documentos, pero tenemos nuestra instancia local de Elasticsearch instalada y en ejecución.

Creando una aplicación Rails

Voy a crear una aplicación Rails muy simple, donde puedes agregar artículos a la base de datos para que podamos realizar una búsqueda Full-Text en ellos usando Elasticsearch. Comienza creando una nueva aplicación de Rails:

rails new elasticsearch-rails

A continuación generamos un nuevo recurso de artículo con Scaffolding:

rails generate scaffold Article title:string text:text

Ahora necesitamos agregar una nueva ruta raíz, para que podamos ver por defecto la lista de artículos. Edita config/routes.rb:

Crea la base de datos ejecutando el comando rake db:migrate. Si inicias rails server, abre el navegador, ingresa a localhost:3000 y agrega algunos artículos a la base de datos, o simplemente descarga el archivo db/seeds.rb con datos ficticios que he creado para que no tengas que gastar mucho tiempo llenando formularios.

Añadiendo Búsqueda

Ahora que tenemos nuestra pequeña aplicación Rails con artículos en la base de datos, estamos listos para agregar nuestra funcionalidad de búsqueda. Vamos a comenzar agregando la referencia a ambas gemas oficiales de Elasticsearch:

En muchos sitios web, es muy común tener un cuadro de texto para buscar en el menú superior en todas las páginas. Por esa razón, voy a crear un formulario parcial en app/views/search/_form.html.erb. Como puedes ver, estoy enviando el formulario con GET, por lo que es fácil copiar y pegar la URL para una búsqueda específica.

Agrega una referencia al formulario al diseño del sitio web principal. Edita app/views/layouts/application.html.erb.

Ahora también necesitamos un controlador para realizar la búsqueda real y mostrar los resultados, por lo que lo generamos ejecutando rails g new controller Search

Como puedes ver, estoy llamando al método search en el modelo Article. Aún no lo hemos definido, así que si intentamos realizar una búsqueda en este punto, obtenemos un error. Además, no hemos agregado una ruta para SearchController en el archivo config/route.rb, así que hagámoslo:

Si nos fijamos en la documentación de la gema 'elasticsearch-rails', debemos incluir dos módulos en los modelos que queremos indexar en Elasticsearch, en nuestro caso Article.rb.

El primer modelo inyecta el método Search que estábamos usando en nuestro controlador anterior entre otros. El segundo módulo se integra con las devoluciones de llamada de ActiveRecord para indexar cada instancia de un artículo que guardamos en la base de datos, y también actualiza el índice si modificamos o eliminamos el artículo de la base de datos. Así que todo es transparente para nosotros.

Si importaste los datos a la base de datos anteriormente, esos artículos aún no están en el índice de Elasticsearch; solo los nuevos son indexados automáticamente. Por esta razón, tenemos que indexarlos manualmente, y es fácil si iniciamos rails console. Por lo tanto, solo tenemos que ejecutar irb(main) > Article.import

Articleimport

Ahora estamos listos para probar la funcionalidad de búsqueda. Si escribo 'ruby' y hago clic en buscar, aquí están los resultados:

Search Result

Resaltado de búsqueda

En muchos sitios web, puedes ver en la página de resultados de búsqueda cómo se resalta el término que buscaste. Esto es muy fácil de hacer usando Elasticsearch.

Edita app/models/article.rb y modifica el método de búsqueda predeterminado:

De forma predeterminada, el método search está definido por la gema 'elasticsearch-models', y el objeto proxy __elasticsearch__ se proporciona para acceder a la clase wrapper para la API de Elasticsearch. Por lo tanto, podemos modificar la consulta predeterminada utilizando las opciones JSON estándar según lo proporciona la documentación.

Ahora el método search ajustará los resultados que coincidan con la consulta con las etiquetas HTML especificadas. Por este motivo, también debemos actualizar la página de resultados de búsqueda para poder representar las etiquetas HTML de forma segura. Para hacerlo, edita app/views/search/search.html.erb.

Agrega un estilo CSS a app/assets/stylesheets/search.scss, para la etiqueta resaltada:

Intenta buscar 'ruby' otra vez:

Search result

Como puedes ver, es fácil resaltar el término de búsqueda, pero no es lo ideal, ya que necesitamos enviar una consulta JSON como lo especifica la documentación de Elasticsearch, y no tenemos ningún tipo de abstracción.

Gema Searchkick 

La gema Searchkick es proporcionada por Instacart, y es una abstracción sobre las gemas oficiales de Elasticsearch. Voy a refactorizar la funcionalidad de resaltado, así que comenzamos agregando gem 'searchkick' al archivo gem. La primera clase que necesitamos cambiar es el modelo Article.rb:

Como puedes ver, es mucho más simple. Necesitamos reindexar de nuevo los artículos y ejecutar el comando rake searchkick:reindex CLASS=Article. Para resaltar el término de búsqueda, debemos pasar un parámetro adicional al método de búsqueda desde nuestro search_controller.rb.

El último archivo que debemos modificar es views/search/search.html.erb, ya que los resultados se devuelven en un formato diferente por searchkick ahora:

Ahora es el momento de volver a ejecutar la aplicación y probar la funcionalidad de búsqueda:

Search result

Nota que ingresé como un término de búsqueda 'dato'. Hice esto a propósito para mostrarte que, por defecto, Searchkick está configurado para analizar el texto indexado y ser más permisivo con las faltas de ortografía.

Sugerencias automáticas

La sugerencia automática predice lo que un usuario escribirá, lo que hace que la experiencia de búsqueda sea más rápida y fácil. Ten en cuenta que a menos que tengas miles de registros, podría ser mejor filtrar en el lado del cliente.

Comencemos agregando el complemento typeahead, que está disponible a través de gem 'bootstrap-typeahead-rails', y agrégalo a tu archivo Gemfile. A continuación, debemos agregar algo de JavaScript a app/assets/javascripts/application.js para que al comenzar a escribir en el cuadro de búsqueda, aparezcan algunas sugerencias.

Algunos comentarios sobre el fragmento anterior. En las dos últimas líneas, debido a que no he deshabilitado los turbolinks, esa es la forma de conectar el código que deseo ejecutar en la carga de la página. En la primera parte de la secuencia de comandos, puedes ver que estoy usando Bloodhound. Es el motor de sugerencias typeahead.js, y también estoy configurando el punto final JSON para que las solicitudes AJAX obtengan las sugerencias. Después de eso, llamo a initialize() en el motor, y configuro typeahead en el campo de texto de búsqueda usando su id "término".

Ahora, tenemos que hacer la implementación de back-end para las sugerencias, comencemos agregando la ruta, edita app/config/routes.rb.

A continuación, voy a agregar la implementación en app/controllers/search_controller.rb.

Este método está devolviendo los resultados de búsqueda para el término ingresado usando JSON. Solo busco por título, pero también puedo especificar el contenido del artículo. También estoy limitando el número de resultados de búsqueda a máximo 10.

Ahora estamos listos para probar la implementación de typeahead:

Typeahead

Conclusión

Como puedes ver, el uso de Elasticsearch con Rails hace que la búsqueda de nuestros datos sea realmente fácil y muy rápida. Aquí te mostré cómo usar las gemas de bajo nivel proporcionadas por Elasticsearch, así como la gema Searchkick, que es una abstracción que oculta algunos de los detalles de cómo funciona Elasticsearch.

Dependiendo de tus necesidades específicas, es posible que estés contento de usar Searchkick y de que tu búsqueda Full-Text se implemente rápida y fácilmente. Por otro lado, si tienes otras consultas complejas, incluidos filtros o grupos, es posible que debas conocer más detalles sobre el lenguaje de consulta en Elasticsearch y terminar usando las gemas de nivel inferior 'elasticsearch-models' y 'elasticsearch-rails'.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.