DaveG
DaveG

Reputation: 1203

Select multiple facets or filter data simultaneously

UPDATED 6/29/12

I have managed to set up a search and a side bar for filtering results of a search using Sunspot and act-as-taggable with Rails. I was following this tutorial here but I still can't select mutliple filter's at once. When I select a filter the other sub-category names still disappear. What am I missing?

My Original Question was this:

I'm not quite sure how to select multiple facets at once filtering the data. So I have multiple sub categories i.e.(hiking, skiing, climbing, etc.) I want to be able to select hiking and climbing simultaneously so the data shown is only those objects of hiking and climbing. Right now I select one (let's say hiking) and all the other options disappear. Can someone explain?

Below is my code:

Gear Controller

  def index
    @search = Gear.solr_search do
        exclusions = []
        fulltext params[:search]
        exclusions << with(:sub_category_name, params[:name]) if params[:name].present?
        exclusions.compact!
        exclusions = nil if exclusions.empty?
        facet :sub_category_name
        facet :sub_category_name, :exclude => exclusions, :name => :all_categories
        paginate(page: params[:page], :per_page => 15)
    end

    @gears = @search.results
  end 

** Gear Index View**

<div class="gears_container">
    <div class="side_bar_search">
        <%= form_tag gears_path, :method => :get do %>
          <p>
            <%= text_field_tag :keywords, params[:keywords] , class: 'gearsearchbar' %> <%= submit_tag "Search", :name => nil, class: 'btn btn-inverse gearsearchbutton'  %>

         </p>
        <% end %>

    <div class="sidebar_section">Sub Category</div>
    <ul>
    <% for row in @search.facet(:all_categories).rows %>
      <li class="sidebar_options">
        <% if params[:name].present? %>
          <strong><%= row.value %></strong>(<%= link_to "remove", :name => nil %>)
        <% else %>
          <%= link_to row.value, :name => row.value %> (<%= row.count %>)
        <% end %>
      </li>
    <% end %>
    </ul>
</div>
<div c
    <div class="search_results_gear">   
        <% @hits.each do |gear| %>
            <%= render partial: 'gear', locals: {gear: gear} %>
        <% end %>
        <div style="clear:both"></div>
     <%= will_paginate @hits, class: 'flickr_pagination' %>
         </br>
    </div>
</div>

Gear Model

class Gear < ActiveRecord::Base
  attr_accessible :title, :size, :price, :sub_category_id, :user_id, :image, :image_a, :remote_image_url, :color, :year, :latefee, :cancellation, :minrental, :policy, :about, :address, :city, :state, :zip, :sub_category_name
  belongs_to :user
  belongs_to :sub_category
  has_one :category, :through => :sub_category
  has_many :comments, :dependent => :destroy 
  has_many :line_items
  require 'carrierwave/orm/activerecord'
  mount_uploader :image, GearpicUploader
  mount_uploader :image_a, GearpicUploader
  before_destroy :ensure_not_referenced_by_any_line_item
  acts_as_taggable_on :tags

  validates :title, presence: true
  validates :size,  presence: true
  validates :price, presence: true
  validates :sub_category_id, presence: true
  validates :user_id, presence: true

  searchable do
     text :title, :size, :price, :year, :zip, :state, :city, :minrental, :about, :latefee, :color

     text :user_firstname do
          user.firstname
     end

     text :user_lastname do
          user.lastname
     end
     # **Facet Section**   

     string :size, :price, :year, :zip, :state, :city, :minrental, :latefee, :color 

     string :sub_category_name , :multiple => true, :stored => true do
       sub_category.name
     end

     string :category_name do
       category.name
     end
   end

   private
   def ensure_not_referenced_by_any_line_item
     if line_items.empty?
      return true
      else
      errors.add(:base, 'Line Items present')
      return false
     end
   end

end

Upvotes: 4

Views: 1444

Answers (1)

vanhowen
vanhowen

Reputation: 153

It looks like you need to take advantage of the :exclude option for your faceted search.

def index
  @search = Gear.search do
    exclusions = []
    # tags, AND'd        
    if params[:tag].present?
      all_of do
        params[:tag].each do |tag|
          exclusions << with(:sub_category_name, tag)
        end
      end
    end
    exclusions.compact!
    exclusions = nil if exclusions.empty?
    facet :sub_category_name
    facet :sub_category_name, :exclude => exclusions, :name => :all_categories
    paginate(page: params[:page])
  end
  @hits = @search.results
end

So what I've done above, is create an exclusions array to hold the collection of filters. The with() method returns a filter object, so we capture all the filters in the exclusions array. We do the compact! to remove nil objects from the array, and if it's empty, we make it nil (this is due to the limitations of the :exclude option, if you pass it an empty array, it will not work correctly). We then facet on the same :sub_category_name term, excluding the filters, and also give it a name. This facet will give you all the facet categories excluding the selected ones.

Then in your view, when you display the facet options, you use the named :all_categories facet.

@search.facet(:all_categories).rows.each_with_index do |facet, index|
  # This just outputs all the facets, you can add the logic in.
  <li><%= facet.value %> (<%= facet.count %>)</li>
end

Upvotes: 3

Related Questions