I put tire search in my model:
class Name < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
mapping do
indexes :name, type: 'string', analyzer: 'snowball'
indexes :lang, type: 'string'
indexes :private, type: 'boolean'
indexes :id, index: :not_analyzed, type: 'integer'
end
end
Then, when i perform:
txt = params[:search]
Name.tire.search page: page, per_page: PER_PAGE do
string txt
end
If works well, but how do i chain more search conditions like:
Name.where(private: false, lang: ['ru', 'en'], id: [1,2,3,4])
I tried to do:
#results = Name.tire.search per_page: per, page: page do
query do
boolean do
must { string txt }
must { term 'names.id', ids } unless ids.blank?
must { term 'names.private', false }
must { term 'names.lang', lang }
end
end
end
But it not returning any results..
try with:
Name.tire.search per_paga: per, page: page do
query {string txt}
filter :boolean, private: false
filter :array, lang: ['ru', 'en'] #here i'm not sure if is array or string
end
Finally found the solution.
Name.tire.search per_pag: per, page: page do
query {string 'text'}
filter :term, private: false
filter :terms, lang: ['ru', 'en']
filter :terms, id: [1,2,3,4]
end
Note the difference in "term" and "terms"(for array desired)
Related
After reading several sites (including elasticsearch's documentation) and experimenting around a lot, I'm having trouble getting highlights. I can do the basic keyword search, but it's clear I'm not grasping something. Here's my code.
Gems:
gem 'elasticsearch-model'
gem 'elasticsearch-rails'
Controller:
class TermsController < ApplicationController
def search
#terms = Term.search(params[:query]).results
end
end
Model:
require 'elasticsearch/model'
class Term < ActiveRecord::Base
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
settings index: { number_of_shards: 1, number_of_replicas: 0 } do
mappings dynamic: 'false' do
indexes :id, index: :not_analyzed
indexes :name, analyzer: 'spanish'
indexes :gender, index: :not_analyzed
indexes :part_of_speech, index: :not_analyzed
indexes :definition
indexes :etymology1
indexes :etymology2
indexes :uses
indexes :romance_cognates
indexes :notes1
indexes :notes2
indexes :quote1, analyzer: 'spanish'
indexes :quote2, analyzer: 'spanish'
end
end
def as_indexed_json(options = {})
as_json(
only: [:name, :gender, :part_of_speech, :definition, :etymology1, :etymology2, :uses, :romance_cognates, :notes1, :notes2, :quote1, :quote2]
)
end
def self.search(query)
__elasticsearch__.search(
{
query: {
multi_match: {
query: query,
fields: ['name', 'definition', 'etymology1', 'etymology2', 'uses', 'romance_cognates', 'notes1', 'notes2', 'quote1', 'quote2']
}
},
highlight: {
tags_schema: 'styled',
fields: {
:'*' => {}
}
}
}
)
end
end
# Delete the previous terms index in Elasticsearch
Term.__elasticsearch__.client.indices.delete index: Term.index_name rescue nil
# Create the new index with the new mapping
Term.__elasticsearch__.client.indices.create \
index: Term.index_name,
body: { settings: Term.settings.to_hash, mappings: Term.mappings.to_hash }
# Index all term records from the db to Elasticsearch
Term.import(force: true)
I also tried:
{
query: {
multi_match: {
query: query,
fields: ['name', 'definition', 'etymology1', 'etymology2', 'uses', 'romance_cognates', 'notes1', 'notes2', 'quote1', 'quote2']
}
},
highlight: {
fields: {
content: {'force_source': true}
}
}
}
and
{
query: {
multi_match: {
query: query,
fields: ['name', 'definition', 'etymology1', 'etymology2', 'uses', 'romance_cognates', 'notes1^5', 'notes2', 'quote1', 'quote2']
}
},
highlight: {
fields: {
content: {type: 'plain'}
}
}
}
and
{
query: {
multi_match: {
query: query,
fields: ['name', 'definition', 'etymology1', 'etymology2', 'uses', 'romance_cognates', 'notes1^5', 'notes2', 'quote1', 'quote2']
}
},
highlight: {
pre_tags: ['<tag1>']
post_tags: ['</tag1>']
fields: {
_all: {}
}
}
}
...Along with many other attempts I can't remember
It appears the key that I was missing as illustrated here is that I needed the try() method in my view template. I'm sure there's a more concise way of writing this, but a sample of my view syntax looks like this:
<%= term.try(:highlight).try(:definition) ? term.highlight.definition[0].html_safe : term.definition.html_safe %>
<%= term.try(:highlight).try(:etymology1) ? term.highlight.etymology1[0].html_safe : term.etymology1.html_safe %>
I've got an Article model:
class Article < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
settings default_options do
mapping do
indexes :id, index: :not_analyzed
indexes :roles do
indexes :machine_name, analyzer: 'keyword'
end
indexes :published_at, type: 'date', include_in_all: false
end
end
end
where the default_options is:
index: { store: { type: Rails.env.test? ? :memory : :niofs },
analysis: {
analyzer: {
default: {
tokenizer: "standard",
filter: ["asciifolding", "lowercase", "snowball"],
char_filter: ["html_strip"]
}
}
}
I'm simply trying to search articles while filtering roles, but I don't have any idea how to do so. I've been trying something like that without success:
Tire.search("article") do
query { string 'foo bar baz' }
filter :nested, { path:'roles',
query: {
filtered: {
query: {
match_all: {}
},
filter: {
term:{'roles.machine_name' => ['da']}
}
}
}
}
end
This give me that error:
QueryParsingException[[development-oaciq::application-article] [nested] nested object under path [roles] is not of nested type];
After finding this question, it seems the nested filter wasn't required, it could be done like this:
Tire.search("article") do
query do
string 'foo bar baz'
term 'roles.machine_name', 'test'
end
end
I am trying build a search function in rails based on elasticsearch+tire enabling search for Persons with filtering for associated Objects and their Values. A Person has_many Objects, and an Object has_many Values.
I have managed to get the filtering on the object name (params[:object]) to work, but not for object+value. How should I construct the range filter for the values and the mapping so that the value is dependent on the object?
Person controller
mapping do
indexes :objects do
indexes :_id
indexes :object_values do
indexes :value
end
end
indexes :name, type: 'string', analyzer: 'snowball'
end
def self.search(params)
tire.search do
query do
boolean do
must { string params[:query]} if params[:query].present?
end
end
filter :term, {"objects._id" => params[:object]} if params[:object].present?
filter :range, “objects.object_values.value” => {from: params[:value] } if params[:value].present?
end
end
def to_indexed_json
{
:name => name,
:objects => objects.map { |o| {
:_type => 'object',
:_id => o.id,
:object_values => o.object_values.map {|ov| {
:_type => 'object_value',
:_id => ov.id,
:value => ov.value } },
} }
}.to_json
end
Use gt or gte rather than from to specify the lower bounds of your range
filter :range, “objects.object_values.value” => {from: params[:value] }
I'm trying to index a geo_point field in Elasticsearch with the Tire gem. Here is my Tire mapping for my ActiveRecord model :
class Availability < ActiveRecord::Base
belongs_to :user
attr_accessible :date, :latitude, :longitude
include Tire::Model::Search
include Tire::Model::Callbacks
tire do
mapping do
indexes :id, type: 'integer', index: 'not_analysed'
indexes :user_id, type: 'integer', index: 'not_analysed'
indexes :user_firstname, type: 'string', as: 'user_firstname'
indexes :user_lastname, type: 'string', as: 'user_lastname'
indexes :user_level, type: 'integer', as: 'user_level'
indexes :date, type: 'date'
indexes :location, type: 'geo_type', as: 'location'
end
end
# def location
# "#{latitude},#{longitude}"
# end
def location
[longitude.to_f, latitude.to_f]
end
def user_firstname
user.firstname
end
def user_lastname
user.lastname
end
def user_level
user.level
end
end
When I create the mapping (bundle exec rake environment tire:import CLASS=Availability FORCE=true), Elasticsearch seems to ignore the geo_point type for the location field.
Here is the result of the http://localhost:9200/availabilities/_mapping call :
{
availabilities: {
availability: {
properties: {
date: {...},
id: {...},
location: {
type: "double"
},
user_firstname: {...},
user_id: {...},
user_lastname: {...},
user_level: {...}
}
}
}
}
The location field is indexed as an array of double on the documents (results of http://localhost:9200/availabilities/_search) :
{
id: 8,
...
location: [
2.301643,
48.780651
]
}
When I change the location method to :
def location
"#{latitude},#{longitude}"
end
Which is another solution to index a geo_point field according to the documentation (http://www.elasticsearch.org/guide/reference/mapping/geo-point-type.html), the result for the location mapping is :
location: {
type: "string"
},
And of course the location field is indexed as a string :
{
id: 4,
...
location: "48.780651,2.301643"
}
Any idea why the geo_point is ignored in my mapping ?
Thanks !
The location index type was mistyped.
You used geo_type instead of geo_point:
indexes :location, type: 'geo_point', as: 'location'
What I want to do:
I have a model 'Item' with 2 fields I want elasticsearch to search on: title and description.
I want the search to find partial words, ex: bicycl should match against bicycle, bicycles, etc...
Current situation:
The search only shows perfect matches
Here is what I have right now in my Item model:
include Tire::Model::Search
include Tire::Model::Callbacks
class << self
def search_index
Tire.index(Item.index_name)
end
end
settings :analysis => {
:filter => {
:my_ngram => {
"type" => "nGram",
"max_gram" => 10,
"min_gram" => 3 }
},
:analyzer => {
:my_analyzer => {
"type" => "custom",
"tokenizer" => "standard",
"filter" => ["my_ngram"]
}
}
} do
mapping do
indexes :title, boost: 10, analyzer: 'my_analyzer'
indexes :description, boost: 5, analyzer: 'my_analyzer'
end
end
def self.search(query_string)
tire.search(load: true) do
if query_string.present?
query do
string query_string, default_operator: "AND"
end
end
end
end
When you do...
string query_string, default_operator: "AND"
... you're actually searching the magic _all field.
I'm pretty sure that you need to specifically search for the field analyzed with the ngram filter for this to work.
should { string "title:#{query_string}", default_operator: "OR" }
should { string "description:#{query_string}", default_operator: "OR" }
for instance.