Querying multiple models with different attributes using Sunspot

ruby-on-rails, sunspot, sunspot-rails, sunspot-solr

Solution

After a lot more Googling and some trial and error I finally figured it out. Here's a heavily annotated version of the code I ended up with:

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

Problem

I'm using Sunspot to index and search several models in a Rails project and I need to limit results based on the models' HABTM associations with a `Department` model. This is because users may not have permission to see records in all departments so results from those departments shouldn't be returned. Here are the important parts of two of the 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's the search controller code: ``` 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 a user may be able to read documents in Department A and Department B but they should only see messages in Department B. Is there a way to apply the `with` scope to a specific model in a multi-model search? Or is there another way of doing this that I'm missing?

Original source