elasticsearch rails searching models - ruby-on-rails

I am using elasticsearch-rails and elasticsearch-model gem for searching products in my rails app.
I want to make my search case insensitive and must be independent of pluralization. I researched a lot on google but got a hunch on how to do it using analyzer but not success. So I had to post a new question.
Here is my product model where I want to search to take place
class Product < ActiveRecord::Base
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
belongs_to :category
belongs_to :sub_category
has_many :variations
settings index: { number_of_shards: 1 } do
mappings dynamic: 'false' do
indexes :name, analyzer: 'snowball'
indexes :description, analyzer: 'snowball'
indexes :category, analyzer: 'snowball'
end
end
def as_indexed_json(options={})
as_json(only: [:name, :description, :brand, :color, :make, :style, :gender],
include: { category: { only: :name}, sub_category: {only: :name}}
)
end
def self.search(query)
__elasticsearch__.search({
query: { query_string: {
query: query,
default_operator: "AND"
}},
})
end
end
My questions are how do I do search irrespective of pluralization?
tshirt, T-shirt, Tshirts should all match.
Above all I never found any explanation of the following block given below
settings index: { number_of_shards: 1 } do
mappings dynamic: 'false' do
indexes :name, analyzer: 'snowball', index_options: 'offsets'
indexes :description, analyzer: 'snowball'
indexes :category, analyzer: 'snowball'
end
end
what is the exact purpose of this block for searching.
what does mappings dynamic: 'false' means?
what does indexes :name, analyzer: 'snowball', index_options: 'offsets'
means?
What are the other options that could be added in this block with a little bit of explanation would be of great help for me.
Any links for further research is also helpful.
Thanks

Related

Rails/Tire – Prevent attribute from being indexed

I'm trying to create a custom mapping using Rails (3.2.8), Tire, and ElasticSearch.
I'd like "title" and "description" to be the only searchable/indexed attributes... but, I just can't seem to make this work:
Below is my model:
class BlogPost < ActiveRecord::Base
attr_accessible :title, :description, :attachment, :attachment_thumbnail
include Tire::Model::Search
include Tire::Model::Callbacks
tire.mapping do
indexes :id, :type => 'integer', :index => :not_analyzed, :include_in_all => false
indexes :attachment, :type => 'string', :index => :not_analyzed, :include_in_all => false
indexes :attachment_thumbnail, :type => 'string', :index => :not_analyzed, :include_in_all => false
indexes :title, analyzer: 'snowball', boost: 100
indexes :description, analyzer: 'snowball', boost: 100
end
def self.search(params)
tire.search(load: true, page: params[:page], per_page: 15) do
query do
boolean do
must { string params[:query], default_operator: "AND" } if params[:query].present?
end
end
end
end
end
You need to set index to no:
indexes :id, :type => 'integer', :index => :no, :include_in_all => false
indexes :attachment, :type => 'string', :index => :no, :include_in_all => false
indexes :attachment_thumbnail, :type => 'string', :index => :no, :include_in_all => false
indexes :title, analyzer: 'snowball', boost: 100
indexes :description, analyzer: 'snowball', boost: 100
Using not_analysed will only prevent the field being broken down into tokens - it will still be included in the index.
See core types for more info.
If you change the mapping, you should reindex entire data again. If it is a test data remove the indices. Create the indices and index the data again. I guess you tried to add :include_in_all => false after indexing the data.

Filter issue with tire elasticsearch

Want to search for the title from the board with live_flag true.
class Board < ActiveRecord::Base
has_many :deals
include Tire::Model::Search
tire.mapping do
indexes :title, type: 'string'
indexes :deals do
indexes :title, type: 'string'
end
end
def to_indexed_json
{
:title => title,
:deals => {:title => self.deals.map(&:title)},
}.to_json
end
def self.search(params)
tire.search(load: true) do
query {string params[:query]+"*"}
filter :term, live_flag: true
sort { by :created_at, "desc" } if params[:query].blank?
end
end
end
Now it will not search anything.
It works properly when below code is used. Difference is that i have removed filter text from it.
def self.search(params)
tire.search(load: true) do
query {string params[:query]+"*"}
sort { by :created_at, "desc" } if params[:query].blank?
end
end
**But i want to get boards whose live_flag is true.**
Right now your index does not include the live_flag
just add live_flag to your to_indexed_json and mapping
tire.mapping do
indexes :title, type: 'string'
indexes :live_flag, type: 'boolean'
indexes :deals do
indexes :title, type: 'string'
end
end
def to_indexed_json
{
:title => title,
:live_flag => live_flag,
:deals => {:title => self.deals.map(&:title)},
}.to_json
end

Rails elasticsearch tire nested mapping

I'm trying to index nested tags to my product model.
The products indexes well but not the nested tags associated to the product.
How can i do? is my mapping correct?
Product Class
include Tire::Model::Search
include Tire::Model::Callbacks
mapping do
indexes :id, type: 'integer', index: :not_analyzed
indexes :name, type: 'string', analyzer: 'snowball', boost: 100
indexes :description, analyzer: 'snowball'
indexes :price, type: 'float'
indexes :category, type: 'string'
indexes :location, type: 'string'
indexes :online, type: 'boolean'
indexes :created_at, type: 'date', index: :not_analyzed
indexes :updated_at, type: 'date', index: :not_analyzed
indexes :tags do
indexes :id, type: 'integer', index: :not_analyzed
indexes :name, type: 'string', analyzer: 'snowball', boost: 100
end
end
def to_indexed_json
{
id: id,
name: name,
description: description,
price: price,
category: category,
location: location,
online: online,
created_at: created_at,
updated_at: updated_at,
include: { tags: { only: [:name] } }
}.to_json
end
Thanks!
ok, i have found the answer:
def to_indexed_json
{
name: name,
description: description,
price: price,
category: category,
location: location,
online: online,
created_at: created_at,
updated_at: updated_at,
tags: tags
}.to_json
end
And no need to include id, updated_at and created_at to the mapping because it's automatically indexed. Thanks Tire!

Rails ElasticSearch Tire - filter nested field on multiple parameters the same time

I have a Movie model and a search page that has a movie genres facet.
It's possible to select a checkbox near every facet on the search page.
I pass the list of checked facet terms to a controller and I want to filter movies collection to include only those movies that have genres selected with checkboxes.
My model with indexes and search definition is:
class Movie < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
mapping do
indexes :id, :index => :not_analyzed
indexes :title, :analyzer => 'snowball', :boost => 100
indexes :description, :analyzer => 'snowball'
indexes :genres do
indexes :title, analyzer: 'keyword'
end
end
def to_indexed_json
to_json( include: { genres: { only: [:title, :id] } } )
end
def self.search(params={})
tire.search(page: params[:page], per_page: 5, load: false) do
query do
all
#boolean do
# must { string params[:query], :default_field => 'title' }
#end
end
#filter 'genres.title', :values => params[:genres] if params[:genres].present?
filter :terms, 'genres.title' => ['Genre 1', 'Genre 2', 'Genre 3']
facet 'global-genres', global: true do
terms 'genres.title', size: 15
end
facet 'scoped-genres' do
terms 'genres.title', size: 15
end
end
end
attr_accessible :description, :title, :year, :genres
has_and_belongs_to_many :genres, :uniq => true
end
I'm not sure how this part should be rewritten:
#filter 'genres.title', :values => params[:genres] if params[:genres].present?
filter :terms, 'genres.title' => ['Genre 1', 'Genre 2', 'Genre 3']
#filter 'genres.id', :values => [1, 2, 3]
Later, I'm going to pass the list of genres or genre IDs as parameters, and I need to filter on them.
filter :terms, 'genres.title' => ['Genre 1', 'Genre 2', 'Genre 3']
It filters to movies that have 'Genre 1' OR 'Genre 2' OR 'Genre 3'
I need those genres to have AND logic.
How can I do this properly?
You need to set the filter with execution: 'and'. I've added this as an example to Tire integration test suite:
s = Tire.search('articles-test') do
query { all }
filter :terms, :tags => ['ruby', 'python'], :execution => 'and'
end

Tire not seeing singular/plural?

From everything I have read this should work. I'm re-indexing after each change.
My category names are stored as plural, e.g. "books", "movies" or "tapes". In rails terms this is the same as #resource.category.name
It works if i search books but does not work if i search book. I'm trying to make it so you can search either singular or plural and find a result for the category
# Tire
include Tire::Model::Search
include Tire::Model::Callbacks
mapping do
indexes :url
indexes :title, :boost => 3
indexes :description, :boost => 2
indexes :categories do
indexes :name, analyzer: 'snowball', :boost => 1.5, store: 'true'
end
indexes :user do
indexes :username, analyzer: 'keyword'
end
end
def self.elasticsearch(params)
tire.search(
:load => { :include => [:tags] },
:page => params[:page],
:per_page => 20) do
query { string params[:e], default_operator: "OR" } # if params[:e].present?
end
end
def to_indexed_json
to_json( include: { user: { only: [:username] },
category: { only: [:name] } } )
end

Resources