Last active
April 13, 2025 08:50
-
-
Save mamantoha/9c0aec7958c7636cebef to your computer and use it in GitHub Desktop.
Rails API Filtering and Sorting
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/models/experience.rb | |
# | |
# == Schema Information | |
# | |
# Table name: experiences | |
# | |
# id :integer not null, primary key | |
# title :string | |
# description :text | |
# created_at :datetime not null | |
# updated_at :datetime not null | |
# city_id :integer | |
# price :integer default(0) | |
# distance :integer | |
# duration :integer | |
# | |
class Experience < ActiveRecord::Base | |
belongs_to :city | |
has_many :categorizations | |
has_many :categories, through: :categorizations | |
scope :by_city, -> (city_ids) { where(city_id: city_ids) } | |
scope :by_price, -> (from, to) { where("price >= ? AND price <= ?", from, to) } | |
scope :by_duration, -> (from, to) { where("duration >= ? AND duration <= ?", from, to) } | |
scope :by_distance, -> (from, to) { where("distance >= ? AND distance <= ?", from, to) } | |
scope :by_category, -> (category_ids) { joins(:categories).where(categories: { id: category_ids }) } | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/serializers/experience_serializer.rb | |
class ExperienceSerializer < ActiveModel::Serializer | |
attribute :id | |
attribute :title | |
attribute :description | |
attribute :price | |
attribute :distance | |
attribute :duration | |
belongs_to :city | |
has_many :categories | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/controllers/api/v1/experiences_controller.rb | |
class Api::V1::ExperiencesController < Api::V1::BaseController | |
include Orderable | |
before_filter :authenticate_user! | |
# Filters: | |
# /api/v1/experiences?by_price[from]=100&by_price[to]=999 | |
# /api/v1/experiences?by_category=1,2,3 | |
# /api/v1/experiences?by_city=1,2,3 | |
# /api/v1/experiences?by_duration[from]=10&by_duration[to]=60 | |
# | |
has_scope :by_category, only: :index | |
has_scope :by_city, only: :index | |
has_scope :by_price, using: [:from, :to], only: :index | |
has_scope :by_duration, using: [:from, :to], only: :index | |
has_scope :by_distance, using: [:from, :to], only: :index | |
# GET /api/v1/experiences | |
def index | |
@experiences = | |
apply_scopes(Experience) | |
.order(ordering_params(params)) | |
.includes(:city, :user, :categories) | |
.all | |
render json: @experiences | |
end | |
end | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/controllers/concerns/orderable.rb | |
module Orderable | |
extend ActiveSupport::Concern | |
module ClassMethods | |
end | |
# A list of the param names that can be used for ordering the model list | |
def ordering_params(params) | |
# For example it retrieves a list of experiences in descending order of price. | |
# Within a specific price, older experiences are ordered first | |
# | |
# GET /api/v1/experiences?sort=-price,created_at | |
# ordering_params(params) # => { price: :desc, created_at: :asc } | |
# Experience.order(price: :desc, created_at: :asc) | |
# | |
ordering = {} | |
if params[:sort] | |
sort_order = { '+' => :asc, '-' => :desc } | |
sorted_params = params[:sort].split(',') | |
sorted_params.each do |attr| | |
sort_sign = (attr =~ /\A[+-]/) ? attr.slice!(0) : '+' | |
model = controller_name.classify.constantize | |
if model.attribute_names.include?(attr) | |
ordering[attr] = sort_order[sort_sign] | |
end | |
end | |
end | |
ordering | |
end | |
end |
Hi. Using the structure you have, if you want to include the name of the city in the json and use the name as a parameter to order the result, that method does not work or yes?
There is an edge-case in the Orderable#ordering_params
processing. If a client submits a URL with a literal plus sign +
(not encoded to %2B
) then it will be decoded as a space " "
. If you wish to catch that case, here's the diff.
# A list of the param names that can be used for ordering the model list
def ordering_params(params)
ordering = {}
if params[:sort]
- sort_order = { "+" => :asc, "-" => :desc }
+ sort_order = { " " => :asc, "+" => :asc, "-" => :desc }
sorted_params = params[:sort].split(",")
sorted_params.each do |attr|
- sort_sign = attr =~ /\A[+-]/ ? attr.slice!(0) : "+"
+ sort_sign = attr =~ /\A[ +-]/ ? attr.slice!(0) : "+"
model = controller_name.classify.constantize
if model.attribute_names.include?(attr)
ordering[attr] = sort_order[sort_sign]
end
end
end
ordering
end
@beporter it doesn't needed. sort_sign
will be "+" by default
@mamantoha Yes, the sign will be +
by default, but my diff covers the case where the client sends an unencoded +
(instead of a URL encoded %2B
). Consider these two request URLs:
http://my.server.com/route/action?sort=%2Bcreated_at
(The%2B
gets decoded as a plus+
)http://my.server.com/route/action?sort=+created_at
(The+
gets decoded as a space" "
)
My diff covers the second case.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
really nice man! exactly what I was searching for!