2009-05-24 23 views
29

Estoy haciendo algunos cálculos estáticos en mi producto. Un usuario ha realizado una serie de operaciones, digamos comentarios publicados. Quiero poder mostrarles cuántos comentarios publicaron por semana durante el último mes o por mes durante el año pasado.Agrupación por semana/mes/etc. y ActiveRecord?

¿Hay alguna manera con activerecord para agrupar de esta manera? ¿Es mi mejor esfuerzo simplemente hacer esto manualmente - para iterar sobre la suma de registros según mis propios criterios?

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...} 

o hay alguna manera mejor?

gracias! Oren

Respuesta

26

En este caso, la mejor solución para mí fue bien hacerlo en SQL directamente, o utilizar la función activerecord group_by:

@user.all.group_by{ |u| u.created_at.beginning_of_month } 
+10

'group_by' no es un método ActiveRecord, sino más bien un método Ruby en' Enumerable'. – Laurens

+0

Simplemente usando 'u.created_at.month' es más corto –

+11

No cargue todos los registros de la base de datos, cree instancias de objetos ActiveRecord, analice las fechas en los objetos de zona horaria de ActiveSupport, todo para calcular el número de registros. –

13

Mi conjetura sería algo así como:

@user.comments.count(:group => "year(created_at),month(created_at)") 

seco de código, ymmv

+0

Creo que esto se agrupará por meses a lo largo de los años, así que si tiene dos años de datos, todo en enero año 1 y año 2. Lo que estoy buscando es una manera de contar para un 24 ventana de mes, por ejemplo, cuántos por mes. – teich

+0

También puede agregar una condición para limitar el año, como: condiciones => [: created_at, "> # {24.months.ago}"]. Nuevamente, no probado, pero debería ser posible con algo como esto. –

+0

Esto parece específico de mysql – Tachyons

2

Salida has_activity el plugin.

+0

Gracias por el puntero. Parece que has_activity es solo mysql. Mi servidor de producción es postgresql. – teich

66

En Postgres que puede hacer:

@user.comments.group("DATE_TRUNC('month', created_at)").count 

para obtener:

{"2012-08-01 00:00:00"=>152, "2012-07-01 00:00:00"=>57, "2012-09-01 00:00:00"=>132} 

acepta valores de "microsegundos" a "Millennium" para la agrupación: http://www.postgresql.org/docs/8.1/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC

+4

¡Mucho más rápido que con Ruby enumerable group_by! –

+0

Wojtek, ¿qué pasa cuando no se creó nada en un mes? No obtienes nada por ese mes. –

+1

@MattSmith Creo que sí. Afortunadamente, es fácil proporcionar un valor predeterminado al leer los valores del resultado de esta consulta: '' 'output.fetch (" 2012-08-01 00:00:00 ", 0)' '' –

16

Aquí está la versión más refinada de este

@user.comments.group("year(created_at)").group("month(created_at)").count 
4

Uso group_by

@user.comments.group_by(&:week) 

class User < ActiveRecord::Base 
    def week 
    some_attribute_like_date.strftime('%Y-%W') 
    end 
end 

Esto le dará una lista agrupada en el formato AAAA-WW

2

Salida la gema de la fecha del grupo

https://github.com/ankane/groupdate

tiene commits recientes, funciona con postgresql, se integra fácilmente con chart kick para gráficos rápidos, y funciona con husos horarios !!

+0

GroupDate solo funciona con UTC, por lo que si está utilizando otra zona horaria en su base de datos, considere alternativas. – msdundar