rails - Elasticsearch completion suggester and search API - ruby-on-rails

I'm using the search API, and now need to add the completion suggester, I'm using elasticsearch-rails gem.
When I search for an article, everything works
http://localhost:9200/articles/_search
"query": {
"multi_match": {
"query": "test",
"fields": [
"title", "tags", "content"
]
}
}
}
But since I've implemented the completion suggester I had to edit as_indexed_json to make it work, but now the search API doesn't work anymore, only the suggestions.
Here is my Article model:
def self.search(query)
__elasticsearch__.search(
{
query: {
multi_match: {
query: query,
fields: ['title', 'content', 'tags']
}
}
})
end
def self.suggest(query)
Article.__elasticsearch__.client.suggest(:index => Article.index_name, :body => {
:suggestions => {
:text => query,
:completion => {
:field => 'suggest'
}
}
})
end
def as_indexed_json(options={})
{
:name => self.title,
:suggest => {
:input => self.title,
:output => self.title,
:payload => {
:content => self.content,
:tags => self.tags,
:title => self.title
}
}
}.as_json
end
Is it possible to have _search and _suggest working together with the same model ?

I'm just digging into elasticsearch, but, as far as i understand, you can add what you had before modifying in the serializer function and recreate indices, they will live together well in the db. For example:
def as_indexed_json(options={})
{
:name => self.title,
:suggest => {
:input => self.title,
:output => self.title,
:payload => {
:content => self.content,
:tags => self.tags,
:title => self.title
}
}
}.as_json.merge(self.as_json) # or the customized hash you used
To avoid indices redundancy you can look at aliases and routing.

Related

mongodb ruby delete_all nested document

I'm trying to implement a delete_all within a nested document using MongoDB Ruby-Driver. It'll be incorporated within a bulk_write.
Model:
User
- Addresses # which is a nested model within User model
I can do this using Mongoid: user.addresses.delete_all
But I need to implement this using MongoDB Ruby Driver. I tried this but it's not working:
{
:update_one => {
:filter => { "_id" => customer.id },
:update => { "$unset" => { "addresses":{} } },
:upsert => false
}
}
I've also tried this and it doesn't work
{
:update_one => {
:filter => { "_id" => customer.id },
:update => { "$pullAll" => { :addresses => [{ :category => "default"}, { :category => "work"}] }},
:upsert => false
}
}
Any suggestions?
I figured it out, it should be:
{
:update_one => {
:filter => { "_id" => customer.id },
:update => { "$set" => { "addresses":[] } },
:upsert => false
}
}

Rails, Tire, Elasticsearch: how to use synonyms?

I have no idea how to use synonyms/plural with Elasticsearch through Tire gem. Have I a synonyms file to download (an english one in enough)? Something to setup in ES regardless I use Tire or not?
class Story < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
attr_accessible :author, :content, :title
mapping do
indexes :id, :index => :not_analyzed
indexes :author, :analyzer => 'keyword'
indexes :title, :analyzer => 'snowball'
indexes :content, :analyzer => 'snowball'
end
end
class StoriesController < ApplicationController
def index
if params[:q].present?
p = params
#stories = Story.search(per_page: 30, page: params[:page], load: true) do
query { string p[:q], default_operator: 'AND' }
end
end
end
end
I found nothing in documentation...
Thanks!
i guess you mean the synonym-tokenfilter of elasticsearch: http://www.elasticsearch.org/guide/reference/index-modules/analysis/synonym-tokenfilter/
{
"index" : {
"analysis" : {
"analyzer" : {
"synonym" : {
"tokenizer" : "whitespace",
"filter" : ["synonym"]
}
},
"filter" : {
"synonym" : {
"type" : "synonym",
"synonyms_path" : "analysis/synonym.txt"
}
}
}
}
}
afaik in tire, this would go in the settings configuration:
settings :analysis => {
:filter => {
:synonym => {
"type" => "synonym",
"synonyms_path" => Rails.root.join("config/analysis/synonym.txt").to_s
}
},
:analyzer => {
:synonym => {
"tokenizer" => "lowercase",
"filter" => ["synonym"],
"type" => "custom" }
}
} do
mapping { indexes :the_field, :type => 'string', :analyzer => "synonym" }

ElasticSearch Scoring (Tire gem)

I want ElasticSearch (Tire gem to be specific) to return the result based on the number of times a keyword appears in the fields. For example, I index the field title in a model called Article. I have two objects, the first object has the title value 'Funny Funny subject' while the second object has the title value 'Funny subject'. I want to index in such a way that if I search for the keyword 'Funny', the first object will return first since it has two 'Funny' words appearing in the title. Is it possible to do this via Tire? What is the indexing method called as well?
Here a working sample, the key factor here is the boostvalue that has to be high enough and you can't use wildcharts in the query.
require 'tire'
require 'yajl/json_gem'
articles = [
{ :id => '0', :type => 'article', :title => 'nothing funny'},
{ :id => '1', :type => 'article', :title => 'funny'},
{ :id => '2', :type => 'article', :title => 'funny funny funny'}
]
Tire.index 'articles' do
import articles
end
Tire.index 'articles' do
delete
create :mappings => {
:article => {
:properties => {
:id => { :type => 'string', :index => 'not_analyzed', :include_in_all => false },
:title => { :type => 'string', :boost => 50.0, :analyzer => 'snowball' },
:tags => { :type => 'string', :analyzer => 'keyword' },
:content => { :type => 'string', :analyzer => 'snowball' }
}
}
}
import articles do |documents|
documents.map { |document| document.update(:title => document[:title].downcase) }
end
refresh
end
s = Tire.search('articles') do
query do
string "title:funny"
end
end
s.results.each do |document|
puts "* id:#{ document.id } #{ document.title } score: #{document._score}"
end
gives
* id:2 funny funny funny score: 14.881571
* id:1 funny score: 14.728935
* id:0 nothing funny score: 9.81929

Rails 3.1 deep nesting with RABL

I'm using the RABL gem to render JSON user data for users of comments which are children of annotations which are children of images. I'd like to do something similar to:
object #image
node :annotations do
#image.annotations.map { |a| {
:id => a.id,
:x1 => a.x1,
:x2 => a.x2,
:y1 => a.y1,
:y2 => a.y2,
node :comments do
#image.annotations.comments.map { |c| {
:body => c.body,
:user_id => c.user_id,
:name => User.find(c.user_id).name,
:user_icon => user_icon(User.find(c.user_id), 'square', 30)
}}
end
}}
end
I know this isn't valid in RABL, I also tried using child instead of node, but couldn't access the user data that way. How should I go about doing this and whats the proper syntax to make this happen?
I got this answer from #calvin-l. The trick was to just map the a.comments then grab the data from each comment that way:
node :annotations do
#image.annotations.map { |a| {
:id => a.id,
:x1 => a.x1,
:x2 => a.x2,
:y1 => a.y1,
:y2 => a.y2,
:comments => a.comments.map { |c| {
:body => c.body,
:created_at => c.created_at,
:user => {
:id => c.user.id,
:facebook_id => c.user.facebook_id,
:name => c.user.name,
:username => c.user.username
}
}}
}}
end

in ruby, how do you make this nested hash work?

this one creates an error:
#settings = {
:tab1 => {
:name => {
:required => true
},
:description
}
}
need to change :descrpition to :description => {}, but i don't have any values for :description so i want it to remain as is (without the empty => {})
Would you show me the best way to handle this kind of situation?
thanks in advance
You can assign nil to it.
#settings = {
:tab1 => {
:name => {
:required => true
},
:description => nil
}
}
Ruby's Hash prior to 1.9 is not ordered, and even afterwards it's a bit clumsy, as AFAIK you can't reorder items etc., so if you also want to preserve the order of the elements, you may consider using array instead of hash:
#settings = {
:tab1 => [
{
:field => :name,
:required => true
},
{
:field => :description
}
]
}

Resources