Search code examples
ruby-on-railsruby-on-rails-3sunspot-railssunspot-solr

sunspot solr how to search multiple models correctly? All examples online fail


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!


Solution

  • 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.