How to create index of an instance method in elasticsearch-rails? - ruby-on-rails

What I want to do
https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-model
Using this gem, I wanna create indexes of User model, including output of method named full_name. User has columns of id, first_name, last_name.
class User
...
def full_name
first_name + last_name
end
...
end
What I did
module UserSearchable
extend ActiveSupport::Concern
included do
include Searchable
settings index: {
...
}
mappings do
indexes :id, type: :integer
indexes :first_name, type: text
indexes :last_name, type: text
indexes :full_name, type: text, as: 'full_name'
end
def as_indexed_json(options={})
as_json(
methods: [:full_name]
)
end
end
end
I referred to this question.
Index the results of a method in ElasticSearch (Tire + ActiveRecord)
But this doesn't work, ending up with not containing full_name index in the response.
I'm new to elasticsearch and elasticsearch-rails.
How can I fix this?

Sorry, I just forgot to reload changes in the code!
And It works without as: 'full_name' in current version of the gem.

Related

Implementing elasticsearch index update in ruby on rails application

I am creating a rails application with integration to elasticsearch for fulltext search. I have the search working for a model, however when another part of the app does an attribute_update on it, I get below error:
Elasticsearch::Transport::Transport::Errors::BadRequest ([400] {"error":"no handler found for uri [/cards/_doc/123/_update] and method [POST]"}):
I tried to add a call to update_document in the model, however, I still get the same error as above.
class Card < ApplicationRecord
include Searchable
validates :attr1, uniqueness: true
after_update :my_es_update
def my_es_update
self.__elasticsearch__.update_document
end
def self.es_full_search
response = Card.__elasticsearch__.search(
query: {
match_all: {}
},
size: 150
).results
#cards = response.results
end
Update call:
def pin
#card = Card.find(params[:id])
#card.update_attribute("pinned", true)
redirect_back(fallback_location: root_path)
end
What would be the right way to update the index in ES upon update to persistence layer?
Edit:
I have the Elasticsearch::Model::Callbacks module included in my model, per the documentation it should take care of updates...I am not sure what part of the setup I am missing here.
module Searchable
extend ActiveSupport::Concern
included do
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
mapping do
indexes :name, type: :text
indexes :type, type: :text
indexes :size, type: :text
end
end
end
Update2:
I am using ES vsn 8.5.3 and the 2 below gems in my Gemfile for communicating with it:
gem 'elasticsearch'
gem 'elasticsearch-model', '~> 7.2.1'

rails 4 gem 'mongoid_slug' unable to search by id

I just installed the gem 'mongoid_slug', here is the model:
class Book
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Slug
field :_id, type: String, slug_id_strategy: lambda {|id| id.start_with?('....')}
field :name, type: String
slug :name,
...
end
In the controller I have a function call get_book that I call before edit, show etc
Of course it is not working, I also tried find_by_slug.
Error Document not found for class Book with attributes {:id=>"try-new-book"}.
Request info
Request parameters
{"action"=>"show", "controller"=>"startups", "id"=>"try-new-book"}
def get_book
#book = Book.find_by(id: params[:id])
end
Thank you
According to docs it should works just:
Book.find params[:id]
Updated
I answered the same question here. So in short: change id.start_with?('....') to something like id =~ /^[[:alnum:]]+$/

Searchkick / Rails working example with Mongoid

I can't figure how to properly hook a view / form for searching through my docs with elasticsearch + mongoid.
My Searchkick config is working – I get the results inside the rails console. But I don't know exactly where to put things in what order in the controller/model/view to make it work. From the rails console I get the search results, docs are properly mapped and everything is fine.
I can't find a working example of an simple search form with searchkick including an working example of how a model, controller and a view should look like.
Anybody got an working example to checkout with Rails 4.1rc1 / Mongoid4 / Elasticsearch 1.0.1?
I've got rails 4.0.2, Mongoid4, and ElasticSearch >= 1
Anyway, this works for us in the controller:
class CropSearchesController < ApplicationController
def search
query = params[:q].to_s
#crops = Crop.search(query,
limit: 25,
partial: true,
misspellings: {distance: 2},
fields: ['name^20',
'common_names^10',
'binomial_name^10',
'description'],
boost_by: [:guides_count]
)
if query.empty?
#crops = Crop.search('*', limit: 25, boost_by: [:guides_count])
end
# Use the crop results to look-up guides
crop_ids = #crops.map { |crop| crop.id }
#guides = Guide.search('*', where: {crop_id: crop_ids})
render :show
end
end
And this is our model:
class Crop
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Slug
searchkick
field :guides_count, type: Fixnum, default: 0
field :name
field :common_names, type: Array
validates_presence_of :name
field :binomial_name
field :description
belongs_to :crop_data_source
field :sun_requirements
field :sowing_method
field :spread, type: Integer
field :row_spacing, type: Integer
field :height, type: Integer
embeds_many :pictures, cascade_callbacks: true, as: :photographic
accepts_nested_attributes_for :pictures
def search_data
as_json only: [:name, :common_names, :binomial_name, :description, :guides_count]
end
slug :name
end
And then you just accesses the #crops in the view like you would normally.
You can have a look at our source code on github: https://github.com/openfarmcc/OpenFarm
(Just stumbled across this question while looking for how to do the ordering, figured it out, but thought I'd just copy paste this here in case anyone finds this useful).

Mongoid: add some dynamic (non-DB) fields in Model before passing it to Controller

Let's say I have a Model Item which uses Mongoid
class Item
include Mongoid::Document
field :title, type: String
...
...
end
I want to add some dynamic fields to Item right in Model before passing data to a Controller - because Item is being used by several Controllers.
For example I want to add thumb field which I will generate by adding /path/to + filename.
I tried some solutions with attr_accessor:
class Item
include Mongoid::Document
field :title, type: String
...
...
attr_accessor :thumb
def prepare_data
#thumb = "/path/to/thumb"
end
end
...And later in some Controller:
#items_all = Item.all
#thumbs = []
#items_all.each do |i]
i.prepare_data
#thumbs.push(i[:thumb])
end
# #thumbs >>> (empty)
So it seems that I'm missing some point here because it doesn't work.
Also can I avoid calling prepare_data each time manually? May be with help of after_initialize? (which didn't work for me also).
I found my mistake. First I forgot to add after_initialize :do_something and then I found that I can user #attributes.merge!
after_initialize :do_after_initialize
def do_after_initialize
#attributes.merge!({
:title => self.get_title,
:type => self.get_type,
:thumb => ImageUploader::thumb(self[:pictures][0]["filename"])
:price_f => ActionController::Base.helpers.number_to_currency(self[:price], {:precision=>0})
})
end

How can you update/destroy one embed document that is inside of all documents on a collection

My application model allows Patients to have CustomFields. All patients have the same customs fields. Customs fields are embedded in the Patient document. I should be able to add, update and remove custom fields and such actions are extended to all patients.
class Patient
include Mongoid::Document
embeds_many :custom_fields, as: :customizable_field
def self.add_custom_field_to_all_patients(custom_field)
Patient.all.add_to_set(:custom_fields, custom_field.as_document)
end
def self.update_custom_field_on_all_patients(custom_field)
Patient.all.each { |patient| patient.update_custom_field(custom_field) }
end
def update_custom_field(custom_field)
self.custom_fields.find(custom_field).update_attributes({ name: custom_field.name, show_on_table: custom_field.show_on_table } )
end
def self.destroy_custom_field_on_all_patients(custom_field)
Patient.all.each { |patient| patient.remove_custom_field(custom_field) }
end
def remove_custom_field(custom_field)
self.custom_fields.find(custom_field).destroy
end
end
class CustomField
include Mongoid::Document
field :name, type: String
field :model, type: Symbol
field :value, type: String
field :show_on_table, type: Boolean, default: false
embedded_in :customizable_field, polymorphic: true
end
All pacients have the same customs fields embedded in. Adding a custom field works very well. My doubt is about updating and destroying.
This works, but it is slow. It makes a query for each pacient. Ideally I would just be able to say to MongoDB 'update the document with id: that is embedded in the array *custom_fields* for all documents in the Patient collection'. Idem for destroy.
How can I do this in Mongoid?
I am using Mongoid 3.1.0 & Rails 3.2.12
I don't think there is a way you can do that with a good efficiency with embedded documents.
Maybe you should consider having a referenced relationship between your models, so that you can use the delete_all and update_all methods on the collection.

Resources