2010-09-23 15 views
2

que je veux quelque chose comme ceci dans Rails:Comment puis-je calculer une somme conditionnelle dans mon modèle?

class Proposal < ActiveRecord::Base 
    def interest_level 
     self.yes_votes.count - self.no_votes.count 
    end 

    private 
    def yes_votes 
     self.votes.where(:vote => true) 
    end 

    def no_votes 
     self.votes.where(:vote => false) 
    end 
end 
  1. Qu'ai-je essentiellement fait mal dans le code ci-dessus? (Je me rends compte qu'il est probablement terrible de nombreuses façons.)
  2. Quelle est la bonne façon de le faire à partir d'un point de vue Rails?
  3. Quelles considérations dois-je garder à l'esprit d'un point de vue de la base de données? (Par exemple, même si le code comme ci-dessus était possible, je suppose que ce serait excessif du côté DB. Mais, naturellement, je ne suis pas vraiment sûr.)
+0

Etes-vous en train de demander spécifiquement Rails 3 (j'ai choisi Cue de '.where')? – Swanand

Répondre

2

classe Proposition < ActiveRecord :: Base

def interest_level 
    self.votes.sum('votes', :conditions => {:votes = true}) - self.votes.sum('votes', :conditions => {:votes = false}) 
end 

fin

grâce, Anubhaw

+0

Cela n'a pas beaucoup de sens. En fait je pense que ça ne marchera pas, ça générerait, si les rails le permettaient, deux requêtes comme celle-ci: 'SELECT SUM (votes) FROM votes WHERE votes = true AND proposal_id = ..' Qui échouerait (pas de colonne nommée «votes»). La méthode dans la question générerait le code SQL correct: 'SELECT count (1) FROM votes WHERE vote = true ET proposition_id = ..' –

1

je ne vois pas vraiment quoi que ce soit ouvertement mal avec votre code. Bien qu'il existe un certain nombre de façons d'accomplir ce que vous semblez essayer de le faire, votre méthode semble que cela devrait fonctionner correctement (même si j'ai une expérience limitée Arel).

Une autre façon d'aller à ce sujet pourrait être tout simplement changer la méthode interest_level:

def interest_level 
    self.votes.count - self.no_votes.count * 2 # same as (total - no_votes) - no_votes 
end 

Le ci-dessus pourrait être un peu plus rapide, mais je doute fortement fera beaucoup de différence, le nombre d'interrogations sont assez vite sur colonnes indexées, et votre version de cette méthode est plus facile à lire

2

Compte tenu de la charge de base de données, je recommande de mettre en œuvre un cache de compteur personnalisé. Je ferais comme ceci:

class Vote < ActiveRecord::Base 

    def after_create 
     self.update_counter_cache 
    end 

    def after_destroy 
     self.update_counter_cache 
    end 

    def update_counter_cache 
     self.proposal.yes_votes_count = self.proposal.votes.where(:vote=>true) 
     self.proposal.no_votes_count = self.proposal.votes.where(:vote=>false) 
     self.propsal.save 
    end 
end 

S'il vous plaît noter que vous devez ajouter deux colonnes à votre modèle Proposal.

add_columns_migration.rb 

add_column :proposals, :yes_votes_count, :integer 
add_column :proposals, :no_votes_count, :integer 
+0

Cela fonctionnerait, mais il est plus complexe, plus sujet à l'échec, et ses performances par rapport à la méthode dans la question serait basée sur la fréquence à laquelle les votes sont mis à jour ou modifiés. Ajouter des votes ou les changer en utilisant cette méthode entraînerait un coût plus élevé. Oui –

+0

implique certainement plus grand coût, mais je pense que le coût est encore plus petit que de faire deux requêtes de jointure sur chaque chargement de page. Aussi la bonne solution dépend de vos besoins, comment vous devriez changer un vote, ou créer. – dombesz