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
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.
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
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!
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
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