Rails find at least one "order" with status "completed" - ruby-on-rails

Not the best with Rails so this might be very simple.
I have a database with tables Groups and Orders - groups has_many orders, orders belongs_to groups. Orders have a string field called status, which can be in progress, canceled, completed. An order is considered "not in progress" when it is either canceled or completed.
I would like to be able to find all groups with no orders in progress and at least one order completed. So far I have the none in progress:
groups = Group.find(:all, :include => :orders, :conditions => ["orders.status != :in_progress", { :in_progress => Constant::IN_PROGRESS }])
Is there a way to find groups with at least one order completed (without simply iterating over this array and picking them out)?
Edit: Rails version 2.1.0 Sorry for the oversight!

If you put this into one find condition it's going to be really complicated and you will probably want to add more filters later so might want to break it into to finds.
#groups = []
groups = Group.find(:all, :joins => :orders, :conditions =>])
no_orders_in_progress = Group.find(:all, :jois => 'order', :conditions => ['status!=', 'in progress'], :limit => 5)
orders_completed = Group.find(:all, :joins => 'order', :conditions => ['status=?', 'completed'], :limit => 1)
#groups << orders_completed + no_orders_in_progress
Make sure to limit it however many you want.

Related

How do I find all records of a model based on it's association with another model?

What I would like to find is all Events, where event.event_date is in the future, and only get the top 3 events sorted by how many Users the event has associated with it. Events and Users are joined by a HABTM relationship. Here's what I tried:
#popular_events = Event.where("event_date >= ?", Time.now)
.find(:all,
:joins => :users,
:group => 'event_id',
:order => "users.count DESC",
:limit => 10 )
I've tried a few other things with no luck. It is saying users.count is not a valid column.
believe it or not, this is a pain in the b*tt to do this with ActiveRecord. You will more or less have to rely on raw SQL to do it. Some answers here : http://www.tatvartha.com/2009/03/activerecord-group-by-count-and-joins/

RoR top five ratings

I have a model that has ratings in it for an post.
I want to display a list of the top 5 ratings for a given post, but I am completely lost on where to start. I figure the find method might have something useful, but I'm unsure. I've even considered looping through each record getting its size, adding it to a hash, sorting the hash, etc., but that seems too complicated.
Does anyone know how I might accomplish something like this?
Thank you
Edit: I found this to get all the posts that have the rating of agree:
Post.find(:all, :include => :post_ratings, :condtions => ['post_ratings.agree = ?', true])
The only problem is I can't figure out how to get the top five ratings from this query.
Might be worth giving a little more of an example of the code you're working with but I'll answer with a few assumptions.
If you have:
class Post
has_many :post_ratings
end
class PostRating
belongs_to :post
# Has a 'rating' attribute
end
You can find the top five post ratings with:
p = Post.find(:first) # For example
p.post_ratings.find(:all, :limit => 5, :order => 'rating desc')
To get the top five post ratings overall you can do:
PostRating.find(:all, :limit => 5, :order => 'rating desc')
UPDATE:
Following your edit it seems you have an 'agree' and a 'disagree' column. Not sure how that works in combination so I'll stick with the 'agree' column. What you'll need to do is count the ratings with agree flagged. Something like:
count_hsh PostRating.count(:group => 'post_id',
:order => 'count(*) desc',
:conditions => { :agree => true },
:limit => 5)
This will return you a hash mapping the post id to the count of agree ratings. You can then use that post_id to locate the posts themselves. The ratings are provided by the counts so the individual ratings are (I think) of no use though you could access them by calling post.post_ratings.
So, to get the top five posts:
#top_five_posts = []
count_hsh.each_pair do |post_id, ratings|
p = Post.find(post_id)
p[:rating_count] = ratings
#top_five_posts << p
end
This is probably more verbose than it could be but is hopefully illustrative. The p[:rating_count] is a virtual attribute which isn't in the database but will allow you to access the .rating_count method on the posts in your view if you wish to.
Assuming the same Post and PostRating from Shadwell's answer:
class Post
has_many :post_ratings
end
class PostRating
belongs_to :post
# Has a 'rating' attribute
end
To get the top five ratings for all Posts:
post_ratings.find(:all, :limit => 5, :order => 'rating desc')
To get the top five ratings for a specific Post you can:
p = Post.find(:first)
post_ratings.find_all_by_post_id(p.id, :limit => 5, :order => 'rating desc')
To find all posts sorted by average rating, you can use ActiveRecord::Calculations.
PostRating.average(:rating, :group => :post_id, :include => :post, :order => 'average desc')

Complex filtering based on a many-to-many relationship

In my application, Annotations are considered "accepted" if either:
They have been explicitly marked "accepted" (i.e., their state == 'accepted')
They were last updated by a user who has the "editor" role
My question is how to find all accepted explanations with a single DB query. Basically I'm looking for the database-driven version of
Annotation.all.select do |a|
a.last_updated_by.roles.map(&:name).include?('editor') or a.state == 'accepted'
end
My first attempt was
Annotation.all(:joins => {:last_updated_by => :roles}, :conditions => ['roles.name = ? or annotations.state = ?', 'editor', 'accepted'])
But this returns a bunch of duplicate records (adding a .uniq makes it work though)
Changing :joins to :include works, but this makes the query way too slow
Are the results of your first attempt just wrong or do they only need an ".uniq"?
Have you tried
:include => {:last_updated_by => [:roles]}
instead of the join?
or making two queries
#ids = Editor.all(:conditions => ["role = 'editor'"], :select => ["id"]).map{|e|e.id}
Annotation.all(:conditions => ["last_updated_by in (?) or state = ?", #ids.join(","), "accepted"]
is that any faster?

Rails find by *all* associated tags.id in

Say I have a model Taggable has_many tags, how may I find all taggables by their associated tag's taggable_id field?
Taggable.find(:all, :joins => :tags, :conditions => {:tags => {:taggable_id => [1,2,3]}})
results in this:
SELECT `taggables`.* FROM `taggables` INNER JOIN `tags` ON tags.taggable_id = taggables.id WHERE (`tag`.`taggable_id` IN (1,2,3))
The syntax is incredible but does not fit my needs in that the resulting sql returns any taggable that has any, some or all of the tags.
How can I find taggables with related tags of field taggable_id valued 1, 2 and 3?
Thanks for any advice. :)
UPDATE:
I have found a solution which I'll post for others, should they end up here. Also though for the attention of others whose suggestions for improvement I'd happily receive. :)
Taggable.find(:all, :joins => :tags, :select => "taggables.*, tags.count tag_count", :conditions => {:tags => {:taggable_id => array_of_tag_ids}}, :group => "taggables.id having tag_count = #{array_of_tag_ids.count}"))
Your question is a bit confusing ('tags' seemed to be used quite a bit :)), but I think you want the same thing I needed here:
Taggable.find([1,2,3], :include => :tags).tags.map { |t| t.taggables }
or (if you want the results to be unique, which you probably do):
Taggable.find([1,2,3], :include => :tags).tags.map { |t| t.taggables }.flatten.uniq
This gets at all the taggables that have the same tags as the taggables that have ids 1,2, and 3. Is that what you wanted?

Activerecord: Finding non associated plus associated records with a single query

Given the following scenario, using activerecord:
User belongs_to Event
I want to find all the Users who do not have an Event with the name "party".
So I tried the following:
User.all(:joins => :event, :conditions => ["events.name != ?", "party"])
However, that will only return those Users who do have an event, but just not a "party" event. It won't return all the Users who have no event at all, because :joins automatically limits the results to associated records only.
I could do two queries and add the results together, but is there a way, in one query to fetch the non-associated records, plus the associated records that match the condition?
How about this?
User.all(
:include => :event,
:conditions => ["users.event_id IS NULL OR events.name != ?", "party"])

Resources