Dies ist ein Problem, das ich häufig stolpere. Es gab einige ähnliche Fragen zu diesem Problem, aber keine von ihnen war sehr vollständig (Und sie könnten möglicherweise veraltet sein, da Rails 4 neue Funktionen eingeführt haben könnte, die bei diesem Problem helfen)Sortieren nach Anzahl auf has_many Beziehung
Lassen Sie mich ein einfaches Beispiel für die Problem und die bekannten Wege zu ‚lösen‘ das Problem:
Sagen wir, ich habe ein User
Modell und ein Post
Modell und ein User has_many :posts
Jetzt habe ich eine Top-fünf der Benutzer erhalten möchten mit die meisten Beiträge.
Nachfolgend sind Optionen, die ich kenne, aber sie alle haben ihre eigenen Nachteile:
1)
users = User.all
@top_users = users.sort {|a,b| a.posts.count <=> b.posts.count}.take(5)
Nachteile: Eine Datenbank Anfrage für jeden Benutzer vorgenommen wird, so dass diese Lösung sehr langsam.
2) Verwenden Sie die SQL-Code direkt mit einem Mitglied wird (siehe zum Beispiel this question and answer)
select('users.*, COUNT(posts.id) AS posts_count').joins(:posts).group('users.id').order('posts_count DESC').take(5)
Dies läuft alle Sortierlogik in der Datenbank. Allerdings:
- Wir verwenden eine Menge DB-spezifischen Code (In PostgreSQL zum Beispiel würden wir andere Syntax benötigen). Es wäre besser, wenn möglich, ActiveRecord-Methoden zu verwenden.
- Wenn Sie einen internen Join verwenden, werden Benutzer ohne Posts nie zurückgegeben. Dies ist ein Problem, wenn wir Benutzer auch ohne Posts zurückgeben möchten.
3) Verwenden Sie SQL direkt mit einem OUTER JOIN (siehe zum Beispiel this question and answers)
User.select("users.*, COUNT(posts.id) as posts_count").joins("LEFT OUTER JOIN posts ON posts.user_id = users.id").group("posts.id").order("posts_count DESC")
Dies auch Benutzer ohne Pfosten zurück. Nachteile:
- Noch mehr DB-spezifischen Code als # 2, und noch schwerer zu lesen. Verwenden
4) einen Zähler Cache Spalte (Für eine vollständige Erklärung dieser Technik finden this Railscasts episode)
im Grunde erstellen Sie eine neue Spalte auf den User
, die für die Titel der aktuellen Zählung von posts
hält Benutzer, indem Sie den Wert im Feld jedes Mal ändern, wenn ein neuer Beitrag erstellt oder gelöscht wird.
Dies ist sehr schnell und lesbar. Der Nachteil ist, dass wir dies nur verwenden können, nachdem wir ein neues Feld auf User
definiert haben. In vielen Situationen ist dies akzeptabel, aber es wird schwieriger, flexibel zu sein, da die Benutzertabelle geändert werden muss, damit dies pro Assoziation funktioniert, für die wir unter Umständen eine Top-Fünf erstellen möchten. Da es sich um ein zwischengespeichertes Feld handelt, gibt es Datenbankmanipulationen, die keine Aktualisierung des Feldes auslösen.
Gibt es einen schöneren (lesbaren und effizienten) Weg, dies zu erreichen? Vorzugsweise etwas, das integrierte ActiveRecord-Methoden verwendet.
Es gibt eine andere Option, um Ihre Liste mit Nachteilen hinzuzufügen. Sie können dem 'User'-Modell ein Feld' posts_count' hinzufügen. Wenn Posts seltener hinzugefügt werden als die Top 5 der ausgewählten Benutzer, kann dies ein guter Versuch sein. – Glupo
'In PostgreSQL zum Beispiel würden wir andere Syntax brauchen '- was genau ist DB spezifisch in Beispiel # 2? – EugZol
Anstelle eines expliziten 'LEFT OUTER JOIN' könnten Sie versuchen,' includes (...) 'zu verwenden, was implizit einen' OUTER JOIN' bewirkt. Außerdem scheinen Ihre Abfragen in den Optionen 2 und 3 generisch genug zu sein, dass die Syntax in den SQL-Implementierungen, an denen Sie interessiert sind, üblich ist. – lurker