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'
Related
I'm using elasticsearch on my rails app and every time I try to add a block to the def self.search(query) I get an error:
[400] {"error":{"root_cause":[{"type":"parse_exception","reason":"illegal latitude value [269.99999983236194] for [GeoDistanceSort] for field [distance_type]."}],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[{"shard":0,"index":"prices","node":"AfyW4Pa4S-qKhua-3lY4rg","reason":{"type":"parse_exception","reason":"illegal latitude value [269.99999983236194] for [GeoDistanceSort] for field [distance_type]."}}]},"status":400}
Up to this point, it doesn't return an error:
def self.search(query)
__elasticsearch__.search(
{
query: {
multi_match: {
query: query,
fields: ['title^5', 'description']
}
},
highlight: {
pre_tags: ['<em>'],
post_tags: ['</em>'],
fields: {
title: {},
description: {},
}
}
}
)
end
if a try to add the sort block query inside the model, it returns the above error:
require 'elasticsearch/model'
require 'elasticsearch/model'
class Item < ApplicationRecord
mount_uploader :image, ImageUploader
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
index_name Rails.application.class.parent_name.underscore
document_type self.name.downcase
settings index: { number_of_shards: 1 } do
mappings dynamic: 'false' do
indexes :title, analyzer: 'english', index_options: 'offsets'
indexes :description, analyzer: 'english'
indexes :location, type: 'geo_point'
end
end
def location
[longitude.to_f, latitude.to_f]
end
def current_location
location = request.location
end
def self.search(query)
__elasticsearch__.search(
{
query: {
multi_match: {
query: query,
fields: ['title^5', 'description']
}
},
sort: [
{
_geo_distance: {
"pin.location": ["current_location"],
distance: ["radius"],
unit: ["km"],
mode: ["min"],
order: ["asc"],
distance_type: ["arc"],
}
}
],
highlight: {
pre_tags: ['<em>'],
post_tags: ['</em>'],
fields: {
title: {},
description: {},
}
}
}
)
end
end
Item.__elasticsearch__.client.indices.delete index: Item.index_name rescue nil
Item.__elasticsearch__.client.indices.create \
index: Item.index_name,
body: { settings: Item.settings.to_hash, mappings: Item.mappings.to_hash }
Item.import(force: true)
Update 1
I have installed the geocoder gem to get the location of each address added. So when I create the account with address I get the longitude and latitude coordinates with the following code. After that I added the longitude and langitude columns to the items table, and then when the user uploads an item the coordinates are updated on the items.rb as well.
Here is the account.rb setup:
geocoded_by :full_address
after_validation :geocode, if: ->(obj){ obj.full_address.present? }
def full_address
[street, city, state, zip_code, country].join(",")
end
I have two models:
class City
include Mongoid::Document
field :alternate_names, type: String
field :coordinates, type: Array
field :name, type: String
index({ coordinates: '2d' }, { min: -180, max: 180 })
belongs_to :country
end
and
class Country
include Mongoid::Document
field :iso_3166_code, type: String
field :name, type: String
has_many :cities
end
In the controller I use
#cities = City.where(alternate_names: /#{params[:query].downcase}/).limit(10)
to receive cities list.
Here is an JSON output for each city:
...
"country_id": {
"$oid": "56fc453eae3bbe5c2abcd933"
}
...
How can I get country instead of it's country_id?
I found a solution.
#cities = City.where(alternate_names: /#{params[:query].downcase}/).includes(:country).limit(10)
render json: #cities, except: :country_id, include: :country
Basically I got 3 models(Book,Chapter,Author), and I want to include some of the books and author attributes when indexing chapter.
here is my Chapter.rb
class Chapter < ActiveRecord::Base
belongs_to :book, :counter_cache => true
include Elasticsearch::Model
index_name [Rails.env, model_name.collection.gsub(/\//, '-')].join('_')
mappings do
indexes :id, type: :integer
indexes :title, type: :string
indexes :description, type: :string
indexes :content, type: :string
indexes :updated_at, type: :date # Date example
indexes :book_title
indexes :book_type
indexes :author_name
indexes :book_id
end
def book_title
book.title
end
def book_type
book.book_type
end
def author_name
" #{book.author.firstname} #{book.author.lastname} "
end
def to_indexed_json
to_json methods: [:book_title, :book_type, :author_name]
end
end
http://localhost:9200/development_chapters/_mapping?pretty shows correct mapping
{
"development_chapters" : {
"mappings" : {
"chapter" : {
"properties" : {
"author_name" : {
"type" : "string"
},
"book_title" : {
"type" : "string"
},....
}
}
}
}
}
Then why do I not get author_name, book_title etc... in the search results
<Elasticsearch::Model::Response::Result:0x00000105e393a0 #result=#<Hashie::Mash _id="415" _index="development_chapters" _score=1.0 _source=#<Hashie::Mash book_id=153 content="[\"Explicabo accusantium odit .\"]" created_at="2015-04-22T18:43:58.586Z" description="You can't generate the application without quantifying the cross-platform SDD bandwidth!" id=415 title="Future Communications Orchestrator" updated_at="2015-04-22T18:43:58.586Z"> _type="chapter">>
You are defining wrong serialization method. Elasticsearch::Model searches for method as_indexed_json and you are defining to_indexed_json. In elasticesearch-model gem you can find examples https://github.com/elastic/elasticsearch-rails/blob/master/elasticsearch-model/examples/activerecord_associations.rb#L82
It should look something like this:
def as_indexed_json(options = {})
as_json methods: [:book_title, :book_type, :author_name]
end
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 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)