Rails : Ransack : Sorting data by ratings

Its not quite easily graspable to sort your search results by Ratings if you have separate Ratings table and there is an intermediate table called Job or Similar. Example Model schema is below for reference.

class Garage < ApplicationRecord
  has_many :jobs, through: :quote_requests
  has_many :ratings, through: :jobs
class Job < ApplicationRecord
  belongs_to :quote_request
  belongs_to :garage
  has_one :rating
# == Schema Information
# Table name: ratings
#  id         :integer          not null, primary key
#  star_count :integer
#  job_id     :integer
#  user_id    :integer
#  metadata   :jsonb
#  created_at :datetime         not null
#  updated_at :datetime         not null

class Rating < ApplicationRecord
  belongs_to :job
  belongs_to :reviewer, class_name: :VehicleOwner, foreign_key: :user_id

I suppose, you have got the idea about the relationship amount the Models.


I found somewhere that I need to override Ransack to support custom sorting scopes like

# in config/initializers/ransack_override.rb
# A custom initializer that enables sorting via custom scopes in Ransack (like the same feature in MetaSearch)

module Ransack
  module Adapters
    module ActiveRecord
      class Context < ::Ransack::Context

        # Allows for sorting by custom scopes
        # Define your custom scopes in your model, e. g. sort_by_title_asc and sort_by_title_desc
        # (The scopes would sort by some calculated column or a column added via some crazy join, etc.)
        # In your sort links refer to the scopes like to standard fields, e. g.
        #   <%= sort_link(@q, :title, 'Crazy calculated title') %>
        def evaluate(search, opts = {})
          viz = Visitor.new
          relation = @object.where(viz.accept(search.base))
          if search.sorts.any?
            custom_scopes = search.sorts.select do |s|
              custom_scope_name = :"sort_by_#{s.name}_#{s.dir}"
            attribute_scopes = search.sorts - custom_scopes

            relation = relation.except(:order)

            custom_scopes.each do |s|
              custom_scope_name = :"sort_by_#{s.name}_#{s.dir}"
              relation = relation.public_send(custom_scope_name)

            relation = relation.reorder(viz.accept(attribute_scopes)) if attribute_scopes.any?
          opts[:distinct] ? relation.distinct : relation

In views

# This is slim template | index.slim
  = sort_link @q, :ratings do
    |  Rating

In Model [Garage]

# models/garage.rb
scope :sort_by_ratings_asc,  -> { joins(:ratings).group('id').order('AVG(ratings.star_count)')}
scope :sort_by_ratings_desc, -> { joins(:ratings).group('id').order('AVG(ratings.star_count) desc')}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s