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
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
In reference to the Query DSL Explained Tutorial Slides 14-15
How do I flatten Nested Objects?
I have a Model named Entry and another named Category and they share a HABTM association.
Everything is currently working and the search results seem to be correct, but I don't know if my mapping is correct. The tutorial says that when you flatten objects the Document will look like this :
{
tweet => "Perl is GREAT!",
posted => "2011-08-15",
user.name => "Clinton Gormley",
user.email => "drtech#cpan.org",
tags => ["perl","opinion"],
posts => 2,
}
with the Object user being flattened. When I look at the source of my JSON document it looks like this:
{
"title":"First",
"description":"first test",
"categories":
{"categories_name":"CAP and Using the CAP website"},
"attachment":"VEVTVCE=\n",
"published":true
}
So, I'm assuming that its supposed to say categories.categories_name but I don't know how to specify that or if that's even necessary. Here's some Model code:
class Entry < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
has_and_belongs_to_many :categories
mount_uploader :doc, EntryDocUploader
tire.mapping do
indexes :title
indexes :description
indexes :categories do
indexes :categories_name, type: 'string', index: 'not_analyzed'
end
indexes :attachment, :type => 'attachment',
:fields => {
:title => { :store => 'yes' },
:attachment => { :term_vector => 'with_positions_offsets', :store => 'yes' }
}
end
def to_indexed_json
{
:title => title,
:description => description,
:categories => {:categories_name => cats}, #categories.map { |c| { :categories_name => c.name}}.to_sentence,
:attachment => attachment,
}.to_json
end
def self.search(params)
tire.search(load: true) do
query { string params[:query], default_operator: "AND" } if params[:query].present?
filter :term, :published => "true"
end
end
def cats
categories.map(&:name).to_sentence
end
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
Having a tough time wrapping my head around Tire's syntax, that of Elasticsearch and how they map together.
I have successfully indexed PDFs in a Rails app via Tire. But I need to break the full reports down into individual pages so queries can be more granular. It's easy enough to split the PDFs into individual pages and add them to a Page model that belongs_to the full Report model. What I'm struggling with is how to set up the mapping and where?!? I'd like to take advantage of Elasticsearch's Parent Field mapping so I can realize this ultimate goal.
Hoping someone can set me straight.
Report model (this is working for me if I index an entire PDF as the :attachment):
class Report < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
has_many :pages, :dependent => :destroy
attr_accessible :filename, :title
tire.mapping do
indexes :id, :type =>'integer'
indexes :title
indexes :attachment, :type => 'attachment',
:fields => {
:content_type => { :store => 'yes' },
:author => { :store => 'yes' },
:title => { :store => 'yes' },
:attachment => { :term_vector => 'with_positions_offsets', :store => 'yes' },
:date => { :store => 'yes' }
}
end
...
end
Page model:
class Page < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
belongs_to :report
attr_accessible :filename, :page_num,
tire.mapping do
indexes :id, :type => 'integer'
indexes :page_num, :type => 'integer'
indexes :report_id, :type => 'integer' ###<== how is this associated with _parent?
indexes :attachment, :type => 'attachment',
:fields => {
...
...
end
end