Search code examples
ruby-on-railssearchelasticsearchcontrollerenumerable

Gathering search results from a multi-model query


I'm currently indexing search queries using the searchkick gem. It should also be noted that I'm searching across two models, Artists and Albums. In my search_controller I have 3 instance variables, first being @results for returning all results across the two models for my autocomplete (used elsewhere):

@results = Artist.search(params[:query], index_name: [Artist.searchkick_index.name, Album.searchkick_index.name], fields: [{ name: :word_start }], limit: 10)

Then there are my @artists/@albums instance variables.

@artists = @results.select { |result| result.class.name == "Artist" }
@albums = @results.select { |result| result.class.name == "Album" }

I split these results up so that I can group artists and albums in my index view as so:

<h1>Search Results</h1>

<div class="artist-results">
  <h2>Artists</h2>

  <% @artists.each do |artist| %>
    <%= link_to artist.name, artist_path(artist) %>
  <% end %>
</div>

<div class="album-results">
  <h2>Albums</h2>

  <% @albums.each do |album| %>
    <%= link_to album.name, album_path(album) %>
  <% end %>
</div>

The one main concern I have with my search controller code is that I'm performing 2 almost identical enumerables. I could be wrong but something tells me that I shouldn't do this. Is there a more performant solution to what I'm doing? Am I on the right track with this?

Complete Controller Code

class SearchController < ApplicationController
  before_filter :set_results

  def index
  end

  def autocomplete
    render json: @results
  end

  private
    def set_results
      @results = Artist.search(params[:query], index_name: [Artist.searchkick_index.name, Album.searchkick_index.name], fields: [{ name: :word_start }], limit: 10)
      @artists = @results.select { |result| result.class.name == "Artist" }
      @albums = @results.select { |result| result.class.name == "Album" }
    end
end

Solution

  • Not sure it's more performant. But you could Use group by.

    results = Artist.search(params[:query], index_name: [Artist.searchkick_index.name, Album.searchkick_index.name], fields: [{ name: :word_start }], limit: 10)
    @grouped_results = results.group_by{|x| x.class.name }
    

    Then

    <h1>Search Results</h1>
    
    <% @grouped_results.each do |class_name, results| %>
      <div class="<%= class_name.downcase %>-results">
        <h2><%= class_name.pluralize %></h2>
    
        <% results.each do |result| %>
          <%= link_to result %>
        <% end %>    
      </div>
    <% end %>