Rails 7.1 Adds Support For Composite Key Multi-Column Ordering In ActiveRecord::Batches

ActiveRecord::Batches module provides methods like find_each, find_in_batches, and in_batches to process records in batches, reducing memory consumption.

By default, records are processed in ascending order by primary key(ID).

# default asc order

User.find_each do |user|
  puts user.id
end

User Load (0.4ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1000]]

1
2
3

Before

Before Rails 7.1, ActiveRecord::Batches methods - like find_each, find_in_batches, and in_batches supported ORDER BY the primary key (ID) in either ascending or descending order.

class User < ApplicationRecord
  self.primary_key = [:first_name, :last_name]
end
# Before Rails 7.1, trying to pass an array [:desc, :asc] to the order clause raised ArgumentError

User.find_each(order: [:asc, :desc]) do |user|
  puts "#{user.first_name} #{user.last_name}"
end

:order must be :asc or :desc, got [:asc, :desc] (ArgumentError)  

We could only specify a single sorting direction (:asc or :desc) as it only supported ORDER BY on single primary column.

This restriction made it impossible to sort by multiple columns, such as having one column in ascending order while sorting another column in descending order, as ActiveRecord::Batches did not support multi-column ordering.

After

Rails 7.1 adds support for batching using composite primary keys and multiple column ordering.

It allows selection of ascending or descending order for each key in composite primary key.

By default the order is set to ASC. Valid values for the ORDER BY clause are :asc or :desc or an array consisting of :asc or :desc and it’s case sensitive.

class User < ApplicationRecord
  self.primary_key = [:first_name, :last_name]
end
# Users in ascending order of first_name and descending order of last_name

User.find_each(order: [:asc, :desc]) do |user|
  puts "#{user.first_name} #{user.last_name}"
end

User Load (1.7ms)  SELECT "users".* FROM "users" ORDER BY "users"."first_name" ASC, "users"."last_name" DESC LIMIT $1  [["LIMIT", 1000]]

Book Keeper
Oliver Smith
Sam Smith
Sam Admin
Varun Agarwal

Passing anything other than :asc, :desc raises ArgumentError

raise ArgumentError, ":order must be :asc or :desc or an array consisting of :asc or :desc, got #{order.inspect}"

Need help on your Ruby on Rails or React project?

Join Our Newsletter