Need help with an SQL query in Rails - sql

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

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! :)

How to order by multi relation table 's field?

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)

Correct this Rails/ruby method for me, please?

I've a post model with act-as-taggable-on gem. Both tables have timestamps.
I started with
def tags
#posts = current_user.posts.find_tagged_with(params[:tag], :order => "#posts.tags.updated_at DESC"])
end
And when that didn't work, I tried changing things and ended up with this mess.
def tags
#posts = current_user.posts.find_tagged_with(params[:tag])
#tags = #posts.tags.all
#posts = #tags(params[:tag, :order => "#posts.tags.updated_at DESC"])
end
I basically want to sort by when the tags was last updated.
Bonus: Sort by tag.updated_at or post.updated_at, but in this particular app, I'll be updating tags the most, so just first one will be fine.
Any help is appreciated.
You have to join the tags table in your find statement:
def tags
#posts = Post.find_tagged_with(
params[:tag],
:conditions => {:user_id => current_user.id},
:joins => :tags,
:order => 'tags.updated_at DESC',
:group => 'posts.id'
)
end
Note: Find the right conditions to select only posts from the current user. This example could work, though.