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).
