I'm trying to write a scope that sorts on one of two columns--if the first (scheduled_at
) has data in it then use that data, otherwise resort to created_at
.
def self.by_scheduled_at
if scheduled_at
order(scheduled_at: :desc)
else
order(created_at: :desc)
end
end
This code fails because scheduled_at
is an instance attribute and this is a class method.
How can I write this scope so it sorts the collection based on whether scheduled_at
is present?
CodePudding user response:
I'm going to assume you mean to sort by whether created_at or sorted_at is present in each row. That is if sorted_at has a value, use that. But if it's nil, use created_at.
sorted_at | created_at |
---|---|
2020-01-01 | 2030-01-01 |
null | 2020-02-02 |
2020-03-03 | null |
2020-04-04 | 1999-01-01 |
For that you'd use coalesce
; it returns the first non-null value. Then write it as a scope.
scope :by_scheduled_at, -> { order("coalesce(scheduled_at, created_at) DESC") }
CodePudding user response:
If you want a chainable scope, something like this should work (tested on MySQL, via the mysql2
gem):
scope :by_scheduled_at, -> { order(Arel.sql("IFNULL(scheduled_at, created_at) DESC")) }
As noted by @Schwern, IFNULL
is MySQL specific, use COALESCE
for standard SQL (implemented also by SQLite and PostgreSQL).