How would one correctly search multiple models in SunSpot Solr?
Profile model
has_one :match
searchable do
string :country
string :state
string :city
end
Match model
belongs_to :profile
searchable do
string :looking_for_education
integer :age_from
integer :age_to
end
ProfilesController#Index
def index
@search = Sunspot.search Profile, Match do
with(:country, params[:country])
with(:state, params[:state])
with(:looking_for_education, params[:looking_for_education]) <= from the 2nd model
end
@profiles = @search.results
end
This fails with:
Using a with statement like
with(:age).between(params[:age_from]..params[:age_to])
undefined method `gsub' for nil:NilClass
Removing the
with(:age).between(params[:age_from]..params[:age_to]) line then it tries to
then it tries to load the
view app/views/educators/educator.html.haml
which does not exist ( im only using
/app/views/profiles/_profile.html.haml
to show profiles
EDIT #1:
What are good opensource projects in ruby on rails that use sunspot and solr in a bit more advanced way to have a look at? Maybe I can find the answer there. Any answer in this direction will also be accepted the bounty if it yields in resulting this issue, thx!
The method you've found for searching multiple models is correct. However, it appears that the meaning of your search is not what you intended. It looks as if you're trying to say:
Give me all Profile
records with these country
and state
values, and whose Match
record has this looking_for_education
value
Your search, however, says:
Give me all records of type Profile
or Match
that have all of these country
, state
and looking_for_education
values
Because neither Profile
nor Match
have all of these fields in their respective searchable
blocks, no single record can match the conditions you specify.
If I'm correct about your intended behaviour above, then you need to include the profile's associated match information in the profile's searchable
block, like so:
class Profile < ActiveRecord::Base
has_one :match
searchable do
string(:country)
string(:state)
string(:city)
string(:looking_for_education) { match.looking_for_education }
integer(:age_from) { match.age_from }
integer(:age_to) { match.age_to }
end
end
Here, we've told Sunspot to index properties of the profile's match association as if they lived on the profile itself. In the respective blocks, we've told Sunspot how to populate these values when the profile is indexed.
This will allow you to write your search using only the Profile
model:
def index
@search = Sunspot.search Profile do
with(:country, params[:country])
with(:state, params[:state])
with(:looking_for_education, params[:looking_for_education])
with(:age).between(params[:age_from]..params[:age_to])
end
@profiles = @search.results
end
This search will return only Profile
records, while still reflecting the properties of each profile's match association, because we stored them when the profile was indexed.
Note that this increases complexity when you index your models. If a Match
record changes, its associated profile now needs to be reindexed to reflect those changes.
This is what i am doing when i have to search for multiple models
Sunspot.search [Model1, Model2] do
....
end
@moises-zaragoza answered correctly your question but you have more issues than you think with what you want to do.
The first error:
Using a with statement like
with(:age).between(params[:age_from]..params[:age_to])
undefined method `gsub' for nil:NilClass
Is most likely produced because params[:age_from] and/or params[:age_to] are nil. I can't assure it because you haven't shown the stacktrace. You can fix by filter only when they are present:
with(:age).between(params[:age_from]..params[:age_to]) if params[:age_from].present? and params[:age_to].present?
The second error
Related to your views. I am assuming you are rendering a collection or object with one of the rails helper partial object helpers, without specifying the partial (again, without the code this is more of a good guess than anything else):
<%= render @results %>
or
<% @results.each do |result| %>
<%= render result %>
<% end %>
When Rails does not have a partial specified, it guesses the partial name depending on the object type. In this case, if your object is for example of class Educator, which might be a subclass of Profile, Rails will look for the partial 'educators/_educator.html.erb. Make sure you render the proper partial depending on the object type and this will ensure you render what you want.
Here the answer for search different model on matching string using with
searchable(:auto_index => AppConfig.solr.auto_index) do
string :category_name, :stored => true
text :content, :stored => true
text :title
string :company_id, :stored => true
time :published_on
end
search do |q|
if params[:keyword].present?
q.fulltext params[:keyword] do
fields(:deal_data)
end
end
if (ids = params["company_id"]).present?
ids = ids.split(",")
q.with(:company_id,ids) #here company id formate should be ["***","***"]
end
end