Search code examples
javascriptjqueryruby-on-railssearchfaceted-search

Rails How to make a filter link


I am working on a faceted search in rails. It follows from my previous question: Rails: How to use facets with search results

What I am able to do is to use scopes to get all the facets with respect to the search results as shown in the figure: enter image description here

What I am currently working on is use the facets texts as a link to add extra params to the search result we already got.

I have two questions in regard to this:

  1. How to take existing search URL and add a link to add/merge params when a link is clicked. I am using params merge which is not working. The code is as below.
    <h3 style = "color:#7C064D; text-align: center;"><strong>CYLINDER</strong></h3>

    <%- @listings_cylinder.each do |key, value| -%>
        <h5 style = "color:#F00000; text-align: left;"><strong><%= link_to "#{key.upcase if key} (#{value})" , search_listings_path(params.merge(:cylinder => "#{key}")) %> </strong></h5>
    <% end %>
  1. How to use multiple filters at the same time. What I want is when a facet link is clicked, it should add a small filter link below the sorting link in the pic. When a user wants to undo that facet filter effect, he clicks the X on that small link and he gets to the search query without the filter. The same what happens on hotels.com or priceline.com as shown:

enter image description here

Note: I don't want to use solr/elaticsearch because it's not the best use case here. I want to learn it from scratch. I am already able to build facets texts with simple rails. With a little help, I will be able to do it.

Codes:

Scopes I am using:

    scope :listing_search_cities, -> {group(:city).count}
    scope :listing_search_body_type, -> {group(:bodytype).count}
    scope :listing_search_states, -> {group(:state).count}
    scope :listing_search_newused, -> {group(:NewUsed).count}
    scope :listing_search_transmission, -> {group(:transmission).count}
    scope :listing_search_cylinder, -> {group(:cylinder).count}
    scope :listing_search_fuel, -> {group(:fuel).count}
    scope :listing_search_drive, -> {group(:drive).count}
    scope :listing_search_trim, -> {group(:trim).count}
    scope :listing_search_color, -> {group(:color).count}
    scope :listing_search_year, -> {group(:year).count}
    scope :listing_search_interiorcolor, -> {group(:interiorcolor).count}

In the controller search action, I am using:

def search                  
    @listings = Listing.search(params)  
    @listings_cities = @listings.listing_search_cities  
    @listings_bodytype = @listings.listing_search_body_type
    @listings_states = @listings.listing_search_states

    @listings_newused = @listings.listing_search_newused
    @listings_cylinder = @listings.listing_search_cylinder
    @listings_transmission = @listings.listing_search_transmission
    @listings_drive = @listings.listing_search_drive
    @listings_trim = @listings.listing_search_trim

    @listings_fuel = @listings.listing_search_fuel

    @listings_color = @listings.listing_search_color
    @listings_interiorcolor = @listings.listing_search_interiorcolor
    @listings_year = @listings.listing_search_year

end

In view, I am using iteration through the key values like this:

<%- @listings_cylinder.each do |key, value| -%>
            <h5 style = "color:#F00000; text-align: left;"><strong><%= link_to "#{key.upcase if key} (#{value})" , search_listings_path(params.merge(:cylinder => "#{key}")) %> </strong></h5>
<% end %>

Solution

  • Not the solution, but some ideas (not tested):

    Pass all filters to the search method in a hash in params (such as params[:filters]).

    Define an instance variable with all the filters and load it in the search method in the controller.

    Filter the listing based on filters.

    def search
      @current_filters = params[:filters]
      @listings = Listing.all
      @listings = @listings.where(:cylinder => @current_filters[:cylinder]) if @current_filters[:cylinder]
      @listings = @listings.where(:fuel => @current_filters[:fuel]) if @current_filters[:fuel]
      #etc
    end
    

    In the view:

    <% @listings_cylinder.each do |key, value| %>
      <h5><%= link_to "#{key.upcase if key} (#{value})", search_listings_path(filters: @current_filters.merge(:cylinder => "#{key}")) %>
      </h5>
    <% end %>
    

    In the view, when showing current filters:

    Applied filters: 
    <ul>
      <% @current_filters.each do |key, value| %>
        <li><%= link_to value, search_listings_path(filters: @current_filters.except(key)) %>
        </li>
      <% end %>
    </ul>