Saltar al contenido

Rieles de Elasticsearch con Ruby on Rails|

He proporcionado una aplicación de ejemplo usando Ruby on Rails, que se puede encontrar en mi GitHubitay-grudev/es-tuturial. Échale un vistazo y notarás que contiene una aplicación completa con búsqueda, resaltado de coincidencias y sugerencias de búsqueda basadas en el término sugerido.

La gema oficial de Elasticsearch para Rubí contiene las bibliotecas más importantes para usar Elasticsearch con Rubí. Pero usaremos el modelo de gema de elasticsearch, que está construido sobre la gema de elasticsearch y contiene todas las herramientas para hacer un modelo buscable.

Rieles de Elasticsearch con Ruby on Rails|
Rieles de Elasticsearch con Ruby on Rails|

Para empezar, asegúrate de añadir la gema a tu Gemfile:

1gem $0027elasticsearch-model$0027

rubí

Ajustes e indexación

Supongamos que tenemos un modelo llamado Artículo con dos columnas, título y cuerpo de tipos cadena y texto, correspondientemente.

Lo primero que tenemos que hacer es incluir la funcionalidad del modelo dentro de nuestro modelo. Además, el Elasticsearch::Model::Callbacks asegura que los índices de Elasticsearch se actualizan cuando se crea o actualiza un registro.

1234567classArticle<ActiveRecord::Base incluye Elasticsearch::Modelo incluye Elasticsearch::Modelo::Callbacks index_name Rails.application.class.parent_name.underscore document_type self.name.downcaseend

rubí

Observe las llamadas de nombre_índice y tipo_documento. (Prefiero tener un nombre_índice con el nombre de mi aplicación y el tipo de documento llamado después de mi modelo). Para configurar el tipo de documento necesitamos especificar a ES cómo queremos indexar nuestros datos.

123456settings index:{ number_of_shards:1}do mapping dynamic:falsedo indexes :title, analyzer:$0027english$0027 indexes :body, analyzer:$0027english$0027endend

rubí

Fíjate en el ajuste del número de fragmentos que estoy usando. Lo he puesto a 1 desde que sólo usaba una máquina y una cantidad muy pequeña de datos (< 100MB). Pero si tienes una cantidad muy alta de datos o quieres aumentar el rendimiento del ES, aumenta el número de fragmentos primarios. Dado que ambos campos contendrán texto escrito en inglés, estoy usando el analizador inglés. Elasticsearch tiene un amplio conjunto de Analizadores incluyendo Analizadores de Lenguaje. Dependiendo de los datos almacenados dentro de un campo, un analizador diferente puede dar mejores resultados. Por ejemplo, un analizador de palabras clave es útil para datos como códigos postales e identificaciones, entre otras cosas.

También se puede especificar el tipo de campo como tipos básicos, como un entero, que requiere métodos de indización mucho más sencillos. Así pues, suponiendo que se tiene un estado de campo, que sólo puede tomar los valores de [0, 1, 2, 3] indicando si el artículo es un borrador, privado, no listado o público, lo especificará así:

1indexes :status, tipo::byte

rubí

O en el caso de un ID de autor en una columna llamada ID_usuario, lo harás:

1indexes :user_id, type::integer

rubí

Elasticsearch también soporta tipos complejos como Arrays, Objetos o Anidados, que es un conjunto de objetos. Hay soporte para coordenadas geográficas y direcciones IP, entre otras cosas. Echa un vistazo al conjunto completo de tipos que Elasticsearch soporta al configurar tu índice.

Si tu modelo también tiene campos adicionales que no necesitas almacenar/indexar en ES, podrías ahorrar espacio anulando el método por defecto as_indexed_json y elegir qué campos se incluyen en la representación JSON de un registro que se envía a ES.

123defas_indexed_json(options =nil)self.as_json( only:[:title,:body])end

rubí

Además, si desea indexar un campo agregado o un campo virtual (uno que no esté almacenado en su base de datos), puede hacerlo:

123defas_indexed_json(options =nil)self.as_json( only:[:title,:body], methods::method_name)end

rubí

Aquí es donde :nombre_de_método es un símbolo o una matriz de símbolos que corresponden a los nombres de los métodos en su modelo. Es útil si, por ejemplo, el cuerpo de tu página se almacena en un formato como Markdown. Puedes tener un método que renderice Markdown como texto plano y luego enviar ese texto plano a Elasticsearch.

Tenga en cuenta que su documento puede tener más campos que los especificados en el bloque de configuración, pero no serán indexados.

Implementación del método self.search()

La última y más compleja adición a su modelo es el método de auto-búsqueda que generará solicitudes a Elasticsearch. Es el método más difícil de escribir, ya que necesitarás tener algún conocimiento del DSL de consultas de Elasticsearch.

Lo que estoy a punto de mostrarte es universal para la mayoría de los proyectos, pero si la funcionalidad que necesitas no está cubierta aquí, mira el Elasticsearch Query DSL.

Notará que las preguntas a ES están escritas en JSON. En Rubí, JSON se representa fácilmente con un Hash, así que usaremos un método que llamará al grupo de búsqueda elástica y le enviaremos nuestra consulta escrita como un Rubí Hash. Para ser breve, mostraré cómo se ve el método y describiré cada sección en un bloque de código separado.

12345678910111213defself.search(query) __elasticsearch__.search({ query:{ multi_match:{ query: query, fields:[$0027title^5$0027,$0027body$0027]}},# más bloques irán AQUÍ. ¡Sigue leyendo!
rubí

Generamos una consulta de Multi Match a ES con el término de búsqueda como primer argumento del método de la clase y especificamos en qué campos estamos ejecutando esta consulta. Además, estamos especificando el peso de las coincidencias dentro de cada campo. En el ejemplo, el campo del título es cinco veces más importante que el campo del cuerpo. Hay varios tipos de consultas de Multi Match, especificados por el parámetro type. Aquí están las posibles opciones:

Tipo ParámetroDescripciónbest_fields (por defecto) Encuentra documentos que coinciden con cualquier campo, pero utiliza el _puntuación del mejor campo.most_fieldsBusca documentos que coinciden con cualquier campo y combina el _puntuación de cada campo.cross_fieldsTrata los campos con el mismo analizador como si fueran un gran campo. Busca cada palabra en cualquier campo.phraseRunde una consulta de frase coincidente en cada campo y combina el _puntuación de cada campo.phrase_prefixRunde una consulta de frase coincidente en cada campo y combina el _puntuación de cada campo.

Para más detalles sobre cada opción ver Elasticsearch Multi Match Query.

La siguiente sección que añadiré es para resaltar las palabras clave coincidentes dentro del texto. Queremos hacer coincidir ambos campos, el título y el cuerpo, y encapsularlos en una etiqueta.

12345678highlight:{ pre_tags:[$0027&lt;mark&gt;$0027], post_tags:[$0027&lt;/mark&gt;$0027], fields:{ title:{}, body:{},}},

rubí

Lo siguiente que me gustaría establecer es el término sugerente, así que si el usuario escribe mal una determinada palabra clave, ES propondrá una recomendación para mejorar el término de la investigación.

123456789101112131415sugerir:{ texto: consulta, título:{ término:{ tamaño:1, campo::título}}, cuerpo:{ término:{ tamaño:1, campo::cuerpo}}

rubí

Esto permite sugerencias de términos de búsqueda basados tanto en el título como en los campos corporales con una distancia de golpeo de hasta 1 (puedes cambiar eso). Puedes leer más sobre el Sugeridor de términos de Elasticsearch o echar un vistazo a los otros Sugeridores de Elasticsearch. Por ejemplo, hay un Sugeridor de Terminación para la funcionalidad de autocompletar.

Volvamos al escenario en el que teníamos un campo de estatus de tipo entero que tomaba los valores de [0, 1, 2, 3] indicando si el artículo es un borrador, privado, no listado o público. Suponiendo que el status == 3 significa que un artículo es público, podríamos querer especificar un filtro en nuestra búsqueda para mostrar sólo los artículos públicos.

12345filtro:{ términos:{ estado:[4]}},

rubí

Y eso lo resume todo para el modelo. Ahora, vayamos a la visualización de los datos devueltos desde el ES.