Rails order not working with only - ruby-on-rails

I can't find this documented anywhere but here is my problem: when I query via active record and use "only" the "order" clause is ignored. For example I have a Blog model:
Blog.order('id desc')
returns all the blogs with the highest ID first - as expected but:
Blog.order('id desc').only(:id)
returns only the id's (as expected) but the order clause is completely ignored, the smallest id comes first.
I have tested this with Ruby 1.9.3p327 and both Rails 4.0.0.beta1 and Rails 3.2.13 and I get the same results.
Is this a feature or a bug? To me it's a bug because the Rails crew were trumpeting how find_by_sql is not really needed but in this case it is:
Blog.find_by_sql("select id from blogs order by id desc")
which gives the correct answer.

Try using pluck instead of only. only is used to override portions of the previously formed query chain. As the the docs demonstrate:
Post.where('id > 10').limit(20).order('id desc').only(:order, :where)
results in:
SELECT * FROM posts WHERE id > 10 ORDER BY id DESC
This is because the limit modification will be ignored, since the list passed to only doesn't include :limit.
UPDATE
If you need an actual model object returned instead of an array of id's, use select:
Blog.order('id desc').select('id')

Related

Rails search condition following order of a sequentially generated collection

An array of IDs are being collected
#valid_ts = []
#valid_ts = #valid_ts << dst_1.id
#valid_ts = #valid_ts << dst_2.id
note : each row shown above is in fact run for each member of a collection (i.e. dst_1.size > 1)
When comes time to query the database
slots = Timeslot.where('id IN (?)', #valid_ts).all
the goal is to generate the collection in the order that these members were added to the collection. Rails defaults to updated_at, otherwise the query has to invoke one of the class's attributes, none of which are useful for the targetted context.
How can the originating order of the array be respected with Rails?
If you are using Mysql you can use field function.
Timeslot.where(id: #valid_ts).order("field(id, #{ids.join ','})")
If you are using Postgres you can use position function. -
Timeslot.where(id: #valid_ts).order("position(id::text in '#{ids.join(',')}')")
But ActiveRecord can perform for both if you are using rails >= 5.2.0 as it is added from that version. Most likely it is also backported in rails 5.0. The pull request and the commit and the documentation.
Timeslot.find(#valid_ts)
Check this SO post for more ideas.

Rails - ordering loops from acts_as_follower gem

so I implemented this gem and everything works great, but when I list all of the items my user is following through "user.all_following" it lists it in descending order, from the most oldest to the most recent followed being the last item listed. Im trying to figure out how to reverse this and make the most recent followings displayed first and later following under it. Thanks.
<% allfollows = #user.all_follows %>
<% allfollows.each do |item| %>
....
<& end &>
All you have to do is pass order option to your all_follows method call.
For example:
#user.all_follows(order: "follows.id DESC")
By the way, currently you are getting records in ascending(most oldest to the most recent) order and NOT descending(most recent to most oldest).
I think the answer is given in the README of the gem: https://github.com/tcocca/acts_as_follower
The following methods take an optional hash parameter of ActiveRecord options (:limit, :order, etc…)
follows_by_type, all_follows, all_following, following_by_type
OR
Follow.descending # returns all records in a descending order based on created_at datetime
So you can basically filter by anything you wish. I haven't ever used this gem, but this would make most sense to me.
There is also method present i.e descending
Example:-
#user.follows.descending
This gives you a descending order of records.
Thanks.

How do I order database records in rails by most recent?

I want to order all the items in a model Item so it displays the most recent ones first. According to the Rails guide, the following code should work:
Item.order("created_at DESC")
However, when I type that (or varieties) in the terminal, the most recent item always shows up last, and that's how they show up on my page. How do I efficiently retrieve them with he most recent first? (I'm going to display only some of them at a time on each page.)
Note that my default scope for items is oldest first.
Update:
This is the SQL I get:
SELECT "comments".* FROM "comments" ORDER BY comments.created_at ASC, created_at DESC
So I guess I shouldn't use default scopes...
The query you posted is correct
Item.order("created_at DESC")
The only reason why it would not work is if there is anything else conflicting with it. In general, the conflict is represented by a default_scope.
If you have a default scope that overrides your order, you should first unscope the query
Item.unscoped { Item.order("created_at DESC") }
If you are using default scopes, I strongly encourage you to avoid them. They are very hard to debug and unscope.
There are very few cases where default scopes make sense. You can simply pass the (default) scope at select time in the controller or create a custom method for it.
I realise this is a really old question, but none of the answers contain the solution without writing raw SQL, which is available since Rails 3+:
Item.order(created_at: :desc)
or using the reverse_order method:
Item.order(:created_at).reverse_order
See more at http://guides.rubyonrails.org/active_record_querying.html#ordering
and
http://guides.rubyonrails.org/active_record_querying.html#reverse-order.
I modified CDub's answer with reverse so it now works:
Item.order(:created_at).reverse
I'm still not sure why the Rails guide's way doesn't work. Also the above method doesn't work well with pagination.
Item.unscoped.order('created_at DESC') should work.Using reverse might decrease the performance when the number of records increases
Correct one and tested
#purchase_orders = current_company.purchase_orders.order(:date)
#purchase_orders = #purchase_orders.reverse_order
you can add You can also define the default order in Item model
default_scope order('created_at DESC')

Rails active record querying association with 'exists'

I am working on an app that allows Members to take a survey (Member has a one to many relationship with Response). Response holds the member_id, question_id, and their answer.
The survey is submitted all or nothing, so if there are any records in the Response table for that Member they have completed the survey.
My question is, how do I re-write the query below so that it actually works? In SQL this would be a prime candidate for the EXISTS keyword.
def surveys_completed
members.where(responses: !nil ).count
end
You can use includes and then test if the related response(s) exists like this:
def surveys_completed
members.includes(:responses).where('responses.id IS NOT NULL')
end
Here is an alternative, with joins:
def surveys_completed
members.joins(:responses)
end
The solution using Rails 4:
def surveys_completed
members.includes(:responses).where.not(responses: { id: nil })
end
Alternative solution using activerecord_where_assoc:
This gem does exactly what is asked here: use EXISTS to to do a condition.
It works with Rails 4.1 to the most recent.
members.where_assoc_exists(:responses)
It can also do much more!
Similar questions:
How to query a model based on attribute of another model which belongs to the first model?
association named not found perhaps misspelled issue in rails association
Rails 3, has_one / has_many with lambda condition
Rails 4 scope to find parents with no children
Join multiple tables with active records
You can use SQL EXISTS keyword in elegant Rails-ish manner using Where Exists gem:
members.where_exists(:responses).count
Of course you can use raw SQL as well:
members.where("EXISTS" \
"(SELECT 1 FROM responses WHERE responses.member_id = members.id)").
count
You can also use a subquery:
members.where(id: Response.select(:member_id))
In comparison to something with includes it will not load the associated models (which is a performance benefit if you do not need them).
If you are on Rails 5 and above you should use left_joins. Otherwise a manual "LEFT OUTER JOINS" will also work. This is more performant than using includes mentioned in https://stackoverflow.com/a/18234998/3788753. includes will attempt to load the related objects into memory, whereas left_joins will build a "LEFT OUTER JOINS" query.
def surveys_completed
members.left_joins(:responses).where.not(responses: { id: nil })
end
Even if there are no related records (like the query above where you are finding by nil) includes still uses more memory. In my testing I found includes uses ~33x more memory on Rails 5.2.1. On Rails 4.2.x it was ~44x more memory compared to doing the joins manually.
See this gist for the test:
https://gist.github.com/johnathanludwig/96fc33fc135ee558e0f09fb23a8cf3f1
where.missing (Rails 6.1+)
Rails 6.1 introduces a new way to check for the absence of an association - where.missing.
Please, have a look at the following code snippet:
# Before:
Post.left_joins(:author).where(authors: { id: nil })
# After:
Post.where.missing(:author)
And this is an example of SQL query that is used under the hood:
Post.where.missing(:author)
# SELECT "posts".* FROM "posts"
# LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# WHERE "authors"."id" IS NULL
As a result, your particular case can be rewritten as follows:
def surveys_completed
members.where.missing(:response).count
end
Thanks.
Sources:
where.missing official docs.
Pull request.
Article from the Saeloun blog.
Notes:
where.associated - a counterpart for checking for the presence of an association is also available starting from Rails 7.
See offical docs and this answer.

Rails: Getting column value from query

Seems like it should be able to look at a simple tutorial or find an aswer with a quick google, but I can't...
codes = PartnerCode.find_by_sql "SELECT * from partner_codes where product = 'SPANMEX' and isused = 'false' limit 1"
I want the column named code, I want just the value. Tried everything what that seems logical. Driving me nuts because everything I find shows an example without referencing the actual values returned
So what is the object returned? Array, hash, ActiveRecord? Thanks in advance.
For Rails 4+ (and a bit earlier I think), use pluck:
Partner.where(conditions).pluck :code
> ["code1", "code2", "code3"]
map is inefficient as it will select all columns first and also won't be able to optimise the query.
You need this one
Partner.where( conditions ).map(&:code)
is shorthand for
Partner.where( conditions ).map{|p| p.code}
PS
if you are often run into such case you will like this gem valium by ernie
it gives you pretty way to get values without instantiating activerecord object like
Partner.where( conditions ).value_of :code
UPDATED:
if you need access some attribute and after that update record
save instance first in some variable:
instance=Partner.where( conditions ).first
then you may access attributes like instance.code and update some attribute
instance.update_attribute || instance.update_attributes
check documentation at api.rubyonrails.org for details

Resources