rails 3 group by and sum - ruby-on-rails

I have the following model:
activity_types: id, name
activities: id, id_activity_type, occurrences, date (other fields)
The activities table store how many times an activity occurs by day. But now I want to show to the user how many activities from each type occurred by month.
I got the following solution based on this post which seems ok:
Activity.all(:joins => :activity_types,
:select => "activity_types.id, activity_types.name, SUM(activities.occurrences) as occurrences",
:group => "activity_types.id, activity_types.name",
:order => "activity_types.id")
but this seems a lot of code for the rails standards and rails API says it's deprecated.
I found the following solution which is a lot simple:
Activity.sum(:occurrences).group(:activity_type_id)
Which returns an hash with activity_type_id => occurrences.
What shall I do to get the following hash: activity_type.name => occurrences ?

If the original query worked, then just try rewriting it with Rails 3 syntax:
Activity.joins(:activity_types)
.select("activity_types.id, activity_types.name, SUM(activities.occurrences) as occurrences")
.group("activity_types.id, activity_types.name")
.order("activity_types.id")

Activity.joins(:activity_types).group('activity_types.name').sum(:occurrences)
SELECT SUM(activities.occurrences) AS sum_occurrences, activity_types.name AS activity_types_name FROM activity_types INNER JOIN activity_types ON activity_types.id = activities.activity_types_id GROUP BY activity_types.name
in case you needed an ordered hash based on activity_types.id and assuming activity_types_id is not needed as a part of hash key.
Activity.joins(:activity_types).group('activity_types.name').order(:activity_types_id).sum(:occurrences)
incase [activity_type_id, activity_types.name] needed as a part of key
Activity.joins(:activity_types).group(:activity_types_id, 'activity_types.name').order(:activity_types_id).sum(:occurrences)

Related

Datamapper: Sorting results through association

I'm working on a Rails 3.2 app that uses Datamapper as its ORM. I'm looking for a way to sort a result set by an attribute of the associated model. Specifically I have the following models:
class Vehicle
include DataMapper::Resource
belongs_to :user
end
class User
include DataMapper::Resource
has n, :vehicles
end
Now I want to be able to query the vehicles and sort them by the name of the driver. I tried the following but neither seems to work with Datamapper:
> Vehicle.all( :order => 'users.name' )
ArgumentError: +options[:order]+ entry "users.name" does not map to a property in Vehicle
> Vehicle.all( :order => { :users => 'name' } )
ArgumentError: +options[:order]+ entry [:users, "name"] of an unsupported object Array
Right now I'm using Ruby to sort the result set post-query but obviously that's not helping performance any, also it stops me from further chaining on other scopes.
I spent some more time digging around and finally turned up an old blog which has a solution to this problem. It involves manually building the ordering query in DataMapper.
From: http://rhnh.net/2010/12/01/ordering-by-a-field-in-a-join-model-with-datamapper
def self.ordered_by_vehicle_name direction = :asc
order = DataMapper::Query::Direction.new(vehicle.name, direction)
query = all.query
query.instance_variable_set("#order", [order])
query.instance_variable_set("#links", [relationships['vehicle'].inverse])
all(query)
end
This will let you order by association and still chain on other scopes, e.g.:
User.ordered_by_vehicle_name(:desc).all( :name => 'foo' )
It's a bit hacky but it does what I wanted it to do at least ;)
Note: I'm not familiar with DataMapper and my answer might not be within the standards and recommendations of using DataMapper, but it should hopefully give you the result you're looking for.
I've been looking through various Google searches and the DataMapper documentation and I haven't found a way to "order by assocation attribute". The only solution I have thought of is "raw" SQL.
The query would look like this.
SELECT vehicles.* FROM vehicles
LEFT JOIN users ON vehicles.user_id = users.id
ORDER BY users.name
Unfortunately, from my understanding, when you directly query the database you won't get the Vehicle object, but the data from the database.
From the documentation: http://datamapper.org/docs/find.html. It's near the bottom titled "Talking directly to your data-store"
Note that this will not return Zoo objects, rather the raw data straight from the database
Vehicle.joins(:user).order('users.name').all
or in Rails 2.3,
Vehicle.all(:joins => "inner join users on vehicles.user_id = user.id", :order => 'users.name')

Rails 3 - how to add to autocomplete another condition?

I use the gem rails3-jquery-autocomplete for the autocomplete searching of items in my database. The autocomplete is working fine, but I would need to add another condition to the generated query.
If I start to write a searched string, then is generated following query:
SELECT persons.id, persons.name FROM "persons" WHERE (LOWER(persons.name) ILIKE 'jo%') ORDER BY persons.name ASC LIMIT 10
This returns me all rows, where the name starts at jo.
But how could I search all persons, which name starts at jo and simultaneously, for example, the column active_person=1?
Is there any helper for this purpose or something like that?
Thank you
If I understood the context correctly, then active_person is a boolean that indicates whether the user is active or not.
If this behavior is desirable most of time - that you search for users/people that are only active then you could include a default_scope in your Person model like this:
class Person < ActiveRecord::Base
default_scope where(:active_person => 1)
end
This way any query that is ever generated will be also checking that active_person = 1
More on default scopes here: http://apidock.com/rails/ActiveRecord/Base/default_scope/class
I understand that #Andrei 's answer is correct, but its not what I was looking for. I guess its same for #user984621 too. What I was looking for is I want to add an extra condition to the query.
By reading this https://github.com/crowdint/rails3-jquery-autocomplete/blob/master/lib/rails3-jquery-autocomplete/orm/active_record.rb
I found that rails autocomplete supports additional conditions, you just have to pass another parameter to the default usage, like this
autocomplete :user, :name, :where => { :role => 'employee'}
instead of this
autocomplete :user, :name
Change WHERE (LOWER(persons.name) ILIKE 'jo%') to WHERE (LOWER(persons.name) ILIKE 'jo%') AND active_person = 1
You can chain together loads of clauses with ANDs and OR in that way.
Note that this is really a question about SQL: your title and tags are a bit misleading.

How do I create a 'Most Recently Popular' bar for content in Ruby on Rails?

I'm a noob so please forgive if this is an easy question but I'm trying to create a 'Most Recently Popular' output for specific content on a rails project.
Right now the object I am pulling from has an attribute revision.background_title. I want to calculate popularity by finding the number of specific background_title's added over the past seven days then put them in order. For example if there are 4 background_title's named 'awesomecontent' then that would be above one that has 1 background_title named 'less awesome content'
This pulls all of them:
#revisions = Revision.find(:all, :order => "created_at desc")
Thanks.
You can use the basic ActiveRecord find method to do this. The code would end up looking something like this:
#revisions = Revision.all(
:select => "background_title, count(*) count", # Return title and count
:group => 'background_title', # Group by the title
:order => '2 desc' # Order by the count descending
)
To see the output, you could then do something like this:
#revisions.each do |revision|
puts "Revision #{revision.background_title} appears #{revision.count} times"
end
giving
Revision z appears 10 times
Revision a appears 3 times
Revision b appears 2 times
Another option would be to take a look at ActiveRecord::Calculations:
http://api.rubyonrails.org/classes/ActiveRecord/Calculations/ClassMethods.html
Calculations supports a count method that also supports the group option. However, going this route will give you back a hash containing the background_title as the key and the count as the value. Personally, find the first method more useful.

How to find only the users who have posted at least one comment

I am using Rails 2.3.5 .
This is a standard case. Tables are: users, comments, user_comments . I need to find all the users who have status 'active' and have posted at least one comment.
I know comments table can have foreign key but this is a contrived example. There are two users in the table. There are two comments. Both the comments are posted by the first user.
named_scope :with_atleast_one_comment, lambda { {
:joins => 'inner join user_comments on users.id = user_comments.user_id ' } }
named_scope :with_active_status, {:conditions => {:status => 'active'} }
When I execute
User.with_atleast_one_comment.with_active_status
I get two records. Since both the comments are posted by one user I want only one user.
What's the fix?
The with_at_least_one_comment scope isn't behaving as you expect it to. As it appears in the question, it will select a user for each entry in user_comments. Which results in the duplicate results you're seeing. When compounded with active_users, you will remove any records returned by with_at_least_one_comment that don't have active status.
Let's start by simplifying the problematic named scope first.
You don't need the lambda because there are no arguments to take, and the join can be outsourced to Active Record, which performs an inner join if given an association.
In short, this named scope will do exactly what you want.
named_scope :with_at_least_one_comment, :joins => :user_comments,
:group => 'users.id'
Specify the :uniq => true option to remove duplicates from the collection. This is most useful in conjunction with the :through option.
if i'm is not wrong, there is few way to achieve this...
unless User.comments?
or another way is also specify a new method in your controller
and lastly...
the info from Emfi should work
have a try for it~

How do I find() all records unique in certain fields?

I have a list of 'request' objects, each of which has fairly normal activerecord qualities. The requests table is related to the games table with a join table, 'games_requests,' so that a request has a request.games array.
The question is, is there a way to do a find for the last n unique requests, where uniqueness is defined by the games column and a couple others, but specifically ignores other colums (like the name of the requesting user?)
I saw a syntax like 'find (:all, :limit=>5, :include=>[:games,:stage])' but that was returning duplicates.
Thanks...
EDIT: Thanks to chaos for a great response. You got me really close, but I still need the returns to be valid request objects: the first 5 records that are distinct in the requested rows. I could just use the find as you constructed it and then do a second find for the first row in the table that matches each of the sets returned by the first find.
EDIT:
Games.find(
:all, :limit => 5,
:include => [:games, :requests],
:group => 'games, whatever, whatever_else'
)
...gives an SQL error:
Mysql::Error: Unknown column 'games' in 'group statement': SELECT * FROM `games` GROUP BY games
I made a few changes for what I assumed to be correct for my project; getting a list of requests instead of games, etc:
Request.find(
:all, :order=>"id DESC", :limit=>5,
:include=>[:games], #including requests here generates an sql error
:group=>'games, etc' #mysql error: games isn't an attribute of requests
:conditions=>'etc'
)
I'm thinking I'm going to have to use the :join=> option here.
Games.find(
:all, :limit => 5,
:include => [:games, :requests],
:group => 'games, whatever, whatever_else'
)
Try Rails uniq_by.It also works with association and returns array.
#document = Model.uniq_by(&:field)
More Detail
I think you'll be able to do this using find_by_sql and GROUP BY:
Games.find_by_sql("SELECT * FROM games GROUP BY user_id")

Resources