I have simple Rails app with a Post model that belongs to a creator (which is a User object) and I want to find posts that match the creator's first_name.
class Post < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
belongs_to :creator, polymorphic: true
mapping do
indexes :id, type: 'integer'
indexes :title, boost: 10
indexes :creator do
indexes :first_name
indexes :service_type
end
end
end
When I run search, there are no results:
Post.tire.search(load: true, page: params[:page], per_page: params[:per_page]) do
query do
match 'creator.first_name', 'Matt'
end
end
Even when I've run rake environment tire:import:all FORCE=true and verified that a Post exists with a creator that has a first name of 'Matt' in the database.
Is there something I am missing? Thanks!
UPDATE:
Adding the following to Post.rb did the trick:
def to_indexed_json
as_json.merge(
creator: creator.as_indexed_json
).to_json
end
Apparently, you need to specify the association in the to_indexed_json to make it work.
Related
Hi I am using retire and elasticsearch in a rails project to index my users and an additional model special_codes. My goal is to add an index to my Users model so that I when I search on Users the new index(special_code) will provide hits.
class User < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
has_one :specialcode
after_create :generate_specialcode
mapping do
indexes :email, :type => 'string'
indexes :specialcode do
indexes :code, :type => 'string'
end
end
def to_indexed_json
to_json( include: { specialcode: {only: [:code]} })
end
private
def generate_specialcode
Specialcode.create(code: 'derp', user_id: self.id)
self.tire.update_index #not really needed(see after_save), just here for example.
end
end
class Specialcode < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
belongs_to :user
after_save :update_user_index
private
def update_user_index
self.user.tire.update_index
end
end
I'd like to see User.search('derp') bring back a user hit because I've indexed the special code in with the user. I've tried quite a bit with mappings and updating indexes without getting any hits. The example above hopefully provides a base to work from. Thanks
Update
I found the solution with the help of Bruce. I've added mapping, to_indexed_json, and update_user_index to the code above.
I suppose you have to_indexed_json and mappings in User model already? Try use after_touch callback to update index in user.
I'm trying to index a model when I have a has_many, :through association, but no results are being displayed.
class Business < ActiveRecord::Base
include Tire::Model::Search
include Tire::Model::Callbacks
def self.search(params)
tire.search(load: true) do
query { string params[:q]} if params[:q].present?
end
end
mapping do
indexes :service_name
indexes :service_description
indexes :latitude
indexes :longitude
indexes :services do
indexes :service
indexes :description
end
end
def to_indexed_json #returns json data that should index (the model that should be searched)
to_json(methods: [:service_name, :service_description], include: { services: [:service, :description]})
end
def service_name
services.map(&:service)
end
def service_description
services.map(&:description)
end
has_many :professionals
has_many :services, :through => :professionals
end
Then this is Service model
class Service < ActiveRecord::Base
attr_accessible :service, :user_id, :description
belongs_to :professional
belongs_to :servicable, polymorphic: true
end
I've also reindex using this:
rake environment tire:import CLASS=Business FORCE=true
I can search for the items in Business, but when I tried to search something in Service, I get an empty result.
After struggling with mapping, I created a gem to make search a bit easier. https://github.com/ankane/searchkick
You can use the search_data method to accomplish this:
class Business < ActiveRecord::Base
searchkick
def search_data
{
service_name: services.map(&:name),
service_description: services.map(&:description)
}
end
end
I do not believe there is a way to do mapping on associations with Tire. What you will want to do instead is define easily searchable fields with the :as method and a proc. This way you can also get rid of the to_indexed_json method (you will actually need to)
mapping do
indexes :service_name
indexes :service_description
indexes :latitude
indexes :longitude
indexes :service_name, type: 'string', :as => proc{service_name}
indexes :service_description, type: 'string', :as => proc{service_description}
end
Tire can associate with associations, I've used it to index on has_many association but have not tried has_many, :through yet. Try index on object?
mapping do
indexes :service_name
indexes :service_description
indexes :latitude
indexes :longitude
indexes :services, type: 'object',
properties: {
service: {type: 'string'}
description: {type: 'string'}
}
end
Also, it might be good to have a touch method :
class Service < ActiveRecord::Base
attr_accessible :service, :user_id, :description
belongs_to :professional, touch: true
belongs_to :servicable, polymorphic: true
end
and after_touch callback to update the index.
I am using Rails 3 with mongoid 2. I have a mongoid class forum, which embeds_many topics.
Topics embeds_many forumposts
When I try to save a forumpost doing the following in my controller...
#forum = Forum.find(params[:forum_id])
#forum.topics.find(params[:topic_id]).forumposts.build(:topic_id => params[:forumpost][:topic_id], :content => params[:forumpost][:content], :user_id => current_user.id,:posted_at => Time.now, :created_at => Time.now, :updated_at => Time.now)
if #forum.save
On save I get...
undefined method `each' for 2012-11-14 23:15:39 UTC:Time
Why am I getting that error?
My forumpost class is as follows...
class Forumpost
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
field :content, type: String
field :topic_id, type: String
field :user_id, type: String
field :posted_at, type: DateTime
attr_accessible :content, :topic_id, :user_id, :posted_at, :created_at, :updated_at
validates :content, presence: true
validates :topic_id, presence: true
validates :user_id, presence: true
belongs_to :topic
belongs_to :user
end
There is alot wrong/wierd with your example code, so lets see if we can start at the start:
You say forum embeds many topics, which embeds many posts. But your model is using a belongs_to association. Belongs_to is used for references which are different than embedded documents. If your Topic model has this:
class Topic
...
embeds_many :forumposts
...
end
Then your Forumpost model should have this:
class Forumpost
...
embedded_in :topic
...
end
Read up on references vs embedded documents here: http://mongoid.org/en/mongoid/docs/relations.html
Next point, You don't need to put :topic_id into the forumpost since you are building it off the topic.
Next point, don't save the forum, save the forumpost. And instead of doing a build followed by a save, try just doing it as a create in one go.
Next point, instead of setting user_id => current_user.id, try setting user => current_user. This is the magic that the belongs_to association provides... its cleaner and avoids messing around with IDs.
Next point, why do you need both created_at (supplied by Mongoid::Timestamps) and posted_at ?
Last point, you shouldn't need to set the timestamps, they should be set automatically when created/updated (unless for some reason you actually need posted_at).
Try something more like this:
#forum = Forum.find(params[:forum_id])
#topic = #forum.topics.find(params[:topic_id])
if #topic.forumposts.create(:content => params[:forumpost][:content], :user => current_user)
#handle the success case
else
#handle the error case
end
In my app I am using Sunspot for a fulltext search. The problem is that I want to have sorting by association model field. In my case:
class Movie < ActiveRecord::Base
attr_accessible :description, :genre, :name
has_many :premieres
end
and my Premier model has:
belongs_to :movie
Searching by movie name is done by defining the method:
def movie_name
movie.name
end
but when I try to do:
order_by :movie_name, :asc
It says:
No field configured for Premiere with name 'movie_name'
How do I make this sorting available?
you can do it like this
searchable auto_index: true do
text :movie_name do
if self.movie.present?
self.movie.name
end
end
end
then you can use
order_by :movie_name, :asc
I have following classes in a Rails app:
class Post
include Mongoid::Document
include Sunspot::Mongoid
belongs_to :user
searchable do
text :name
string :user_id
end
end
class Post::Image < Post
searchable do
text :location
time :taken_at
end
end
class Post::Link < Post
searchable do
text :href
end
end
As far as I know, calling following should just work.
Post.search do
fulltext('something')
with(:user_id, user.id)
end
But it does not. It returns an empty result. Calling Post::Link.search does also not work:
# => Sunspot::UnrecognizedFieldError: No field configured for Post::Link with name 'user_id'
Any suggestions?