2009-06-05 8 views

Antwort

9

Als Kevin vorgeschlagen hat, ist counter_cache die einfachste Option, was auf jeden Fall würde ich verwenden.

class Author < ActiveRecord::Base 
    has_many :books, :counter_cache => true 
end 

class Book < ActiveRecord::Base 
    belongs_to :author 
end 

Und wenn Sie mit Rails 2.3 und Sie würden dies der Standard sein, wie die Bestellung Sie die neue default_scope Methode verwenden:

class Author < ActiveRecord::Base 
    has_many :books, :counter_cache => true 

    default_scope :order => "books_count DESC" 
end 

books_count ist das Feld, das den Zähler Caching-Verhalten führt, und es gibt wahrscheinlich einen besseren Weg, als es direkt im Standardumfang zu verwenden, aber es gibt Ihnen die Idee und wird die Arbeit erledigen.

EDIT:

Als Reaktion auf den Kommentar zu fragen, ob counter_cache funktionieren wird, wenn eine nicht Rails-Anwendung, die Daten verändert, und es kann, aber nicht in der Art und Weise standardmäßig als Rails-Schritte und dekrementiert den Zähler zu sparen Zeit. Sie können Ihre eigene Implementierung in einen after_save Callback schreiben.

class Author < ActiveRecord::Base 
    has_many :books 

    after_save :update_counter_cache 

    private 
    def update_counter_cache 
     update_attribute(:books_count, self.books.length) unless self.books.length == self.books_count 
    end 
end 

Jetzt haben Sie nicht ein counter_cache installiert, aber wenn Sie das Feld in der Datenbank books_count Namen gemäß der counter_cache Konvention dann, wenn man nach oben schaut:

@Author = Author.find(1) 
puts @author.books.size 

Es wird nach wie vor der Verwendung Zähler im Cache speichern, anstatt eine Datenbanksuche durchzuführen. Natürlich funktioniert das nur, wenn die App "rails" die Tabelle aktualisiert. Wenn also eine andere App etwas tut, sind Ihre Nummern möglicherweise nicht mehr synchron, bis die Rails-Anwendung zurückkehrt und gespeichert werden muss. Der einzige Weg, den ich mir vorstellen kann, ist ein Cron-Job, um Zahlen zu synchronisieren, wenn Ihre Rails-App die Dinge nicht oft genug nachschlägt, um es unwichtig zu machen.

+0

funktioniert Counter-Caching, wenn eine Nicht-Rails-Anwendung die Daten in der Datenbank direkt aktualisiert (d. H. Ein Buch entfernt)? – dplante

+0

Ich vermute nicht, zumindest bis du den Datensatz wieder von der te Rails App speicherst. –

7

Ich empfehle Counter-Caching der Buchzählung als ein weiteres Attribut auf Autor (Rails unterstützt dies mit einer Option für die Zuordnung). Dies ist bei weitem die schnellste Methode, und Rails ist ziemlich gut darin sicherzustellen, dass es die Zählung synchronisiert hält.

+2

Danke für die schnelle und genaue Antwort. Ich benutzte deine Antwort, um nach weiteren Informationen zu suchen und fand http://railscasts.com/episodes/23-counter-cache-column, was auch sehr nützlich ist. Ich habe die Antwort von Railsninja gewählt, weil sie umfassender ist und Passanten besser helfen würde. –

9

Entnommen http://www.ruby-forum.com/topic/164155

Book.find(:all, :select => "author_id, count(id) as book_count", :group => "author_id", :order => "book_count") 
+0

Rechts. Dies ist im Grunde nur eine SQL-Abfrage und das DBMS die Arbeit zu tun, und das ist die einfachste und beste Möglichkeit, es zu tun. Insbesondere möchten Sie dies tun, um zu vermeiden, viele Abfragen (möglicherweise so viele wie eine pro Autor) zu tun, wenn eine Abfrage das tut, was Sie brauchen. –

3

Angenommen Schienen 3

@authors=Authors.include(:books).sort do |a,b| 
    a.books.size <=> b.books.size 
end 

Hinweis: das wird gibt ein Array von Datensätzen, nicht Active: Relation.

Anmerkung2: Verwenden Sie die Größe, nicht die Anzahl, da die Zählung SQL-Aufrufe an die Datenbank ausführen wird.

Verwandte Themen