I'm trying to update elasticsearch index after updating a field in my db.
Create and delete works fine, the index is correctly updated, but not when I try to update a field.
I have included Elasticsearch::Model::Callbacks
Any idea ? Thank you.
@m2omou When you include the Elasticsearch::Model::Callbacks module, changes to the model are intercepted via ActiveRecord hooks, and the index is updated. Uncomment this line https://github.com/elasticsearch/elasticsearch-rails/blob/master/elasticsearch-model/examples/activerecord_article.rb#L57 in the example, and try it out.
Alternatively, you might want to do the indexing in the background, add a custom logic, etc -- please see the relevant chapter in the README.
Hi @karmi, Thanks for the information !
I already included the elastisearch callbacks, but for some reason it still doesn't work for me.
Here is my code so far:
require 'elasticsearch/model'
class Article < ActiveRecord::Base
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
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, self.content],
:output => self.title,
:payload => {
:id => self.id,
:content => self.content
}
}
}
end
end
class ArticlesController < ApplicationController
def update
@article = Article.find(params[:id])
if @article.update_attributes(article_params)
render :json => @article
else
render :json => @article.errors
end
end
end
Do I need to include anything in the controller ?
Thank you.
@m2omou No, you don't need to include anything in the controller. Been playing with it a it with the linked example file, and it works as excepted, even when I add a custom as_indexed_json method in the model.
Can you isolate the problem by using that example? Can you also turn on logging for the Elasticsearch client in your application (see README) and verify what is and isn't sent to Elasticsearch?
@karmi I turned on the logs, and found out something interesting. The parameters doc was empty.
> {"doc":{}}
Then I tried to comment the as_indexed_json block, and was finally able to see my parameters inside doc.
Do I need to remove as_indexed_json to make it work ?
I'm trying to implement the completion suggester:
And according to the doc, I need to index my data in a such way:
curl -X PUT 'localhost:9200/music/song/1?refresh=true' -d '{
"name" : "Nevermind",
"suggest" : {
"input": [ "Nevermind", "Nirvana" ],
"output": "Nirvana - Nevermind",
"payload" : { "artistId" : 2321 },
"weight" : 34
}
}'
That's what I'm trying to do with the as_indexed_json
def as_indexed_json(options={})
{
:name => self.title,
:suggest => {
:input => [self.title, self.content],
:output => self.title,
:payload => {
:id => self.id,
:content => self.content
}
}
}.as_json
end
Is there another way to do the indexing with or without as_indexed_json ? Thank you !
It's perfectly valid to have a custom as_indexed_json, of course. Eg. for the suggester, or for other reasons. What confuses me is that I can make it work with the example, without problems. Can you check out the example application (see Rails templates), so we have a shared codebase with which we can work?
humm that's strange, that doesn't work for me. I also tried with the rails template, and still facing the same issue.
class Article < ActiveRecord::Base
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
__elasticsearch__.client = Elasticsearch::Client.new :log => true
# elasticsearch settings mapping
settings :index => { :number_of_shards => 1 } do
mappings :dynamic => 'false' do
indexes :name, :type => 'string'
indexes :suggest, :type => 'completion', :index_analyzer => 'simple', :search_analyzer => 'simple', :payloads => true
end
end
# elasticsearch suggest
def self.suggest(query)
Article.__elasticsearch__.client.suggest(:index => Article.index_name, :body => {
:suggestions => {
:text => query,
:completion => {
:field => 'suggest'
}
}
})
end
# elasticsearch indexing
def as_indexed_json(options={})
{
:name => self.title,
:suggest => {
:input => [self.title],
:output => self.title,
:payload => {
:id => self.id
}
}
}.as_json()
end
end
rails console
irb(main):001:0> Article.import
Article Load (0.3ms) SELECT "articles".* FROM "articles" ORDER BY "articles"."id" ASC LIMIT 1000
2015-01-13 18:50:09 +0100: POST http://localhost:9200/articles/article/_bulk?type=article [status:200, request:0.017s, query:0.010s]
2015-01-13 18:50:09 +0100: > {"index":{"_id":1}}
{"name":"Updatedsaasdasdd!","suggest":{"input":["Updatedsaasdasdd!"],"output":"Updatedsaasdasdd!","payload":{"id":1}}}
{"index":{"_id":2}}
{"name":"Two","suggest":{"input":["Two"],"output":"Two","payload":{"id":2}}}
{"index":{"_id":3}}
{"name":"Three","suggest":{"input":["Three"],"output":"Three","payload":{"id":3}}}
{"index":{"_id":4}}
{"name":"Four","suggest":{"input":["Four"],"output":"Four","payload":{"id":4}}}
{"index":{"_id":5}}
{"name":"Five","suggest":{"input":["Five"],"output":"Five","payload":{"id":5}}}
2015-01-13 18:50:09 +0100: < {"took":10,"errors":false,"items":[{"index":{"_index":"articles","_type":"article","_id":"1","_version":6,"status":200}},{"index":{"_index":"articles","_type":"article","_id":"2","_version":3,"status":200}},{"index":{"_index":"articles","_type":"article","_id":"3","_version":3,"status":200}},{"index":{"_index":"articles","_type":"article","_id":"4","_version":3,"status":200}},{"index":{"_index":"articles","_type":"article","_id":"5","_version":3,"status":200}}]}
=> 0
Now trying to update a title:
irb(main):002:0> Article.first.update_attribute :title, 'my title'
Article Load (0.5ms) SELECT "articles".* FROM "articles" ORDER BY "articles"."id" ASC LIMIT 1
(0.1ms) begin transaction
SQL (0.7ms) UPDATE "articles" SET "title" = ?, "updated_at" = ? WHERE "articles"."id" = 1 [["title", "my title"], ["updated_at", "2015-01-13 17:50:33.806050"]]
(1.8ms) commit transaction
2015-01-13 18:50:33 +0100: POST http://localhost:9200/articles/article/1/_update [status:200, request:0.009s, query:n/a]
2015-01-13 18:50:33 +0100: > {"doc":{}}
2015-01-13 18:50:33 +0100: < {"_index":"articles","_type":"article","_id":"1","_version":7}
=> true
As you can see the {"doc":{}} is still empty :/
Maybe I'm doing someting wrong...
Found a solution, I directly update the index in my controller:
@article.__elasticsearch__.index_document
Thanks ! :)
I think this happens when you have custom methods in your as_indexed_json method. For example, I have:
mapping dynamic: false do
indexes :start_at, type: 'date'
indexes :end_at, type: 'date'
end
but start_at and end_at are not fields in the database, are methods that I use as helpers.
Since update_document checks for changed_attributes code, those fields will never appear (even when the data that generate those fields changes), that's why I'm getting an empty {"doc":{}} just like @m2omou
So instead of using the default update_document, you should implement your own, or use index_document
Just stumbled on this myself. Thanks for the explanation @orlando
Perhaps this should be explicitly mentioned in the project README for callbacks? It really had me confused. I'm indexing updated_at in my case so I can easily expose recently updated articles, and that field never ends up triggering an update even though it's returned correctly in my custom as_indexed_json method.
The solution is simply adding a custom callback to handle the reindexing on update.
Alternatively, would you accept a patch that optionally includes updated_at if it's found within the indexed json for the document?
We're seeing the same issue, is there a best practice for working around this issue?
@mguterl Using a custom callback instead of the default update_document I think is the best approach, that way you can control when to update your index.
I'm a bit rusty on the original background here, but absolutely, please, consider the "automatic callbacks" just a starting point, just a convenience for somebody who wants to "try it out" -- and write your own logic using the building blocks for anything significant (as @orlando suggests)...
In in my model I added an aftersave and it fixes the problem. I think this is better than doing it in a controller
after_save :reindex
def reindex
__elasticsearch__.index_document
end
Most helpful comment
Found a solution, I directly update the index in my controller:
Thanks ! :)