Cannon Moyer
Cannon Moyer

Reputation: 3164

Search Associated Models Elastic Search - Rails

I've been looking online for quite some time for a solution, but I cannot seem to come up with anything that works. I am building a product search for a site with three ActiveRecord Models. The associations go as follows:

class NormalBrand < ApplicationRecord
    has_many :normal_models
end

class NormalModel < ApplicationRecord
    belongs_to :normal_brand
    has_many :products
end

class Product < ApplicationRecord
    belongs_to :normal_model
end

I've successfully integrated ElasticSearch into my rails app but I cannot seem to get the search to have a scope of multiple models. Currently when I search, the only fields that are searched are the columns in the Product class. I want the columns in the NormalBrand and NormalModels to be searched as well. Below is my current code for the Product class. I've read through the example on github that uses Tire but I cannot get that to work (https://github.com/elastic/elasticsearch-rails/blob/master/elasticsearch-model/examples/activerecord_associations.rb).

require 'elasticsearch/model'
class Product < ApplicationRecord
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks

#associations
belongs_to :category_model
belongs_to :normal_model
has_many :order_items
has_many :quote_items

    #validations
validates :name, presence: true
validates :part_number, presence: true

validates :description, presence: true
validates :sku, presence: true
validates :sku, uniqueness: true

validates :price, presence: true
validates :price, numericality: { greater_than_or_equal_to: 0.0 }

validates :weight, presence: true
validates :weight, numericality: { greater_than_or_equal_to: 0.0 }
validates :url_key, presence: true
validates :url_key, uniqueness: true


#this is an overridden method.
def self.search(query)
  __elasticsearch__.search(
    {
      query: {
        multi_match: {
          query: query,
          fields: ['name^10', 'text']
        }
      }
    }
  )
end

settings index: { number_of_shards: 1 } do
  mappings dynamic: 'false' do
    indexes :name, analyzer: 'english'
    indexes :text, analyzer: 'english'
  end
end

 def as_indexed_json(options={})
    as_json(

      include: { 
           normal_model: { methods: [:name], only: [:name] }
         }
    )
 end


#def to_param
 # url_key
#end
end

Product.import force: true

The first time the Product.search function is called in my controller

Processing by SearchController#search as HTML
Parameters: {"q"=>"PS300"}
Product Load (0.8ms)  SELECT  "products".* FROM "products" ORDER BY 
"products"."id" ASC LIMIT $1  [["LIMIT", 1000]]
NormalModel Load (0.4ms)  SELECT  "normal_models".* FROM "normal_models" 
WHERE "normal_models"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
CACHE (0.0ms)  SELECT  "normal_models".* FROM "normal_models" WHERE 
"normal_models"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
Rendering search/search.html.erb within layouts/application
Rendered search/search.html.erb within layouts/application (11.6ms)
Rendered layouts/_header.html.erb (4.3ms)
Rendered layouts/_flash_messages.html.erb (0.7ms)
Rendered layouts/_footer.html.erb (0.3ms)
Completed 200 OK in 4797ms (Views: 4544.7ms | ActiveRecord: 8.7ms)

The second time I attempt to search.

Started GET "/catalogsearch/result/?q=Nordictrack" for 50.255.94.246 at 2017-05-02 13:46:47 +0000
Cannot render console from 50.255.94.246! Allowed networks: 127.0.0.1, ::1, 
127.0.0.0/127.255.255.255
Processing by SearchController#search as HTML
Parameters: {"q"=>"Nordictrack"}
Rendering search/search.html.erb within layouts/application
Rendered search/search.html.erb within layouts/application (176.3ms)
Rendered layouts/_header.html.erb (1.3ms)
Rendered layouts/_flash_messages.html.erb (0.6ms)
Rendered layouts/_footer.html.erb (0.4ms)
Completed 200 OK in 239ms (Views: 237.2ms | ActiveRecord: 0.0ms)

My search controller

class SearchController < ApplicationController
  def search
    if params[:q].nil?
      @products = []
    else
      @products = Product.search params[:q]
    end
  end
end

Upvotes: 2

Views: 788

Answers (1)

Vitaly Perminov
Vitaly Perminov

Reputation: 1

in this case:

def as_indexed_json(options={})
    as_json(
      include: { 
           normal_model: { methods: [:name], only: [:name] }
         }
    )
 end

you should add nested indexes for associated model:

settings index: { number_of_shards: 1 } do
  mappings dynamic: 'false' do
    indexes :name, analyzer: 'english'
    indexes :text, analyzer: 'english'
    indexes :normal_model, type: 'nested' do
      indexes :name, analyzer: 'english'
    end
  end
end

and change your query, like this:

def self.search(query)
  __elasticsearch__.search(
    {
      query: {
        multi_match: {
          query: query,
          fields: ['normal_model.name^10', 'name^10', 'text']
        }
      }
    }
  )
end

Upvotes: 0

Related Questions