Skip to content

Commit 00f3856

Browse files
igordepollitimoschillingrafaelfranca
authored
[ActiveRecord] Add option filter on in_order_of (rails#51761)
* Add option on * Add CHANGELOG and fix method doc * Rename option to 'filter' and fix grammar * Adjust filter attribute * Fix typo and solve conflict * Update activerecord/test/cases/relation/field_ordered_values_test.rb Co-authored-by: Timo Schilling <[email protected]> --------- Co-authored-by: Timo Schilling <[email protected]> Co-authored-by: Rafael Mendonça França <[email protected]>
1 parent 4813a0f commit 00f3856

File tree

3 files changed

+38
-11
lines changed

3 files changed

+38
-11
lines changed

activerecord/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
* Add a `filter` option to `in_order_of` to prioritize certain values in the sorting without filtering the results
2+
by these values.
3+
4+
*Igor Depolli*
5+
16
* Fix an issue where the IDs reader method did not return expected results
27
for preloaded associations in models using composite primary keys.
38

activerecord/lib/active_record/relation/query_methods.rb

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,16 @@ def order!(*args) # :nodoc:
701701
# # WHEN "conversations"."status" = 0 THEN 3
702702
# # END ASC
703703
#
704-
def in_order_of(column, values)
704+
# +filter+ can be set to +false+ to include all results instead of only the ones specified in +values+.
705+
#
706+
# Conversation.in_order_of(:status, [:archived, :active], filter: false)
707+
# # SELECT "conversations".* FROM "conversations"
708+
# # ORDER BY CASE
709+
# # WHEN "conversations"."status" = 1 THEN 1
710+
# # WHEN "conversations"."status" = 0 THEN 2
711+
# # ELSE 3
712+
# # END ASC
713+
def in_order_of(column, values, filter: true)
705714
model.disallow_raw_sql!([column], permit: model.adapter_class.column_name_with_order_matcher)
706715
return spawn.none! if values.empty?
707716

@@ -711,16 +720,20 @@ def in_order_of(column, values)
711720
values = values.map { |value| model.type_caster.type_cast_for_database(column, value) }
712721
arel_column = column.is_a?(Arel::Nodes::SqlLiteral) ? column : order_column(column.to_s)
713722

714-
where_clause =
715-
if values.include?(nil)
716-
arel_column.in(values.compact).or(arel_column.eq(nil))
717-
else
718-
arel_column.in(values)
719-
end
723+
scope = spawn.order!(build_case_for_value_position(arel_column, values, filter: filter))
720724

721-
spawn
722-
.order!(build_case_for_value_position(arel_column, values))
723-
.where!(where_clause)
725+
if filter
726+
where_clause =
727+
if values.include?(nil)
728+
arel_column.in(values.compact).or(arel_column.eq(nil))
729+
else
730+
arel_column.in(values)
731+
end
732+
733+
scope = scope.where!(where_clause)
734+
end
735+
736+
scope
724737
end
725738

726739
# Replaces any existing order defined on the relation with the specified order.
@@ -2091,12 +2104,13 @@ def order_column(field)
20912104
end
20922105
end
20932106

2094-
def build_case_for_value_position(column, values)
2107+
def build_case_for_value_position(column, values, filter: true)
20952108
node = Arel::Nodes::Case.new
20962109
values.each.with_index(1) do |value, order|
20972110
node.when(column.eq(value)).then(order)
20982111
end
20992112

2113+
node = node.else(values.length + 1) unless filter
21002114
Arel::Nodes::Ascending.new(node)
21012115
end
21022116

activerecord/test/cases/relation/field_ordered_values_test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,12 @@ def test_in_order_of_with_associations
109109
books = Book.joins(:author).in_order_of(:"authors.name", order)
110110
assert_equal(order, books.map { |book| book.author.name })
111111
end
112+
113+
def test_in_order_of_with_filter_false
114+
order = [3, 4, 1]
115+
posts = Post.in_order_of(:id, order, filter: false)
116+
117+
assert_equal(order, posts.limit(3).map(&:id))
118+
assert_equal(11, posts.count)
119+
end
112120
end

0 commit comments

Comments
 (0)