Skip to main content

Search Filters with ElasticSearch Aggregations Discussion

General • Asked by Chris Oliver
87e4cef5a91390550145caa081250466

This has been very helpful for a project I'm working on. Thank you. On mine, I wanted to add checkboxes to be able to select multiple keys. For anyone else looking to do the same, the solution I came up with is:

<%= form_with(method: :get, local: true, html: { class: 'query'}) do |form| %>
  <h6>Brand</h6>
  <% @televisions.aggs["brand"]["buckets"].sort_by{ |b| b["key"] }.each do |bucket| %>
    <%= check_box_tag "brand[]",
                      bucket["key"],
                      params["brand"].try(:include?, bucket["key"]),
                      id: "brand_#{bucket["key"].to_s.parameterize.underscore}" %>
    <%= label_tag "brand_#{bucket["key"].to_s.parameterize.underscore}", bucket["key"].to_s %>
    (<%= bucket["doc_count"] %>)
    <br>
  <% end %>

  <%= form.submit %>
<% end %>

In the check_box_tag, I added '[]' to the name so "brand[]" establishes an Array. Searchkick can use this to filter for any of the values in the array without changing anything Chris's controller code.

For the id value in the check_box_tag and for value in the label_tag, I used .to_s.parameterize.underscore. This removes any spaces that might be in the bucket key and then standardizes it to my other code with underscores.

In my example I added a submit button but I'll be removing that in production and adding a Stimulus controller to submit the form on click or do a little AJAX to just load the results.

I hope this helps anyone else diving into it and I'm definitely open to any suggestions to make the code better.

edit: I changed the code that states whether the check box should be selected to use the .try method. Before, an error would be raised if no values were selected. This way, it just provides a nil value and doesn't check that box if the params do not exist.

575b623f91468129567ad10ba7f160da

Hi Ken. Did you manage to keep the checkboxes checked after form submission??

87e4cef5a91390550145caa081250466

There was a mistake in my code. I had abstracted it out into a partial and when I rewrote the partial to match Chris's example, I wrote it wrong. My original example was looking into params["bucket"] and it should have been params["brand"].

Starting from the beginning, the third argument of the check_box_tag determines whether the check_box is selected or not. It'll take a true/false value, but you can substitute a nil value for the false. Orignally, I began with params["brand"].include? bucket["key"]. This will return a true/false value but will fail when the params["brand"] is not set, for example when first hitting the search page and no params are set because there is no .include? method on a nil class.

From there, I integrated the .try method, so if the params["brand"] supports the .include? method, it'll use it and return a true/false value. If the params["brand"] method doesn't support .include? then it'll return a nil value to the check_box_tag and the check box will not be selected.

The final code should read as: params["brand"].try(:include?, bucket["key"]). Note, on the .try method, the first argument is the method I'm calling and the second argument is the value I'm passing to the .include? method.

https://api.rubyonrails.org/v4.2/classes/Object.html#method-i-try


2140125e280426d3965997f01d9120ef

This is gold! Great video.
I have two questions.
Is there a way, for better SEO, to move filter params to URL?
Instead of /televisions?brand=Brick to /televisions/brand/brick ?
And second question, how to handle multiple values for attribute?
For example, if I have a color attribute for a television and the tv comes in three colors options 'Black', 'White' and 'Silver'.
Should I make three different televisions or save it as array? Or make another table or use something like hstore?
What is the best approach and how to use this filter with it?


4386b7a04aff14960661807afc01bdd3

This is best! Great video.
Is there a way, for better SEO, to move filter params to URL?


Aa042bc545e5f611f7904781cd4743b1

Great video thank you! I'm currently implementing aggregations in my apps :)
Aggregations sort could actually be made by elastisearch itself.
See in searchkick docs :

Product.search "wingtips", aggs: {color: {order: {"_key" => "asc"}}} # alphabetically

Ce795239ba5dd2384fc2f88ffaff5451

Sweet, thanks for sharing that! Makes a lot of sense to use any built-in functionality that you can with ElasticSearch. This is a good improvement.


Abee9ae5b0302a98ea238d8a092c5926

Is it possible to still use Aggregations when searching multiple models? Especially if each model doesn't have that particular value?

Ce795239ba5dd2384fc2f88ffaff5451

Looks like you can do that: https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-global-aggregation.html

Not sure if you can do it with Searchkick directly, so you'd have to ask them.

Abee9ae5b0302a98ea238d8a092c5926

I know I am reaching a little bit with my questions, but if all models DID have the same attributes, would I be able to setup aggregations just like in the video?


575b623f91468129567ad10ba7f160da

Hi Guys/Chris.

1.I am strungling to have this work in parallel with a search input. not sure how to write the query to avoid intereference with the buckets.

2.Also, similar question but this time with Pundit. Where to incorporate the policy_scope(Model) to allow restriction based on usage rights?

3) Last but not least, in the checkbox_tag used by Ken Beegle (above), how to you retain the checkbox value after submission...so that the user can understand what he/her has selected.

many thks in advance ;-)


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 18,000+ developers who get early access to new screencasts, articles, guides, updates, and more.

By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

More of a social being? We're also on Twitter and YouTube.