2009-05-24 9 views
29

Je fais un peu de calcul statique dans mon produit. Un utilisateur a effectué un certain nombre d'opérations, disons des commentaires postés. Je veux être en mesure de leur montrer combien de commentaires ils ont posté par semaine pour le mois dernier, ou par mois pour l'année écoulée.Regroupement par semaine/mois/etc & ActiveRecord?

Y at-il un moyen avec activerecord de grouper de cette façon? Est-ce que je fais de mon mieux pour le faire manuellement - pour parcourir les records en fonction de mes propres critères?

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

ou y a-t-il un meilleur moyen?

merci! Oren

Répondre

26

Dans ce cas, la meilleure solution pour moi était soit le faire dans SQL directement, ou utiliser la fonction de group_by de activerecord:

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

'group_by' n'est pas une méthode ActiveRecord, mais plutôt une méthode Ruby sur' Enumerable'. – Laurens

+0

L'utilisation de 'u.created_at.month' est plus courte. –

+11

Veuillez ne pas charger tous les enregistrements de la base de données, instancier les objets ActiveRecord, analyser les dates dans les objets d'ActiveSupport, uniquement pour calculer le nombre d'enregistrements. –

13

Je dirais quelque chose comme:

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

-Code sec, YMMV

+0

Je crois que cela va se grouper par mois sur plusieurs années aussi - donc si vous avez deux années de données, toutes les choses en janvier 1 et 2. Ce que je cherche, c'est une façon de dire 24 fenêtre mois par exemple, combien par mois. – teich

+0

Vous pouvez également ajouter une condition pour limiter l'année, comme: conditions => [: created_at, "> # {24.months.ago}"]. Encore une fois, non testé, mais devrait être possible avec quelque chose comme ça. –

+0

Cela semble spécifique à mysql – Tachyons

2

Vérifiez le plugin has_activity.

+0

Merci pour le pointeur. On dirait que has_activity est mysql seulement. Mon hôte de production est postgresql. – teich

66

En Postgres vous pouvez faire:

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

pour obtenir:

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

Il accepte les valeurs de « microsecondes » à « millénaire » pour le regroupement: http://www.postgresql.org/docs/8.1/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC

+4

Tellement plus rapide que l'utilisation de ruby ​​enumerable group_by! –

+0

Wojtek, et quand il n'y avait rien créé dans un mois? Vous n'avez rien pour ce mois-ci. –

+1

@MattSmith Je le crois. Heureusement, il est facile de fournir une valeur par défaut lors de la lecture des valeurs de la sortie de cette requête: '' 'output.fetch (" 2012-08-01 00:00:00 ", 0)' '' –

16

Voici la version plus raffinée de cette

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

Utilisez group_by

@user.comments.group_by(&:week) 

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

Cela vous donnera une liste groupée dans le format AAAA-WW

2

Check out le groupe date gem

https://github.com/ankane/groupdate

il a des commits récents, fonctionne avec postgresql, s'intègre facilement avec le coup de tableau pour la cartographie rapide, et fonctionne avec les fuseaux horaires !!

+0

GroupDate ne fonctionne qu'avec UTC, donc si vous utilisez un autre fuseau horaire dans votre base de données, envisagez des alternatives. – msdundar