Query multiple models with different attributes using Sunspot

I use Sunspot to index and search for multiple models in a Rails project, and I need to limit the results based on the associations of the HABTM models with the Department model. This is because users may not have permission to view records in all departments, so the results of these departments should not be returned.

Here are the important parts of the two models:

 class Message < ActiveRecord::Base has_many :comments, dependent: :destroy has_and_belongs_to_many :departments searchable do text :title, :body text :comments do comments.map(&:body) end date :created_at integer :department_ids, using: :department_ids, references: Department, multiple: true end end class Document < ActiveRecord::Base has_and_belongs_to_many :departments searchable do text :name date :created_at integer :department_ids, using: :department_ids, references: Department, multiple: true end end 

And here is the code of the search controller:

 class SearchController < ApplicationController def index # These arrays are created here for the sake of this example document_permitted_departments = [1, 2, 3] message_permitted_departments = [3, 4] search = Sunspot.search Document, Message do # This obviously doesn't work with(:department_ids, document_permitted_departments) with(:department_ids, message_permitted_departments) fulltext params[:q] paginate page: params[:page], per_page: SEARCH_RESULTS_PER_PAGE order_by :created_at, :desc end @results = search.results @number_of_results = search.total respond_to do |format| format.js format.html end end end 

The problem is that the user can read documents in departments A and B, but they should only see messages in department B.

Is there a way to apply the with scope to a specific model in multi-model search? Or is there another way to do this that I am missing?

+7
ruby-on-rails sunspot sunspot- solr sunspot-rails
source share
2 answers

After much more Google and some trial and error, I finally figured it out. Here's a highly annotated version of the code in which I ended up:

 class SearchController < ApplicationController before_filter :authenticate_user! def index # These arrays are created here for the sake of this example # Push 0 on to the end because empty arrays break the `with :department_ids` scopes below document_permitted_departments = [1, 2, 3].push(0) message_permitted_departments = [3, 4].push(0) search = Sunspot.search Document, Message do any_of do # Return anything that matches any of the scopes in this block all_of do # Return only those results that match these scopes with :class, Document # This limits scopes in this block to Document results with :department_ids, document_permitted_departments end all_of do # Return only those results that match these scopes with :class, Message # This limits scopes in this block to Message results with :department_ids, message_permitted_departments end end fulltext params[:q] paginate page: params[:page], per_page: SEARCH_RESULTS_PER_PAGE order_by :created_at, :desc end @results = search.results @number_of_results = search.total respond_to do |format| format.js # index.js.erb format.html # index.html.erb end end end 
+9
source share

Adding the answer to Simon,

I found another scenario. Where do you want to apply the condition in one model and match the other with the full text.

My first attempt was this

  def search_all(options) Sunspot.search(Post, Tag) do any_of do all_of do with :class, Post with(:status, :published) end end fulltext options[:search] group(:class) { limit 30 } end end 

This only gave me a message, but not a tag, somehow the status :: was published for the tag and no results for Tag. I tried other options.

Finally, I understood one solution.

 Sunspot.search(Post, Tag) do any_of do all_of do with :class, Post with(:status, :published) end # This might look weiered to put all_of without any filter, However # without the next all_of block, status: :published is applied in tags and no results appear for tags. # Meaning any_of is ignored if there is one nested block and Post all_of is applied for entire filter. all_of do with :class, Tag end end fulltext options[:search] group(:class) { limit 30 } end 

This solution worked. Perhaps this may have some other solution. Nevertheless, I am pleased with this.

0
source share

All Articles