Reputation:
I am building an API that is using Sunspot to act as the search engine on the backend and implement a faceted search. I have the following model for how I am doing the many different facet types for all of my content:
Contents
content_name
searchable_fields
Facets
facet_name
ContentFacets
content_id
facet_id
Where Content has_many facets though content_facets. I have managed to get everything set up to get this to work as a single faceted search - as the API it returns everything it needs to and when the front-end application sends back the necessary parameters it will drill down by that one facet, however, I can't get it to do multiple facets from there. Here is the search I have doing that (again, this is working):
#SearchController
facet_id = Facet.find_by_name(params[:commit]).id
@search = Content.search do
fulltext params[:search]
with(:facet_ids, facet_id) if params[:commit].present?
facet(:facet_ids)
paginate :page => 1, :per_page => 300
end
facets = @search.facet(:facet_ids).rows.map { |row| row.instance.name }
render :json => [facets, @search.results]
And then the Content Model for how it is configured:
searchable do
text :searchable_fields
integer :facet_ids, :multiple => true, :references => Facet
end
And lastly, here's how it is handled on the front-side application that is querying my API: (It would appear as if the previous parameters are being passed back as parameters in the route itself, while the :commit param from the submit_tag is what is determining the specific facet name)
<% if @facets %>
<div id="facets">
<h3>Given - <%= @search_params %></h3>
<ul>
<% @facets.each do |facet| %>
<%= form_tag facet_path(@search_params), method: "POST" do %>
<li><%= submit_tag facet %></li>
<% end %>
<% end %>
</ul>
</div>
So my question is this: Given everything what would be the best way for me to make this into an actual multi faceted search, rather than just using the one facet and overwriting it every time a new one is selected?
Upvotes: 1
Views: 177
Reputation: 1305
With your particular problem, the following approach should work -
In your view simply add this line above the submit_tag line
<li><%= hidden_field_tag :existing_facets, @existing_facets %></li>
Then before you make your call to the API have a chunk of code like so:
@existing_facets = ""
unless facet_params[:existing_facets].nil?
@existing_facets = "#{facet_params[:existing_facets]}+#{facet_params[:commit]}"
end
This will keep track of all of the facets that a User has selected for a specific query, while still keeping the original search parameters in there as well - these are being stored in order in a string for ease of use, separated by a + sign.
So now on the API's side I would change your search code to the following:
facets = gather_facets(params[:existing_facets], params[:commit])
@search = Content.search do
fulltext params[:search]
facets.each do |facet|
facet_id = Facet.find_by_name(facet).id
with(:facet_ids, facet_id)
end
facet(:facet_ids)
paginate :page => 1, :per_page => 300
end
facets = @search.facet(:facet_ids).rows.map { |row| row.instance.name }
render :json => [facets, @search.results]
def gather_facets(existing_str, new_facet)
arr = existing_str.scan(/([^+]+)/).flatten << new_facet
end
This will keep track of all of the chosen facets, query them in order, and narrow itself down before the issue of iterating in a search query causes the API to slow down (assuming the facets are done properly :P)
Upvotes: 1