2014-02-16 9 views
7

Я использую Sunspot для индексирования и поиска нескольких моделей в проекте Rails, и мне нужно ограничить результаты на основе ассоциаций HABTM моделей с помощью модели Department. Это связано с тем, что у пользователей может не быть разрешения на просмотр записей во всех отделах, поэтому результаты этих отделов не должны возвращаться.Запрос нескольких моделей с различными атрибутами с помощью Sunspot

Вот важные части двух моделей:

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 

А вот код поиска контроллера:

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 

Проблема заключается в том, что пользователь может читать документы в отделе A и Department B, но они должны видеть сообщения только в отделе B.

Есть ли способ применить область with к определенной модели в мультимодуле del search? Или есть другой способ сделать это, что я пропал?

ответ

9

После того, как появилось много ошибок в Google и некоторых проб и ошибок, я, наконец, понял это. Вот сильно аннотированный версия кода я закончил с:

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 
+1

Awesome !, Действительно хорошая находка. :) –

+1

Это хороший пример. Спасибо. – dps

+0

Мне было бы интересно, как делать блики между различными именами полей и разными моделями, используя этот синтаксис :-) – dps

0

Добавление к Симону ответа,

Я нашел еще один sceneario. Где вы хотите применить условие в одной модели и сопоставить другую только с полным текстом.

Моя первая попытка была эта

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 

Это дает мне только сообщение, но не тегов, так или иначе статус:: опубликовано применяется на категории и без каких-либо результатов, поступающих для тегов. Я попробовал и другие варианты.

Наконец-то я выяснил одно решение.

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 

Это решение сработало. Возможно, это может иметь какое-то другое решение. Тем не менее я доволен этим.