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.
Related
Calling all elasticsearch experts:
Environment:
elasticsearch (6.8.3)
rails 5.1.7
Linux
We changed our association from our user model to the person model from a has_one to a has_many. We did not however remove the person_id column as this can cause other issues (e.g. running code from another branch that still tries to use it)
It seems no matter what we do to the index definitions for ElasticSearch, if any null values exist in person_id (which they do now with all newly added records) we get
"No value specified for terms query"
I have confirmed this by populating that column with an integer value resulting in no error occurring.
I have removed person_id from any indexes I even removed any reference to the peoples table in the ES index declaration but it appears that as soon as ES sees there is an association to a Person model and it sees the person_id column, it tries to use it.
Besides writing an arbitrary value in the person_id column, does anyone else have a better approach? We DO NEED to search fields in the associated Person model (peoples table) but since it's a has_many, it shouldn't be using the person_id column which is no longer relevant.
We also considered reversing the search from Person to User but that would require a LOT of recoding.
Is it possible a newer version of ES will fix it? We'd probably have to upgrade the ES server as well if we update the gem right?
Here is the association:
has_many :people, :class_name => Person.to_s # I believe it's this class name that's causting it to try to use the person_id column
Here is the index definition
def as_indexed_json(_options = {})
as_json(
:only => %i[id email portal_id],
:include => {
:portal => { :only => %i[name slug] },
:people => { :methods => %i[first last full_name], :only => %i[first last full_name] },
:events => { :only => [:id] },
:account_brokerage_firms => { :only => [:id] },
:brokerage_firms => { :only => [:id] },
:roles => { :methods => [:role_name], :only => [:role_name] }
}
)
end
settings AUTOCOMPLETE_SETTINGS do
mapping :dynamic => 'false' do
indexes :id, :type => 'long'
indexes :email, :type => 'text', :analyzer => 'email'
indexes :portal_id, :type => 'long'
indexes :people do
indexes :first, :type => 'text', :analyzer => 'autocomplete'
indexes :last, :type => 'text', :analyzer => 'autocomplete'
indexes :full_name, :type => 'text', :analyzer => 'autocomplete'
end
indexes :portal do
indexes :name, :type => 'text', :analyzer => 'autocomplete'
indexes :slug, :type => 'text', :analyzer => 'autocomplete'
end
indexes :events do
indexes :id, :type => 'long'
end
indexes :account_brokerage_firms do
indexes :id, :type => 'long'
end
indexes :brokerage_firms do
indexes :id, :type => 'long'
end
indexes :roles do
indexes :role_name, :type => 'text', :analyzer => 'autocomplete'
end
end
end
# end of elastic search settings
# this function assumes there is only one user role between a user and an account
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
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 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