KATADE

  • GENERAL
  • WEB
    • Elixir
    • Php
    • Ruby on Rails
  • MOBILE
    • Android
    • iOS
  • NOVEDADES
    • Actualidad
    • Dev Inside
    • Eventos
    • Top 5
  • CONOCE A…

Follow Us

KATADE

  • GENERAL
  • WEB
    • Elixir
    • Php
    • Ruby on Rails
  • MOBILE
    • Android
    • iOS
  • NOVEDADES
    • Actualidad
    • Dev Inside
    • Eventos
    • Top 5
  • CONOCE A…
Ruby on Rails, WEB,

Elasticsearch on Rails, motorizando búsquedas

by Víctor MacíasApril 7, 2016no comment
0
Shares
Share on FacebookShare on Twitter
mockup-865115_960_720

Antes de ponerte a integrar ElasticSearch con Rails como un loco, ¿estás seguro de que necesitas utilizarlo? Debes asegurarte de que es así. A veces, el problema que intentamos resolver con Elasticsearch se resuelve más rápidamente con algunas consultas avanzadas e indices. Nos equivocamos llegando a la solución por el camino más largo.

Por lo general, hay dos casos en los que el uso de Elasticsearch tiene sentido:

  1. Búsqueda de texto completo: Obvio, es para lo que Elasticsearch fue construido.
  2. Denormalización de datos complejos: Debido a que por lo general tratamos de normalizar nuestros datos para que coincidan con nuestros modelos, podemos encontrar algunos problemas de rendimiento cuando se consultan a través de estas tablas normalizadas. Elasticsearch nos permite consultar estos datos de manera más rápida.

Elasticsearch on Rails

A continuación vamos a describir un ejemplo de integración entre Elasticsearch y Rails:

Instalación

En el entorno de desarrollo, usando OS X, puedes instalar Elasticsearch mediante el comando:

➜ ~ brew install elasticsearch

Si utilizas otra plataforma o quieres instalar una version especifica, puedes seguir las indicaciones descritas por Elasticsearch.

Inicialización

Para inicializar Elasticsearch, junto con otros recursos, se puede generar una tarea rake (github) dentro del proyecto:

namespace :check do
  desc 'Check and start Mysql, Redis and Elasticsearch'
  task :environment do
    if RUBY_PLATFORM == 'x86_64-darwin14'
      puts 'This rake task will check if all dependencies are running, if not it will try to run. '

      if `pgrep mysql` == ''
        print 'Starting Mysql ...'
        `mysql.server start &`
        puts `pgrep mysql`.blank? ? ' [ KO ]' : ' [ OK ]'
      end

      if `ps aux | grep '[e]lasticsearch'` == ''
        print 'Starting Elasticsearch ...'
        `elasticsearch -d --path.conf=#{Rails.root}/config`
        puts `ps aux | grep '[e]lasticsearch'`.blank? ? ' [ KO ]' : ' [ OK ]'
      end

      if `pgrep redis` == ''
        print 'Starting Redis ...'
        `/usr/local/Cellar/redis/3.0.5/bin/redis-server /usr/local/etc/redis.conf &`
        puts `pgrep redis`.blank? ? ' [ KO ]' : ' [ OK ]'
      end

      if `pgrep sidekiq` == ''
        print 'Starting Sidekiq ...'
        `bundle exec sidekiq -d`
        puts `ps aux | grep '[s]idekiq'`.blank? ? ' [ KO ]' : ' [ OK ]'
      end
    end
  end
end

Esta tarea se puede lanzar mediante:

➜ ~ rake check:environment

Por último, para comprobar que Elasticsearch está funcionando correctamente, puedes lanzar:

➜ ~ curl -X GET http://localhost:9200/

Por defecto, Elasticsearch se inicializa en el puerto 9200 de la máquina local. Si se quisiera cambiar la configuración, ejecutando la tarea rake, cogería el fichero de configuración, elastisearch.yml (github), del directorio config del proyecto.

Gemfile

La instalación en el proyecto es muy simple, únicamente hay que añadir al gemfile (github):

# Gemfile
# ...
gem 'elasticsearch-model'
gem 'elasticsearch-rails'
gem 'sidekiq'

Puedes ampliar información de la utlización de las gemas aquí y sidekiq. Sidekiq lo utilizaremos para la indexación asíncrona de los datos.

Configuración

Una vez añadidas las gemas al gemfile, generamos el inicializer. Una posible configuración podría ser:

# config/initializers/elasticsearch.rb
Elasticsearch::Model.client = Elasticsearch::Client.new url: ENV['ELASTICSEARCH_URL'] || 'http://localhost:9200/'

Elasticsearch::Model::Response::Response.__send__ :include, Elasticsearch::Model::Response::Pagination::Kaminari

ActiveRecord::Base.establish_connection
ActiveRecord::Base.connection.tables.each do |table|
  next if table == 'schema_migrations'

  if table == 'users'
    if User.__elasticsearch__.index_exists?
      User.__elasticsearch__.refresh_index!
    else
      User.__elasticsearch__.create_index!
      User.import
    end
  end

end

Dentro de este ‘inicializer’, si tuviésemos definida la variable de entorno ELASTICSEARCH_URL, se conectaría a dicha url; en caso contrario, se conectará a la url definida por defecto. De igual manera, se inicializa manualmente la paginación con kaminari e indexamos las tabla que queramos. En concreto, en este ejemplo, al recorrer las tablas de nuestro modelo, si el índice existe, lo refrescamos; y, si no, lo creamos e importamos los datos que se tenga.

En el caso de que se desee mostrar el log de las peticiones que se realizan, basta con modificar la siguiente línea:

Elasticsearch::Model.client = Elasticsearch::Client.new log:true, url: ENV['ELASTICSEARCH_URL'] || 'http://localhost:9200/'

Indexación del Modelo

Para simplificar la indexación de un modelo e incluirle métodos genéricos de búsqueda, generamos un concern donde se incluyen los módulos necesarios de Elasticsearch y la definición de los métodos de búsqueda que se quieran tener por defecto.

Teniendo el concern definido, suponiendo que necesitemos indexar un modelo como puede ser User, que tiene atributos como first_name , last_name , useraname  y email , podemos definirlo de la siguiente forma:

class User < ActiveRecord::Base
...
  include Searchable

  after_save    { Indexer::User.perform_async(self.id, :index) }
  after_destroy { Indexer::User.perform_async(self.id, :delete) }

  index_name "users-#{Rails.env}"

## Se define el numero de shards y replicas del indice, junto con un analizador que utiliza filtros para ingles, español, acentos y opciones de autocompletado.
  settings index: { number_of_shards: 2, number_of_replicas: 2 },
           analysis: {
               analyzer: {
                   default: {
                       type: 'custom',
                       tokenizer: 'standard',
                       filter: %w(standard uppercase lowercase word_delimiter my_ascii_folding english_stemmer spanish_stemmer autocomplete_filter)
                   }
               },
               filter:{
                   my_ascii_folding: {
                       type: 'asciifolding',
                       preserve_original: true
                   },
                   english_stemmer: {
                       type: 'stemmer',
                       name: 'english'
                   },
                   spanish_stemmer: {
                       type: 'stemmer',
                       name: 'light_spanish'
                   },
                   autocomplete_filter: {
                       type:     'edge_ngram',
                       min_gram: 3,
                       max_gram: 20
                   }
               }
           } do
    mappings dynamic: false do
      indexes :first_name,  type: :string
      indexes :last_name,   type: :string
      indexes :username,    type: :completion, payloads: true
      indexes :email,       type: :string
    end
  end

  def as_indexed_json(options={})
    as_json(only: [:id, :first_name, :last_name, :username, :email])
  end
...
end

Además de la configuración del índice, la inclusión de analizadores y filtros, y el mapeo de atributos, se han añadido los callbacks after_save y after_destroy, que lanzan llamadas asíncronas para actualizar registros de Elasticsearch en el caso de que un registro de tu modelo en Rails se modifique y/o elimine.

Por otro lado, index_name “users-#{Rails.env}” permite asignar el nombre del índice según el entorno y así no mezclar datos cuando tengamos varias aplicaciones ejecutándose en distintos entornos.

Por ultimo, definiendo el método as_indexed_json , generamos el json del modelo que Elasticsearch recibirá.

Concluyendo, después del primer post donde introducimos los conceptos básicos de Elasticsearch, en este segundo hemos visto una aproximación entre Elasticsearch y Rails. Mostramos cómo integrar con Ruby on Rails un motor de búsqueda como es Elasticsearch; el cuál, a parte de ofrecernos la posibilidad de realizar búsquedas en los modelos de Rails, permite analizar dichos datos.

elasticsearchRoRruby
Previous

Mantle en iOS

April 5, 2016
Next

Prueba de la API escrita en Ruby con Fixtures y Minitest

April 12, 2016

Leave a Reply Cancel reply

WEB

  • Tipos de conexiones a bases de datos MYSQL desde PHP

    Tipos de conexiones a bases de datos MYSQL desde PHP

    October 31, 2016
  • ActiveModel Serializer, una alternativa a JBuilder

    ActiveModel Serializer, una alternativa a JBuilder

    August 9, 2016
  • Elixir & Phoenix, el nuevo Lamborghini de los lenguajes

    Elixir & Phoenix, el nuevo Lamborghini de los lenguajes

    July 19, 2016
  • ActionCable en Rails 5: Web en tiempo real

    ActionCable en Rails 5: Web en tiempo real

    June 21, 2016
  • Migración en MySQL y Rails sin 'downtime'

    Migración en MySQL y Rails sin 'downtime'

    May 27, 2016

iOS

  • Aprendiendo Swift – Opcionales

    Aprendiendo Swift – Opcionales

    9 months ago
  • Aprendiendo Swift – Controles de flujo

    Aprendiendo Swift – Controles de flujo

    9 months ago
  • Aprendiendo Swift – Operadores

    Aprendiendo Swift – Operadores

    9 months ago

ANDROID

  • Desarrollo Kotlin IV: edición frontend, interactuando con layouts

    Desarrollo Kotlin IV: edición frontend, interactuando con layouts

    9 months ago
  • Desarrollo Kotlin (III): edición componentes generales del lenguaje

    Desarrollo Kotlin (III): edición componentes generales del lenguaje

    9 months ago
  • Desarrollo Kotlin (II): edición ¡Hola Mundo!

    Desarrollo Kotlin (II): edición ¡Hola Mundo!

    9 months ago

GENERAL

  • Las tendencias del mercado que impulsan el interés sobre la ciencia de los datos

    Las tendencias del mercado que impulsan el interés sobre la ciencia de los datos

    6 months ago
  • ¿Cómo aumentar el tráfico hacia tu web con SEO Local?

    ¿Cómo aumentar el tráfico hacia tu web con SEO Local?

    9 months ago
  • Urls amigables: ¿Por qué y cómo usarlas?

    Urls amigables: ¿Por qué y cómo usarlas?

    9 months ago

NOVEDADES

  • Nace ToroDB, la primera base de datos 100% española

    Nace ToroDB, la primera base de datos 100% española

    December 5, 2016
  • Codemotion 2016, el mayor evento nacional sobre desarrollo

    Codemotion 2016, el mayor evento nacional sobre desarrollo

    November 28, 2016
  • Codemotion Spain 2016, un reencuentro entre comunidades

    Codemotion Spain 2016, un reencuentro entre comunidades

    November 10, 2016
  • PyConEs 2016: pitonisas en Almería

    PyConEs 2016: pitonisas en Almería

    November 4, 2016

CONOCE A…

  • GDG Madrid apuesta por España a nivel tecnológico

    GDG Madrid apuesta por España a nivel tecnológico

    December 26, 2016
  • José Luis Esteban: "Aprender Haskell era como empezar de cero"

    José Luis Esteban: "Aprender Haskell era como empezar de cero"

    October 27, 2016
  • Daniel Latorre: "Soy más feliz trabajando por mi cuenta"

    Daniel Latorre: "Soy más feliz trabajando por mi cuenta"

    October 26, 2016

© 2017 Katade. All rights reserved.