elasticsearch - what - searchkick settings
Elasticsearch, Tire y consultas/asociaciones anidadas con ActiveRecord (2)
Estoy usando ElasticSearch con Tire para indexar y buscar algunos modelos de ActiveRecord, y he estado buscando la forma "correcta" de indexar y buscar asociaciones. No he encontrado lo que parece ser una buena práctica para esto, así que quería preguntar si alguien tiene un enfoque que creen que funciona muy bien.
Como una configuración de ejemplo (esto está compuesto pero ilustra el problema), digamos que tenemos un libro, con capítulos. Cada libro tiene un título y autor, y un montón de capítulos. Cada capítulo tiene texto. Queremos indexar los campos del libro y el texto de los capítulos para que pueda buscar un libro por autor o para cualquier libro con ciertas palabras.
class Book < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
has_many :chapters
mapping do
indexes :title, :analyzer => ''snowball'', :boost => 100
indexes :author, :analyzer => ''snowball''
indexes :chapters, type: ''object'', properties: {
chapter_text: { type: ''string'', analyzer: ''snowball'' }
}
end
end
class Chapter < ActiveRecord::Base
belongs_to :book
end
Entonces hago la búsqueda con:
s = Book.search do
query { string query_string }
end
Eso no funciona, aunque parece que la indexación debería hacerlo. Si en cambio yo indico:
indexes :chapters, :as => ''chapters.map{|c| c.chapter_text}.join(''|''), :analyzer => ''snowball''
Eso hace que el texto se pueda buscar, pero obviamente no es un truco agradable y pierde el objeto asociado real. Probé variaciones de la búsqueda, como:
s = Book.search do
query do
boolean do
should { string query_string }
should { string "chapters.chapter_text:#{query_string}" }
end
end
end
Sin suerte allí, tampoco. Si alguien tiene un buen y claro ejemplo de indexación y búsqueda de objetos ActiveRecord asociados utilizando Tyre, parece que sería una muy buena adición a la base de conocimiento aquí.
Gracias por cualquier idea y contribución.
El soporte para las asociaciones ActiveRecord en Tyre está funcionando, pero requiere un par de ajustes dentro de su aplicación. No hay duda de que la biblioteca debería hacer un mejor trabajo aquí, y en el futuro ciertamente lo hará.
Dicho esto, aquí hay un ejemplo completo de configuración de neumáticos para trabajar con las asociaciones de Rails en active_record_associations.rb : active_record_associations.rb
Permítanme resaltar algunas cosas aquí.
Tocando al padre
Primero, debe asegurarse de notificar al modelo principal de la asociación sobre los cambios en la asociación.
Dado que tenemos un modelo de Chapter
, que "pertenece a" un Book
, tenemos que hacer:
class Chapter < ActiveRecord::Base
belongs_to :book, touch: true
end
De esta manera, cuando hacemos algo como:
book.chapters.create text: "Lorem ipsum...."
La instancia del book
recibe una notificación sobre el capítulo agregado.
Respondiendo a los toques
Con esta parte ordenada, debemos notificar a Tyre sobre el cambio y actualizar el índice elasticsearch en consecuencia:
class Book < ActiveRecord::Base
has_many :chapters
after_touch() { tire.update_index }
end
(No hay duda de que Tyre debería interceptar notificaciones after_touch
por sí mismo, y no obligarlo a hacerlo. Es, por otro lado, un testimonio de cuán fácil es trabajar en las limitaciones de la biblioteca de una manera que no perjudique su ojos)
Serialización correcta de JSON en Rails <3.1
A pesar de las menciones de README, debe deshabilitar la "adición automática de la clave raíz en JSON" en Rails <3.1. Muchas personas lo olvidan, por lo que también debe incluirlo en la definición de la clase:
self.include_root_in_json = false
Correcto mapeo para elasticsearch
Ahora viene la carne de nuestro trabajo: definir un mapeo adecuado para nuestros documentos (modelos):
mapping do
indexes :title, type: ''string'', boost: 10, analyzer: ''snowball''
indexes :created_at, type: ''date''
indexes :chapters do
indexes :text, analyzer: ''snowball''
end
end
Observe que created_at
title
con created_at
, created_at
como "fecha" y el texto del capítulo del modelo asociado. Todos los datos son efectivamente "des-normalizados" como un documento único en elasticsearch (si tal término tiene poco sentido).
Documentación adecuada de la serialización JSON
Como último paso, tenemos que serializar adecuadamente el documento en el índice elasticsearch. Observe cómo podemos aprovechar el conveniente método to_json
de ActiveRecord :
def to_indexed_json
to_json( include: { chapters: { only: [:text] } } )
end
Con toda esta configuración en su lugar, podemos buscar propiedades en las partes del Book
y del Chapter
de nuestro documento.
Ejecute el archivo active_record_associations.rb Ruby vinculado al principio para ver la imagen completa.
Para más información, consulte estos recursos:
- https://github.com/karmi/railscasts-episodes/commit/ee1f6f3
- https://github.com/karmi/railscasts-episodes/commit/03c45c3
- https://github.com/karmi/tire/blob/master/test/models/active_record_models.rb#L10-20
Consulte esta respuesta de : ElasticSearch & Tire: Using Mapping y to_indexed_json para obtener más información sobre el mapping
/ to_indexed_json
interplay.
Vea esta respuesta de : Indique los resultados de un método en ElasticSearch (Tire + ActiveRecord) para ver cómo combatir n + 1 consultas al indexar modelos con asociaciones.
He creado esto como una solución en una de mis aplicaciones, que indexa un conjunto de modelos profundamente anidados
https://gist.github.com/paulnsorensen/4744475
ACTUALIZACIÓN: ahora he lanzado una gema que hace esto: https://github.com/paulnsorensen/lifesaver