ElasticSearch + Tire searching only on id field - ruby-on-rails

I've looked everywhere but nothing can help me. And I can't figure out what is the problem.
my model
class Article
include Mongoid::Document
include Mongoid::Timestamps
include Tire::Model::Search
include Tire::Model::Callbacks
mapping do
indexes :_id, :index => :not_analyzed
indexes :title
indexes :body
end
def to_indexed_json
self.to_json
end
http://localhost:9200/articles/_mapping
{
"articles": {
"article": {
"properties": {
"$oid": {
"type": "string"
},
"body": {
"type": "string"
},
"title": {
"type": "string"
}
}
}
}
}
Article.search 'love' gives no results,but there are Article with title "love", I've tried to build many requests but nothing works.
all times the same results:
"hits"=>{"total"=>0, "max_score"=>nil, "hits"=>[]}}
But If I type: Article.search "cbc267c955464f22d72a0100" it gives me article with title: "love"
So it seems to me that tire create indexes only on ID field, regardless mapping indexes on model.
When I recreate indexes
Article.index_name
=> "articles"
Tire.index('articles').delete
=> true
Article.import
my mapping becomes:
{
"articles": {
"article": {
"properties": {
"$oid": {
"type": "string"
}
}
}
}
}
UPDATED
module BSON
class ObjectId
def as_json(*args)
to_s()
end
def to_json(*args)
MultiJson.encode(as_json())
end
end
end
After implementing this initialize, all seems to work fine

I too encountered such problems while using tire. After deleting the index, you can try Article.create_elasticsearch_index and Article.tire.index.import Article.all.
The _source field in your indexed document should have title,id and body included in it, which should make it available for search.
Anyways, tire is getting retired as elasticsearch gem has now been released.

Related

Spell check Ngram for elastic Search not working with rails

I have used in my model to include spell check such that if the user inputs data like "Rentaal" then it should fetch the correct data as "Rental"
document.rb code
require 'elasticsearch/model'
class Document < ApplicationRecord
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
belongs_to :user
Document.import force: true
def self.search(query)
__elasticsearch__.search({
query: {
multi_match: {
query: query,
fields: ['name^10', 'service']
}
}
})
end
settings index: {
"number_of_shards": 1,
analysis: {
analyzer: {
edge_ngram_analyzer: { type: "custom", tokenizer: "standard", filter:
["lowercase", "edge_ngram_filter", "stop", "kstem" ] },
}
},
filter: {
edge_ngram_filter: { type: "edgeNGram", min_gram: "3", max_gram:
"20" }
}
} do
mapping do
indexes :name, type: "string", analyzer: "edge_ngram_analyzer"
indexes :service, type: "string", analyzer: "edge_ngram_analyzer"
end
end
end
search controller code:
def search
if params[:query].nil?
#documents = []
else
#documents = Document.search params[:query]
end
end
However, if I enter Rentaal or any misspelled word, it does not display anything.
In my console
#documents.results.to_a
gives an empty array.
What am I doing wrong here? Let me know if more data is required.
Try to add fuzziness in your multi_match query:
{
"query": {
"multi_match": {
"query": "Rentaal",
"fields": ["name^10", "service"],
"fuzziness": "AUTO"
}
}
}
Explanation
Kstem filter is used for reducing words to their root forms and it does not work as you expected here - it would handle corectly phrases like Renta or Rent, but not the misspelling you provided.
You can check how stemming works with following query:
curl -X POST \
'http://localhost:9200/my_index/_analyze?pretty=true' \
-d '{
"analyzer" : "edge_ngram_analyzer",
"text" : ["rentaal"]
}'
As a result I see:
{
"tokens": [
{
"token": "ren"
},
{
"token": "rent"
},
{
"token": "renta"
},
{
"token": "rentaa"
},
{
"token": "rentaal"
}
]
}
So typical misspelling will be handled much better with applying fuzziness.

Rails ElasticSearch model with fields

I'm using elasticsearch-rails and mongoid
I have the following simple mapping with fields:
"title": {
"type": "string",
"fields": {
"raw": {
"type": "string",
"index": "not_analyzed"
}
}
}
My model looks like this:
class ArticlesEvent
include Mongoid::Document
include Elasticsearch::Model
field :title, type: String
attr_accessible :title
def as_indexed_json(options={})
as_json(except: [:id, :_id])
end
Can anyone show an example how to define the rails model with the title.raw field, and how to access that field? Since the multi fields have been deprecated it's hard to find a working example with rails and mongoid.
Thanks
You should be able to achieve this with the following attribute definition in your model:
attribute :title, String, mapping: {
fields: {
raw: { type: 'string', index: 'not_analyzed' }
}
}
Looks like this is still an open issue with elasticsearch-rails: https://github.com/elastic/elasticsearch-rails/issues/79
You can define below mapping in your model
indexes :name_of_field,type: :keyword, fields: {
raw: {
type: :text
}
}

Rails Elasticsearch analyzer mappings defined in model are not reported in elasticsearch

In my Recipe model I have :
class Recipe < ActiveRecord::Base
index_name "recipes-#{Rails.env}"
settings do
mappings dynamic: 'false' do
indexes :title, type: 'string', analyzer: 'french'
indexes :description, type: 'string', analyzer: 'french'
end
end
def as_indexed_json(options={})
self.as_json({only: [:title, :description]})
end
Then in rails console, I launch Recipe.import. When asking to elasticsearch via curl or Sense GET /recipes-development/_mapping/, I get
{
"recipes-development": {
"mappings": {
"recipe": {
"properties": {
"description": {
"type": "string"
},
"title": {
"type": "string"
}
}
}
}
}
}
I have lost all informations about analyzer. Any idea would be appreciated
Before Recipe.import you have to execute
Recipe.__elasticsearch__.create_index! force: true

Find model with part of title using ElasticSearch / Rails

There is the following Post model:
class Post < ActiveRecord::Base
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
def self.search query
__elasticsearch__.search(
{
query: {
multi_match: {
query: query,
fields: ['title']
}
},
filter: {
and: [
{ term: { deleted: false } },
{ term: { enabled: true } }
]
}
}
)
end
settings index: { number_of_shards: 1 } do
mappings dynamic: 'false' do
indexes :title, analyzer: 'english'
end
end
end
Post.import
I have one Post with 'Amsterdam' title. When I execute Post.search('Amsterdam') I will get one record, all is good. But if I execute Post.search('Amster') I will get no records. What do I wrong? How can I fix it? Thanks!
OS - OS X, ElasticSearch I installed using Homebrew
You will have to use nGram tokenizer, in order to create a partial text search. A very good example of how to do this can be found here. That said, I would be very careful with nGram, as it can often turn up unrelated results.
This is because the substring "mon" is contained within all of the strings: "monkey", "money", and "monday". All of which are unrelated.
Alternatively (What I would do.)
You could try making it a fuzzy search. However, the max distance with fuzzy search is only two, which still doesn't return anything in your example. However, it tends to return relevant results.
The example I found: How to use Fuzzy Search
# Perform a fuzzy search!
POST /fuzzy_products/product/_search
{
"query": {
"match": {
"name": {
"query": "Vacuummm",
"fuzziness": 2,
"prefix_length": 1
}
}
}
}

in rails json rendering, how to show a different key name for a particular attribute

I am using Mongoid as my backend and I am in need to return json with an "id" attribute instead of the default "_id" used by mongoid
for instance, I have now
[{
"_id": "4f2d8b971773eb18e6000001",
"name": "Scooter"
}, {
"_id": "4f2d8d9f1773eb18fd000001",
"name": "Coldplay"
}]
from a call to render:
format.json { render :json => #groups, only:[:name, :_id] }
but need,
[{
"id": "4f2d8b971773eb18e6000001",
"name": "Scooter"
}, {
"id": "4f2d8d9f1773eb18fd000001",
"name": "Coldplay"
}]
Any shortcuts?
Thank you!!
If you're able to add an attribute accessor for _id called just id, then this should be easily solved by overriding as_json in your model.
def id
self._id
end
def as_json(options={})
options.merge!(:except => :_id, :methods => :id)
super(options)
end
Update: Made the override a bit more friendly to the parent method.

Resources