I'm using chewy.
I can't find 'ops' using any fields of op, except id.
Model:
class Op
include Mongoid::Document
...
state_machine :initial => :draft do
...
end
update_index 'ops#op', :self
end
Index:
class OpsIndex < Chewy::Index
define_type Op
end
Controller:
def index
OpsIndex.reset! # => true
OpsIndex.purge # => {\"acknowledged\"=>true}
OpsIndex::Op.import # => true
scope = OpsIndex::Op.query term: { _id: '55263b48336f63004a000000' }
scope.total_count # => 1 nice!
scope.to_a.inspect => #<OpsIndex::Op:0x00000006f5f310 #attributes={\"_id\"=>{\"$oid\"=>\"55263b48336f63004a000000\"}, \"state\"=>\"deactivated\" ...
#But
scope = OpsIndex::Op.query term: { state: 'deactivated' }
scope.total_count # => 0
end
In development.log:
[1m[32mOpsIndex::Op Search (7.4ms)[0m {:body=>{:query=>{:term=>{:_id=>"55263b48336f63004a000000"}}}, :index=>["development_ops"], :type=>["op"]}
[1m[32mOpsIndex::Op Search (3.2ms)[0m {:body=>{:query=>{:term=>{:state=>"deactivated"}}}, :index=>["development_ops"], :type=>["op"]}
What's wrong?
Wild guess, but how about the following query?
scope = OpsIndex::Op.query match: { state: 'deactivated' }
Solved! The fault in definition excess field _id.
Index: (chewy/ops_index.rb)
define_type Op do
# field :_id #don't specify it!
field :created_at, :updated_at, :postponed, type: 'integer', index: :not_analyzed
field :state
field :name
field :description
end
Related
I'm going to use elastic search for my ruby on rails project. I get this error when I search some of the word that It's used in my article too much.
NoMethodError (undefined method `highlight' for #<Elasticsearch::Model::Response::Result:0x007f062ed26708>)
i got this in the log production. this is what everything that i did:
in controller:
# POST /search/article
def search
render json: Article.search(params[:query]), each_serializer: ElasticsearchResultsSerializer
end
this is my article.rb model
#default_scope { order('created_at DESC') }
scope :visible, -> { where(enabled: true) }
after_commit on: [:create] do
self.keywords = self.keywords.each {|str| str.force_encoding("UTF-8")}
__elasticsearch__.index_document if self.enabled?
end
after_commit on: [:update] do
self.keywords = self.keywords.each {|str| str.force_encoding("UTF-8")}
__elasticsearch__.update_document if self.enabled?
end
after_commit on: [:destroy] do
__elasticsearch__.delete_document
end
settings index: { number_of_shards: 1, number_of_replicas: 0 }
mappings dynamic: 'false' do
indexes :content, type: "string", index_options: 'offsets'
indexes :title, type: "string"
indexes :description, type: "string"
indexes :category, type: "string"
indexes :created_at, type: "date"
indexes :keywords, type: "string"
end
def self.search(query)
__elasticsearch__.search(
{
query: {
multi_match: {
query: query,
fields: ['title^10', 'content^5', 'description^2', 'keywords', 'category']
}
},
highlight: {
pre_tags: ['<em>'],
post_tags: ['</em>'],
fields: { title: {}, content: {} }
}
}
)
end
def as_indexed_json(options={})
as_json(
only: [:content, :title, :id, :category, :keywords, :description]
)
end
and also i used serializer
class ElasticsearchResultsSerializer < ActiveModel::Serializer
attributes :_id, :highlight, :_score, :_source
def _source
#article = object._index.singularize.capitalize.constantize.find(object._id)
#serializer = "#{object._index.singularize.capitalize}Serializer".constantize
#serializer.new(#article)
end
end
Maybe is a silly observation, but did you tried to change the value
:highlight to :_highlight ?
class ElasticsearchResultsSerializer < ActiveModel::Serializer
attributes :_id, :_highlight, :_score, :_source
def _source
#article = object._index.singularize.capitalize.constantize.find(object._id)
#serializer = "#{object._index.singularize.capitalize}Serializer".constantize
#serializer.new(#article)
end
end
I would try switching fields to:
fields: {
:"*" => {}
}
Just to see if that gets rid of the error. See the following: https://github.com/elastic/elasticsearch-rails/issues/446
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 have a class model, a student model and an attendance model. Attendance is embedded in Student to improve the performance.
I want to show number of all students in Class, number of present students, number of absent student & percentage of attendance. I am a newbie in Mongodb and i would appreciate any help. Thanks you for your time.
class Klass
include Mongoid::Document
include Mongoid::Timestamps
has_and_belongs_to_many :students
field :name, type: String
end
class Student
include Mongoid::Document
include Mongoid::Timestamps
has_and_belongs_to_many :klasses
embeds_many :attendances
field :name, type: String
end
class Attendance
include Mongoid::Document
include Mongoid::Timestamps
embedded_in :student
field :status, type: Integer # 1 = Present, 2 = Absent
field :klass_id, type: BSON::ObjectId
end
I have solved my problem by following technique.
#students_present_today = #class.students.where({ attendances: { '$elemMatch' => {status: 1, :created_at.gte => Date.today} } }).count
#students_absent_today = #class.students.where({ attendances: { '$elemMatch' => {status: 2, :created_at.gte => Date.today} } }).count
You can try these:
#class = Klass.where(name: 'something').first
#total_students = #class.students.count
#present_students = #class.students.where('attendances.status' => '1').count
#absent_students = #class.students.where('attendances.status' => '2').count
#p_s_today = #class.students.where('attendances.status' => '1', 'attendances.created_at' => {'$gte' => Date.today} ).count
#a_s_today = #class.students.where('attendances.status' => '2', 'attendances.created_at' => {'$gte' => Date.today} ).count
I am trying build a search function in rails based on elasticsearch+tire enabling search for Persons with filtering for associated Objects and their Values. A Person has_many Objects, and an Object has_many Values.
I have managed to get the filtering on the object name (params[:object]) to work, but not for object+value. How should I construct the range filter for the values and the mapping so that the value is dependent on the object?
Person controller
mapping do
indexes :objects do
indexes :_id
indexes :object_values do
indexes :value
end
end
indexes :name, type: 'string', analyzer: 'snowball'
end
def self.search(params)
tire.search do
query do
boolean do
must { string params[:query]} if params[:query].present?
end
end
filter :term, {"objects._id" => params[:object]} if params[:object].present?
filter :range, “objects.object_values.value” => {from: params[:value] } if params[:value].present?
end
end
def to_indexed_json
{
:name => name,
:objects => objects.map { |o| {
:_type => 'object',
:_id => o.id,
:object_values => o.object_values.map {|ov| {
:_type => 'object_value',
:_id => ov.id,
:value => ov.value } },
} }
}.to_json
end
Use gt or gte rather than from to specify the lower bounds of your range
filter :range, “objects.object_values.value” => {from: params[:value] }
What do you think is the most optimal way to retrieve all attributes for all the associations an AR model has?
i.e: let's say we have the model Target.
class Target < ActiveRecord::Base
has_many :countries
has_many :cities
has_many :towns
has_many :colleges
has_many :tags
accepts_nested_attributes_for :countries, :cities, ...
end
I'd like to retrieve all the association's attributes by calling a method on a Target instance:
target.associations_attributes
>> { :countries => { "1" => { :name => "United States", :code => "US", :id => 1 },
"2" => { :name => "Canada", :code => "CA", :id => 2 } },
:cities => { "1" => { :name => "New York", :region_id => 1, :id => 1 } },
:regions => { ... },
:colleges => { ... }, ....
}
Currently I make this work by iterating on each association, and then on each model of the association, But it's kind of expensive, How do you think I can optimize this?
Just a note: I realized you can't call target.countries_attributes on has_many associations with nested_attributes, one_to_one associations allow to call target.country_attributes
I'm not clear on what you mean with iterating on all associations. Are you already using reflections?
Still curious if there's a neater way, but this is what I could come up with, which more or less results in the hash you're showing in your example:
class Target < ActiveRecord::Base
has_many :tags
def associations_attributes
# Get a list of symbols of the association names in this class
association_names = self.class.reflect_on_all_associations.collect { |r| r.name }
# Fetch myself again, but include all associations
me = self.class.find self.id, :include => association_names
# Collect an array of pairs, which we can use to build the hash we want
pairs = association_names.collect do |association_name|
# Get the association object(s)
object_or_array = me.send(association_name)
# Build the single pair for this association
if object_or_array.is_a? Array
# If this is a has_many or the like, use the same array-of-pairs trick
# to build a hash of "id => attributes"
association_pairs = object_or_array.collect { |o| [o.id, o.attributes] }
[association_name, Hash[*association_pairs.flatten(1)]]
else
# has_one, belongs_to, etc.
[association_name, object_or_array.attributes]
end
end
# Build the final hash
Hash[*pairs.flatten(1)]
end
end
And here's an irb session through script/console to show how it works. First, some environment:
>> t = Target.create! :name => 'foobar'
=> #<Target id: 1, name: "foobar">
>> t.tags.create! :name => 'blueish'
=> #<Tag id: 1, name: "blueish", target_id: 1>
>> t.tags.create! :name => 'friendly'
=> #<Tag id: 2, name: "friendly", target_id: 1>
>> t.tags
=> [#<Tag id: 1, name: "blueish", target_id: 1>, #<Tag id: 2, name: "friendly", target_id: 1>]
And here's the output from the new method:
>> t.associations_attributes
=> {:tags=>{1=>{"id"=>1, "name"=>"blueish", "target_id"=>1}, 2=>{"id"=>2, "name"=>"friendly", "target_id"=>1}}}
try this with exception handling:
class Target < ActiveRecord::Base
def associations_attributes
tmp = {}
self.class.reflections.symbolize_keys.keys.each do |key|
begin
data = self.send(key) || {}
if data.is_a?(ActiveRecord::Base)
tmp[key] = data.attributes.symbolize_keys!
else
mapped_data = data.map { |item| item.attributes.symbolize_keys! }
tmp[key] = mapped_data.each_with_index.to_h.invert
end
rescue Exception => e
tmp[key] = e.message
end
end
tmp
end
end
This is updated version of Stéphan Kochen's code for Rails 4.2
def associations_attributes
association_names = self.class.reflect_on_all_associations.collect { |r| r.name }
me = self.class.includes(association_names).find self.id
pairs = association_names.collect do |association_name|
object_or_array = me.send(association_name)
if object_or_array.is_a? ActiveRecord::Associations::CollectionProxy
association_pairs = object_or_array.collect { |o| [o.id, o.attributes] }
[association_name, Hash[*association_pairs.flatten(1)]]
else
[association_name, object_or_array.attributes]
end
end
Hash[*pairs.flatten(1)]
end