How to order by multi relation table 's field? - sql

I need to generate a list of receipts in rails which need to be ordered by item's order relationship field (payment_method_meta_type.name).
Models :
Receipt
Deposit
PaymentMethodMetaType
In Deposit Model:
class Deposit < ActiveRecord::Base
belongs_to :payment_method_meta_type
has_many :receipts, :class_name=>"Receipt", :foreign_key=>"deposit_id",
:dependent => :destroy
end
I got an Array of Receipts in controller already :
#receipts = Receipt.find(:all, :conditions => ["date BETWEEN ? AND ?",
#start_date, #end_date], :order => "date DESC, id DESC",
:limit => limit, :offset => offset)
In the view I can show the payment_method_meta_type.name as well
- #receipts.each do |o|
%tr.
.....
%td #{o.receipt_number}
%td #{o.deposit.payment_method_meta_type.name}
.....
But how can I show the list by the order of receipts.deposit.payment_method_meta_type.name in the controller when I create the collection of the receipts array?

Try this:
#receipts = Receipt.all(:joins => {:deposit => :payment_method_meta_type},
:conditions => {:date => #start_date..#end_date},
:order => "payment_method_meta_types.name ASC",
:limit => limit, :offset => offset)

thx , I finally work it out, use 'include' in the query string
#receipts = Receipt.find(:all, :include => {:deposit => [:payment_method_meta_type] } ,:conditions => ["Receipts.business_date BETWEEN ? AND ?", #current_month_start_date, #current_month_end_date],:order => "tag_types.name , Receipts.business_date DESC",:limit => limit)

Related

(Rails Active Record): Request with constraint for uniqueness of a filed of the result objects

Ok, I got the following simple model class:
class Baby < ActiveRecord::Base
attr_accessible :name, :born_at
...
end
And I need to collect the youngest 20 babies with unique names
Baby.all(:order => "born_at desc", :limit => 20)
But I don't know what to add to the request so the names of the babies to be unique.
Disclaimer: I'm pretty new to SQL databases, so don't judge me for my lame question.
Baby.group(:name).order('born_at desc').limit(20)
Baby.all(:order => 'born_at desc', :limit => 20, :group => :name)
Or if you need only names
Baby.select(:name).limit(20).order('born_at desc')

How to get number of items in a grouped row in Ruby on Rails?

I am finishing up my first RoR project, and am working on a leaderboard system that shows the number of points users have accrued for correctly answering quiz questions.
I am getting all of the users that have answered at least one question correct, grouping them by user_id, and displaying them in descending order by most correct using this:
#users = Point.find(:all,
:group => 'user_id',
:order => 'correct DESC', :conditions => { :correct => "yes"})
In my view, I am using this to iterate through the results:
<% #users.each_with_index do |user, index| %>
However, I am not able to get the number of correct answers per user. I tried:
user.count
but that doesn't work. How do I get the number of items per group?
You're on the right track. Seems like you would be better off using the all command with the count condition within it as opposed to the count command. Something like this:
Point.all(:select => 'user_id, count(id) as point_count', :group => :user_id, :conditions => { :correct => 'yes' }, :order => 'point_count desc', :limit => 10)
This will return 10 limited Point objects with a user_id attribute (so you can still access the user relationship), and a point_count attribute with the number of correct points said user has obtained.
Note: you could change the limit to be however many users you wanted to display in your leaderboard. This example would return 10.
It might make more sense to have your code look like this:
#points = Point.all(:select => 'user_id, count(id) as point_count', :group => :user_id, :conditions => { :correct => 'yes' }, :order => 'point_count desc', :limit => 10)
And as I said in a comment below, you could iterate through them by doing something like this (this would assume that your User model has a name attribute):
<table>
<% #points.each do |point| %>
<tr>
<td><%= point.user.name %></td>
<td><%= point.point_count %></td>
</tr>
<% end %>
</table>
I think the problem may be that you think you're getting an Array back, but you actually get a Hash back.
Try doing:
p #users
(which is equivalent to puts #users.inspect). You'll probably see it's more so something like:
{ "1" => [UserObject, UserObject], "2" => `[UserObject] }
You can even do p #users.class and you'll see it's not an array.
When you loop with a .each_with_index on a Hash, you need to do:
#users.each_with_index do |(key, value), index|
Then you can do #users[key].count or value.count.
Figured out how to get the correct count:
#users = Point.count(:group => :user_id, :conditions => { :correct => "yes"})
The most simple way should be:
#user.points.where(:correct => "yes").count
Though this will only work if have defined your associations in the user and point model like
class User < ActiveRecord::Base
has_many :points
class Point < ActiveRecord::Base
belongs_to :user
(personally I would have used a bool flag (smallint) instead of string for the "correct" column.

using :include && :conditions in a ruby on rails SQL request?

Hey I'm trying to figure something out..
I want to get all Carts that have a cart_stage.stage equal to '35' (this is a separate table and a cart has many stages)
the cart_stage table is a bit like
id ----- cart_id ----- stage
1 ------- 123 ---------- 20
2 ------- 123 ---------- 35
3 ------- 102 ---------- 35
I am trying this at the moment:
# Cart model
has_one :top_stage, :foreign_key => 'cart_id', :class_name => "CartStage", :order => 'stage'
# Cart controller
#carts = Cart.find :all, :order => 'created_at DESC', :include => :top_stage, :conditions => ["top_stage.stage = ?", 35]
This gives me :
SQLite3::SQLException: no such column: top_stage.stage: SELECT DISTINCT "carts".id FROM "carts" WHERE (top_stage.stage = 35) ORDER BY created_at DESC LIMIT 40 OFFSET 0
Hope it all makes sense and any help would be greatly appreciated,
Alex
That should probably be:
#carts = Cart.find(:all, :order => 'carts.created_at DESC', :include => :top_stage, :conditions => { "cart_stages.stage" => 35 })
Remember that you use the name of the table in the conditions, not the name of the association, however you do use the name of the association in the include option.
Whenever possible, you should probably use the hash method for expressing conditions to keep your declarations simple. It's when you need complicated OR or > type logic that the array-style proves necessary.
A better way of expressing it for Rails 3 is:
#carts = Cart.order('carts.created_at DESC').include(:top_stage).where('top_stages.stage' => 35)
It's not clear why your cart_stages table isn't being added with a JOIN.
Anywhere that you include literal SQL, you need to use the real table and column names, not the Rails association names. So you need to have: :conditions => ["cart_stage.stage = ?", 35] instead of top_stage
I managed to work it out my self actually.. What I wanted was -
#carts = Cart.find :all, :order => 'created_at DESC', :joins => :top_stage, :conditions => { :cart_stages => { :stage => 35 } }
Thanks for all the help guys! :)

Refactoring a find for children of parent created after a time

I am trying to find any comments created after a datetime that were not made by the current user.
At first I did this..
current_user.comments.find(:all, :conditions=>["created_at > ? AND user_id != ?",
current_user.last_checked_mail, current_user])
but the problem here was that because it begins with the user model, its only finding comments exclusively made by that user.
So instead, I began searching for all comments associated with the user, and those comments children so long as their user_id is not the current_user
Comment.find(:all, :conditions => ["user_id = ?", current_user]).select { |c|
c.comments.find(:all, :conditions => ["user_id != ?", current_user])
}
But it seems that still draws every single comment related to the current_user, and not their children.
My Comment Model :
belongs_to :commentable, :polymorphic => true
has_many :comments, :as => :commentable
So you have a parent relation in your comment model? Use that!
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :parent, :class_name => "Comment"
def self.after(date)
where "created_at > ?", date
end
def self.replies_for(comments)
where :parent_id => comments.map(&:id)
end
def self.exclude_user(user_id)
where "user_id != ?", user_id
end
end
class User < ActiveRecord::Base
has_many :comments
def new_comments
comments.after(last_check_mail)
end
def new_responses
Comment.replies_for(new_comments).after(last_check_mail).exclude_user(id)
end
end
current_user.new_responses
PS. This is for Rails 3.
Try:
Comment.find(:all, :conditions => ["created_at > ? AND user_id != ?", current_user.last_checked_mail, current_user])
Try this:
Comment.all(:conditions => ["user_id != ? AND parent_id = ? AND created_at > ?",
current_user.id, current_user.id, current_user.last_checked_mail])
Better solution is to create a named_scope on the Comment model.
class Comment
named_scope :other_comments, lambda {|u| { :conditions =>
["user_id != ? AND parent_id = ? AND created_at > ?",
u.id, u.id, u.last_checked_mail ] }}
end
Now you can get others comments as:
Comment.other_comments(current_user)
Edit 1
Updated answer based for Polymorphic associations:
class Comment
def self.other_comments(u)
Comment.all(
:joins => "JOIN comments A
ON A.commentable_type = 'User' AND
A.commentable_id = #{u.id} AND
comments.commentable_type = 'Comment' AND
comments.commentable_id = A.id",
:conditions => ["comments.user_id != ? AND comments.created_at > ?",
u.id, u.last_checked_mail]
)
end
end
Now you can get others comments as:
Comment.other_comments(current_user)

Need help with an SQL query in Rails

on my site, I'm trying to display the hiking trails with the latest posted pictures (Scroll to the "Picture section: http://www.trailheadfinder.com/trail_search/latest_trails). However, the current query I use, is showing the trails in order of the "first" picture posted. So when a new picture is added at a later date, the trail does not show at the top. I have a trail table and a trailpicture table that are linked. Here is the current query I use:
#trails_pictures = Trailpicture.find(:all,
:limit => 20,
:include => [:trail],
:select => 'trailpictures.trail_id, trails.name, trails.short_description, trails.city, trails.state, trails.country',
:group => 'trailpictures.trail_id',
:conditions => ["trailpictures.parent_id is NULL"],
:order => 'trailpictures.id DESC')
Any help would be greatly appreciated!
Thank You,
Nick,
You need to order by created_at. :)
I finally figured it out. This is how it needs to be written:
#trails_pictures = Trail.find(:all,
:joins => 'INNER JOIN trailpictures ON trails.id = trailpictures.trail_id',
:limit => 20,
:conditions => ["trailpictures.parent_id is NULL"],
:select => 'trails.id, trails.name, trails.short_description, trails.city, trails.state, trails.country, max(trailpictures.id)',
:group => 'trails.id',
:order => 'max(trailpictures.id) DESC')