Wenn Sie dies Something.find(array_of_ids)
in Rails tun , hängt die Reihenfolge des resultierenden Arrays nicht von der Reihenfolge von ab array_of_ids
.
Gibt es eine Möglichkeit, die Bestellung zu finden und zu bewahren?
Geldautomat Ich sortiere die Datensätze manuell nach der Reihenfolge der IDs, aber das ist irgendwie lahm.
UPD: Wenn es möglich ist, die Reihenfolge mit dem :order
Parameter und einer Art SQL-Klausel anzugeben , wie dann?
ruby-on-rails
ruby
activerecord
Leonid Shevtsov
quelle
quelle
Antworten:
Die Antwort ist nur für MySQL
In MySQL gibt es eine Funktion namens FIELD ()
So können Sie es in .find () verwenden:
Update: Dies wird im Rails 6.1 Rails-Quellcode entfernt
quelle
FIELDS()
in Postgres?Object.where(id: ids).order("field(id, #{ids.join ','})")
Seltsamerweise hat niemand so etwas vorgeschlagen:
So effizient wie es nur geht, abgesehen davon, dass das SQL-Backend dies tun kann.
Bearbeiten: Um meine eigene Antwort zu verbessern, können Sie dies auch folgendermaßen tun:
#index_by
und#slice
sind ziemlich praktische Ergänzungen in ActiveSupport für Arrays bzw. Hashes.quelle
Wie Mike Woodhouse in seiner Antwort feststellte , geschieht dies, weil Rails unter der Haube eine SQL-Abfrage mit a verwendet
WHERE id IN... clause
, um alle Datensätze in einer Abfrage abzurufen. Dies ist schneller als das Abrufen jeder einzelnen ID, aber wie Sie bemerkt haben, wird die Reihenfolge der Datensätze, die Sie abrufen, nicht beibehalten.Um dies zu beheben, können Sie die Datensätze auf Anwendungsebene gemäß der ursprünglichen Liste der IDs sortieren, die Sie beim Nachschlagen des Datensatzes verwendet haben.
Aufgrund der vielen hervorragenden Antworten zum Sortieren eines Arrays nach den Elementen eines anderen Arrays empfehle ich die folgende Lösung:
Oder wenn Sie etwas schnelleres (aber wohl etwas weniger lesbares) benötigen, können Sie dies tun:
quelle
Dies scheint für postgresql ( Quelle ) zu funktionieren - und gibt eine ActiveRecord-Beziehung zurück
kann auch für andere Spalten erweitert werden, zB für die
name
Spalte, verwenden Sieposition(name::text in ?)
...quelle
["position((',' || somethings.id::text || ',') in ?)", ids.join(',') + ',']
Vollversion, die für mich funktioniert hat:scope :for_ids_with_order, ->(ids) { order = sanitize_sql_array( ["position((',' || somethings.id::text || ',') in ?)", ids.join(',') + ','] ) where(:id => ids).order(order) }
danke @gingerlime @IrishDubGuyAls ich hier antwortete , habe ich gerade ein Juwel ( order_as_specified ) veröffentlicht, mit dem Sie native SQL-Bestellungen wie folgt ausführen können:
Soweit ich testen konnte, funktioniert es nativ in allen RDBMS und gibt eine ActiveRecord-Beziehung zurück, die verkettet werden kann.
quelle
In SQL ist dies leider nicht in allen Fällen möglich. Sie müssten entweder einzelne Fundstücke für jeden Datensatz schreiben oder in Ruby bestellen, obwohl es wahrscheinlich eine Möglichkeit gibt, ihn mit proprietären Techniken zum Laufen zu bringen:
Erstes Beispiel:
Sehr ineffizient
Zweites Beispiel:
quelle
sorted = arr.map { |val| Model.find(val) }
sorted = arr.map{|id| unsorted.detect{|u|u.id==id}}
Die Antwort von @Gunchars ist großartig, funktioniert jedoch in Rails 2.3 nicht sofort, da die Hash-Klasse nicht bestellt ist. Eine einfache Problemumgehung besteht darin, die Enumerable-Klasse zu erweitern, um die OrderedHash-Klasse
index_by
zu verwenden:Jetzt wird der Ansatz von @Gunchars funktionieren
Bonus
Dann
quelle
Angenommen, Sie
Model.pluck(:id)
kehren zurück[1,2,3,4]
und möchten die Reihenfolge von[2,4,1,3]
Das Konzept besteht darin, die
ORDER BY CASE WHEN
SQL-Klausel zu verwenden. Beispielsweise:In Rails können Sie dies erreichen, indem Sie eine öffentliche Methode in Ihrem Modell haben, um eine ähnliche Struktur zu erstellen:
Dann mach:
Das Gute daran. Ergebnisse zurückgegeben werden als Active Relations (so dass Sie Methoden verwenden , wie
last
,count
,where
,pluck
, usw.)quelle
Es gibt ein Juwel find_with_order , mit dem Sie es mithilfe der nativen SQL-Abfrage effizient ausführen können.
Und es unterstützt sowohl
Mysql
undPostgreSQL
.Beispielsweise:
Wenn Sie eine Beziehung wünschen:
quelle
Unter der Haube wird
find
mit einem Array von IDsSELECT
eineWHERE id IN...
Klausel mit einer Klausel generiert , die effizienter sein sollte als das Durchlaufen der IDs.Die Anfrage wird also in einer Fahrt in die Datenbank erfüllt, aber
SELECT
s ohneORDER BY
Klauseln sind unsortiert. ActiveRecord versteht dies und erweitert unser Angebotfind
wie folgt:Wenn die Reihenfolge der IDs in Ihrem Array willkürlich und signifikant ist (dh Sie möchten, dass die Reihenfolge der zurückgegebenen Zeilen unabhängig von der darin enthaltenen Reihenfolge der IDs mit Ihrem Array übereinstimmt), sind Sie meiner Meinung nach der beste Server, wenn Sie die Ergebnisse in nachbearbeiten Code - Sie könnten eine
:order
Klausel erstellen , aber sie wäre teuflisch kompliziert und überhaupt nicht aufschlussreich.quelle
:order => id
)Obwohl ich es nirgendwo in einem CHANGELOG erwähnt sehe, sieht es so aus, als ob diese Funktionalität mit der Veröffentlichung der Version geändert wurde
5.2.0
.Hier verpflichten Sie sich, die mit getaggten Dokumente zu aktualisieren.
5.2.0
Es scheint jedoch auch in die Version zurückportiert worden zu sein5.0
.quelle
Mit Bezug auf die Antwort hier
Object.where(id: ids).order("position(id::text in '#{ids.join(',')}')")
arbeitet für Postgresql.quelle
In find (: order => '...') gibt es eine order-Klausel, die dies beim Abrufen von Datensätzen tut. Hier können Sie auch Hilfe bekommen.
Link Text
quelle