How use order by condition - ruby-on-rails

Is it possible to sort the data on a condition?
For example, figuratively, .order( "expiration_date <?", Time.current, :desc )

The ORDER BY keyword is used to sort the result-set by one or more columns.
The ORDER BY keyword sorts the records in ascending order by default. To sort the records in a descending order, you can use the DESC keyword.
you can sort by using something like :-
sorted = #records.sort_by &:created_at
or try where by first getting the resultset and then sorting it:-
#records.where( "expiration_date <?", Time.current).order( :created_at, :desc )
Moreover you can paginate easily;-
#records.paginate(:page => params[:page], :per_page => 15)#.order(" created_at ASC")

Related

How sorting works in ruby on rails? when there are multiple fields to sort in a single query

I have a model with the fields price, min_price,max_price, discount,in my product table. if I want to execute ascending descending orders, how that will get executed when we apply for an order on multiple fields. for example like below.
#products = Product.order("price asc").order("min_price desc").order("max_price asc").order("updated_at asc") (Query might be wrong but for reference im adding)
will it order as per the order sequence ?
If you append .to_sql to that, it will show the generated SQL so you can investigate yourself.
I tried a similar query:
Book.select(:id).order("id asc").order("pub_date desc").to_sql
=> "SELECT \"books\".\"id\" FROM \"books\" ORDER BY id asc, pub_date desc"
You might instead:
Book.select(:id).order(id: :asc, pub_date: :desc).to_sql
=> "SELECT \"books\".\"id\" FROM \"books\" ORDER BY \"books\".\"id\" ASC, \"books\".\"pub_date\" DESC"
... which you see adds the table name in, so is more reliable when if you are accessing multiple tables

Sorting record array twice while maintaining initial sort

so I have the following query for my companies
companies.where(industry: industries_queried)
I would like to sort them, so that the first records returned are the ones with plan_id == 3, then 2, then 1. (descendingly)
But then, I would also like to arrange each of these 4 parts, so that they are sorted alphabetically per name.
How would I go about doing this in Rails 5?
The Active Record Query Interface guide, gives us the following info on ordering records.
4 Ordering
To retrieve records from the database in a specific order, you can use
the order method.
For example, if you're getting a set of records and want to order them
in ascending order by the created_at field in your table:
Customer.order(:created_at)
# OR
Customer.order("created_at")
You could specify ASC or DESC as well:
Customer.order(created_at: :desc)
# OR
Customer.order(created_at: :asc)
# OR
Customer.order("created_at DESC")
# OR
Customer.order("created_at ASC")
Or ordering by multiple fields:
Customer.order(orders_count: :asc, created_at: :desc)
# OR
Customer.order(:orders_count, created_at: :desc)
# OR
Customer.order("orders_count ASC, created_at DESC")
# OR
Customer.order("orders_count ASC", "created_at DESC")
Applying this to your issue you would end up with:
companies.where(industry: industries_queried).order(plan_id: :desc, name: :asc)

Multiple order values for find_with_reputation

The following function sorts the businesses (relation) by votes descending (using the active record reputation system link to gem).
#businesses = Business.find_with_reputation(:votes, :all, order: "votes desc")
How would I add a secondary order value so that it sorts by votes but if votes are equal it sorts by created_at (oldest at the top)?
Is it simply:
#businesses = Business.find_with_reputation(:votes, :all, order: "votes desc, created_at desc")
I suspect that your proposed answer is going to result in a SQL error because 'created_at' probably occurs in both the reputation table and the Business table. To get around that, I'd suggest being specific on created_at.
Business.find_with_reputation(:votes, :all, order: "votes desc, businesses.created_at desc")
However, I'd suggest instead that you use the id column. DBs will be faster with INTs and the sort sequence should be the same.
Business.find_with_reputation(:votes, :all, order: "votes desc, businesses.id desc")

Order by two things in two different styles

Theoretically what I want is this:
:order => "column1 ASC, column2 DESC"
But column1 is actually not a column in my case but an aggregated value:
:order => relations.count
How can I order by two columns when one of those is actually an aggregated value?
Btw: Counter-Cache is not an option (because it is outdated when I need it)
:order => "COUNT(*), column2 DESC"
To order/group/etc by a calculated value, you have to specify it in the corresponding clause, in your case using a (possibly left) join too:
Something.select("somethings.*, COUNT(others.id)").
joins("LEFT JOIN others ON others.something_id = somethings.id").
group("somethings.*").
order("COUNT(others.id), column2 DESC")

How do I Order on common attribute of two models in the DB?

If i have two tables Books, CDs with corresponding models.
I want to display to the user a list of books and CDs. I also want to be able to sort this list on common attributes (release date, genre, price, etc.). I also have basic filtering on the common attributes.
The list will be large so I will be using pagination in manage the load.
items = []
items << CD.all(:limit => 20, :page => params[:page], :order => "genre ASC")
items << Book.all(:limit => 20, :page => params[:page], :order => "genre ASC")
re_sort(items,"genre ASC")
Right now I am doing two queries concatenating them and then sorting them. This is very inefficient. Also this breaks down when I use paging and filtering. If I am on page 2 of how do I know what page of each table individual table I am really on? There is no way to determine this information without getting all items from each table.
I have though that if I create a new Class called items that has a one to one relationship with either a Book or CD and do something like
Item.all(:limit => 20, :page => params[:page], :include => [:books, :cds], :order => "genre ASC")
However this gives back an ambiguous error. So can only be refined as
Item.all(:limit => 20, :page => params[:page], :include => [:books, :cds], :order => "books.genre ASC")
And does not interleave the books and CDs in a way that I want.
Any suggestions.
The Item model idea will work, but you are going to have to pull out all the common attributes and store them in Item. Then update all you forms to store those specific values in the new table. This way, adding a different media type later would be easier.
Update after comment:
What about a union? Do find_by_sql and hand-craft the SQL. It won't be simple, but your DB scheme isn't simple. So do something like this:
class Item < ActiveModel::Base
attr_reader :title, :genre, :media_type, ...
def self.search(options = {})
# parse options for search criteria, sorting, page, etc.
# will leave that for you :)
sql = <<-SQL
(SELECT id, title, genre, 'cd' AS media_type
FROM cds
WHERE ???
ORDER BY ???
LIMIT ???
) UNION
(SELECT id, title, genre, 'book' AS media_type
FROM books
WHERE ???
ORDER BY ???
LIMIT ???
)
SQL
items = find_by_sql(sql)
end
end
untested
Or something to that effect. Basically build the item rows on the fly (you will need a blank items table). The media_type column is so you know how to create the links when displaying the results.
Or...
Depending on how often the data changes you could, gasp, duplicate the needed data into the items table with a rake task on a schedule.
You say you can't change how books and CDs are stored in the database, but could you add an items view? Do you have any control over the database at all?
CREATE VIEW items
(id, type, title, genre, created_at, updated_at)
AS
SELECT b.id, 'Book', b.title, b.genre, b.created_at, b.updated_at
FROM books b
UNION
SELECT c.id, 'CD', c.title, c.genre, c.created_at, c.updated_at
FROM cds c;
You can paginate on a results array, so leave pagination out of the invidual model queries, and add it to your results array:
re_sort(items,"genre ASC").paginate(:page => params[:page], :per_page => items_per_page)

Resources