All threads / Searchkick Aggregation for has_many_through association

Ask A Question

Notifications

You’re not receiving notifications from this thread.

Searchkick Aggregation for has_many_through association

Lubomir Herko asked in Gems / Libraries

Hey guys,

I have an ecommerce product catalogue, where I store products, product options and option values in this kind of fashion:

# models/product.rb
has_many :option_values
has_many :option_types, through: :option_values

# models/option_value.rb
belongs_to :option_type
belongs_to :product

#models/option_type.rb
has_many :option_values
has_many :products

I came up with this structure so I can have for example a lamp Product that can be connected to OptionType titled "Power supply included" via value OptionValue set to true. Data in database for this product are saved as follows:

#products
id: 22
title: "A lamp"

#option_values
id: 1
option_type_id: 5
product_id: 22
value: true

#option_types
id: 5
title: "Power supply included"

Every ProductCategory has its own specific set of option values, that's why I came up with this structure, it is inspired by Spotify architecture and I will need this in search/aggregates/filters, what leads me to my question:

How can i create aggregate search so I can select all products, that have "Power supply included" set to true? Do I need nested aggregates, or do I setup search_data in some fashion so I can access them via aggs?

Searchkick supports the complete Elasticsearch https://vidmate.bet/ Search API. As your search becomes more advanced, we recommend you use the Elasticsearch DSL for maximum flexibility.

I have been playing happy wheels. This is my favorite game ever..

Hi, I am solving very similar problem. And I think I crack it =)
For your product model you need add to search_data this

# product.rb
def search_data
  {
    option_value_id: option_values.map(&:id),
    option_type_option_value_id: option_values.map{ |option_value| option_value.option_type.id.to_s + ";" + option_value.id.to_s }
  }
end

option_type_option_value_id its a little hacky way that will help us to aggregate.
We take option_type id (like 5) and combine it with option_value id (like 10) and delimiter ; so we get something like this '5;10'
Elasticsearch will aggregate that string.

In your controller where you want to display catalog add to method

option_value_ids = array_of_option_value_ids_from_params

search_params = {}
search_params = search_params.merge(option_value_id: {all: option_value_ids}) if option_value_ids.present?

# Here we aggregate option_type_option_value_id and search with sended option values, I have pagination too
@products = Product.search "*", aggs: { option_type_option_value_id: {} }, where: search_params, page: params[:page], per_page: 25

# Here we are creating an object with option_type_id, option_value_id and count, we use this in our view
@aggs_products = @products.aggs["option_type_option_value_id"]["buckets"].map { |bucket| { option_type_id: bucket["key"].split(';')[0].to_i, option_value_id: bucket["key"].split(';')[1].to_i, count: bucket["doc_count"] } }

Now last piece the view.
Here we iterate all option types and control if we have it in our @aggs_products hash
If is there we then iterate over option type option values to test if the value is in our @aggs_products hash
If value is there we print title and count for this option (you could make it as link_to a send it as parameter to our method from above)

<% OptionType.all.each do |option_type| %>
<% if @aggs_products.any? { |h| h[:option_type_id] == option_type.id } %>
  <div class="option-type">
    <h3 class="option-type__title"><%= option_type.title %></h3>
    <ul>
      <% option_type.option_values.each do |option_value| %>
      <% if @aggs_products.any? { |h| h[:option_value_id] == option_value.id } %>
      <li>
        <%= option_value.title %> <span class="option-type__option-count">(<%= @aggs_products.detect {|c| c[:option_value_id] == option_value.id }[:count] %>)</span>
      </li>
      <% end %>
      <% end %>
    </ul>
  </div>
<% end %>
<% end %>
Join the discussion

Want to stay up-to-date with Ruby on Rails?

Join 53,130+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    logo Created with Sketch.

    Ruby on Rails tutorials, guides, and screencasts for web developers learning Ruby, Rails, Javascript, Turbolinks, Stimulus.js, Vue.js, and more. Icons by Icons8

    © 2021 GoRails, LLC. All rights reserved.