I'm doing some statics calculation in my product. A user has performed a number of operations, let's say posted comments. I want to be able to show them how many comments they've posted per week for the past month, or per month for the past year.
Is there any way with activerecord to group this way? Is my best best to simply do this manually - to iterate over the records summing based on my own criteria?
class User < ActiveRecord::Base
has_many :comments
end
class Comments < ActiveRecord::Base
belongs_to :user
end
#user.comments(:all).map {|c| ...do my calculations here...}
or is there some better way?
thanks!
Oren
In Postgres you can do:
#user.comments.group("DATE_TRUNC('month', created_at)").count
to get:
{"2012-08-01 00:00:00"=>152, "2012-07-01 00:00:00"=>57, "2012-09-01 00:00:00"=>132}
It accepts values from "microseconds" to "millennium" for grouping:
http://www.postgresql.org/docs/8.1/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
In this case, the best solution for me was to either do it in straight SQL, or to use the Ruby group_by function:
#user.all.group_by{ |u| u.created_at.beginning_of_month }
Here is the more refined version of this
#user.comments.group("year(created_at)").group("month(created_at)").count
My guess would be something like:
#user.comments.count(:group => "year(created_at),month(created_at)")
Dry-code, ymmv
Use group_by
#user.comments.group_by(&:week)
class User < ActiveRecord::Base
def week
some_attribute_like_date.strftime('%Y-%W')
end
end
This will give you a grouped list in the format of YYYY-WW
Check out the has_activity plugin.
Check out the group date gem
https://github.com/ankane/groupdate
it has recent commits, works with postgresql, integrates easily with chart kick for fast charting, and works with time zones!!
Related
I have two Models: Tournaments, Results
class Tournament < ApplicationRecord
has_many :results
end
class Result < ApplicationRecord
belongs_to :tournament
end
I currently have the following to achieve "Determine the most recent tournament date that is either today or in the past":
last_tournament = Tournament.where("tournament_date <= ?", Date.today).order(tournament_date: :desc).limit(1)[0]
But I want to add to this ..."and has at least 3 result entries". Other homepage code depends on "last tournament" and it makes it easier if it does not try to analyze data that has not been entered yet, as soon as it turns midnight.
I was thinking it would use .joins() with Result and Tournament but I think I need to use .count on Result and that throws off me off, I'm having so trouble figuring out a solution.
Maybe a do loop that rolls out a desc order of the last tournaments and an if statement that checks if that Result(tournament_id : x).count >= 3? Is there a one-liner thats clean for this solution?
thanks.
Group the rows by tournaments.id and use HAVING to set a condition on the group:
Tournament
.joins(:results)
.where(tournament_date: ..Date.today)
.order(tournament_date: :desc)
.group(:id)
.having(Result.arel_table[:id].count.gteq(3))
.limit(1)
I have models UserVote, Comment, Edit, etc, all of which have a user_id attribute. I'm trying to create a sort of timeline of recent activity, and this has me querying all 5 of my models separately and sorting by datetime. However, with accounts that have a lot of activity, these 5 queries take a very long time to execute. I'd like to find a way to optimize the performance, and I figured combining the 5 queries might work.
I haven't been able to come up with any working query to achieve what I'd like.
Thanks for any help!
I think the best suggestion in the comments is from Steve Jorgensen, with "I have generally seen this done by adding records to an activity log, and then querying that.".
If you want to take this idea to the next level, check out sphinx (a search engine designed for indexing database content). You can integrate easily with rails using thinksphinx - http://freelancing-god.github.com/ts/en/.
Also, as Tim Peters brings up, you really should have indexs on all of your fkeys, regardless of how you solve this - http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index.
I think it is good idea to use Polymorphic associations for this problem - http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
class TimeLine < ActiveRecord::Base
belongs_to :timelineable, :polymorphic => true
end
class UserVote < ActiveRecord::Base
has_many :time_lines, :as => :timelineable
end
class Comments < ActiveRecord::Base
has_many :time_lines, :as => :timelineable
end
Now you can sort time_line and access associated resources.
A User has_many Solutions
How do I order Users by those with the most Solutions?
I'm trying to find the top ten users but I'm not sure how the most tidy or efficient way to do this?
Does anyone have an example that isn't too computationally expensive?
User
.joins(:solutions)
.select("users.*, count(solutions.id) as scount")
.group("users.id")
.order("scount DESC")
If you really want a fast way of doing it, put a counter_cache on a users' solutions (have a solutions_count column in your User) and order by that column. You don't need to manage that counter, because rails does it for you. You can read more about counter_cache in the Rails Guides
Assuming the following models
class User
has_many :solutions
end
class Solution
belongs_to :user
end
Then the best way is to counter_cache the solutions_count and order by it. more on counter_cache
The query will then be
User.order("users.solutions_count DESC").limit(10)
Don't forget to index the solutions_count column.
Live site: http://iatidata.heroku.com
Github: https://github.com/markbrough/IATI-Data
Based on aid information released through the IATI Registry: iatiregistry.org
I'm a bit of a Rails n00b so sorry if this is a really stupid question.
There are two key Models in this app:
Activity - which contains details
such as recipient country, funding
organisation
Transaction - which contains details such as how much money (value) was committed or disbursed (transaction_type), when, to whom, etc.
All Transactions nest under an Activity. Each Activity has multiple Transactions. They are connected together by activity_id. has_many :transactions and belongs_to :activity are defined in the Activity and Transaction Models respectively.
So: all of this works great when I'm trying to get details of transactions for a single activity - either when looking at a single activity (activity->show) or looping through activities on the all activities page (activity->index). I just call
#activities.each do |activity|
activity.transactions.each do |transaction|
transaction.value # do something like display it
end
end
But what I now really want to do is to get the sum of all transactions for all activities (subject to :conditions for the activity).
What's the best way to do this? I guess I could do something like:
#totalvalue = 0
#activities.each do |activity|
activity.transactions.each do |transaction|
#totalvalue = #totalvalue + transaction.value
end
end
... but that doesn't seem very clean and making the server do unnecessary work. I figure it might be something to do with the model...?! sum() is another option maybe?
This has partly come about because I want to show the total amount going to each country for the nice bubbles on the front page :)
Thanks very much for any help!
Update:
Thanks for all the responses! So, this works now:
#thiscountry_activities.each do |a|
#thiscountry_value = #thiscountry_value + a.transactions.sum(:value)
end
But this doesn't work:
#thiscountry_value = #thiscountry_activities.transactions.sum(:value)
It gives this error:
undefined method `transactions' for #<Array:0xb5670038>
Looks like I have some sort of association problem. This is how the models are set up:
class Transaction < ActiveRecord::Base
belongs_to :activity
end
class Activity < ActiveRecord::Base
has_and_belongs_to_many :policy_markers
has_and_belongs_to_many :sectors
has_many :transactions
end
I think this is probably quite a simple problem, but I can't work out what's going on. The two models are connected together via id (in Activity) and activity_id (in Transactions).
Thanks again!
Use Active Record's awesome sum method, available for classes:
Transaction.sum(:value)
Or, like you want, associations:
activity.transactions.sum(:value)
Let the database do the work:
#total_value = Transaction.sum(:value)
This gives the total for all transactions. If you have some activities already loaded, you can filter them this way:
#total_value = Transaction.where(:activity_id => #activities.map(&:id)).sum(:value)
You can do it with one query:
#total_value = Transaction.joins(:activity).where("activities.name" => 'foo').sum(:value)
My code was getting pretty messy summing up virtual attributes. So I wrote this little method to do it for me. You just pass in a collection and a method name as a string or symbol and you get back a total. I hope someone finds this useful.
def vsum collection, v_attr # Totals the virtual attributes of a collection
total = 0
collection.each { |collect| total += collect.method(v_attr).call }
return total
end
# Example use
total_credits = vsum(Account.transactions, :credit)
Of course you don't need this if :credit is a table column. You are better off using the built in ActiveRecord method above. In my case i have a :quantity column that when positive is a :credit and negative is a :debit. Since :debit and :credit are not table columns they can't be summed using ActiveRecord.
As I understood, you would like to have the sum of all values of the transaction table. You can use SQL for that. I think it will be faster than doing it the Ruby way.
select sum(value) as transaction_value_sum from transaction;
You could do
#total_value = activity.transactions.sum(:value)
http://ar.rubyonrails.org/classes/ActiveRecord/Calculations/ClassMethods.html
I have to define an association that doesn't seem to fit in well to the "has_one / belongs_to" bucket very well.
The situation is this, I have a table whereby each row corresponds to monthly statistics for a given month and year. I'd love to be able to define certain associations on my model such as record.prior_month or record.prior_year which would correspond to the prior month / year of the current record.
I can't think of any clever way to do this as it doesn't make any sense to maintain foreign keys that would have to be updated every month for tons of records.
I can always handle the logic in the controller, but I'd prefer to keep it with the model if I could.
Thanks!
Mike
So rather than store the Month/Year, also store the Month+Year*12. So March 2011 is 24135
That way, you know the next month is 21436, and you can easily paginate over your records.
TrafficGroup.order("month_calculated").paginate(:page=>params[:page])
Something like this?
class MyModel < AR::Base
def prior_month
created_at.month
end
def prior_year
created_at.year
end
end
example = MyModel.last
example.prior_year
#=> 2010
example.prior_month
#=> 3
You can do this a few ways. Assuming the month is stored in the model.
My favorite is scopes, since it plays nicely with other associations.
For instance you can do:
class TrafficRecord < ActiveRecord::Base
scope :only_month, lambda {|month| where :month => month} # this should also contain join conditions
def prior_month
self.class.only_month(self.month - 1) #decrement your month
end
end