Saat Anda melakukannya Something.find(array_of_ids)
di Rails, urutan larik yang dihasilkan tidak bergantung pada urutan array_of_ids
.
Apakah ada cara untuk menemukan dan melestarikan pesanan?
ATM Saya mengurutkan catatan secara manual berdasarkan urutan ID, tetapi itu agak timpang.
UPD: jika mungkin untuk menentukan urutan menggunakan :order
param dan beberapa jenis klausa SQL, lalu bagaimana?
ruby-on-rails
ruby
activerecord
Leonid Shevtsov
sumber
sumber
Jawaban:
Jawabannya hanya untuk mysql
Ada fungsi di mysql yang disebut FIELD ()
Berikut adalah bagaimana Anda dapat menggunakannya di .find ():
Pembaruan: Ini akan dihapus dalam kode sumber Rails 6.1
sumber
FIELDS()
di Postgres?Object.where(id: ids).order("field(id, #{ids.join ','})")
Anehnya, tidak ada yang menyarankan sesuatu seperti ini:
Efisien karena selain membiarkan backend SQL melakukannya.
Sunting: Untuk meningkatkan jawaban saya sendiri, Anda juga dapat melakukannya seperti ini:
#index_by
dan#slice
merupakan tambahan yang sangat berguna di ActiveSupport untuk array dan hashes.sumber
Seperti yang dinyatakan Mike Woodhouse dalam jawabannya , hal ini terjadi karena, di bawah tenda, Rails menggunakan kueri SQL dengan a
WHERE id IN... clause
untuk mengambil semua rekaman dalam satu kueri. Ini lebih cepat daripada mengambil setiap id satu per satu, tetapi seperti yang Anda perhatikan, ini tidak menjaga urutan catatan yang Anda ambil.Untuk memperbaikinya, Anda dapat mengurutkan catatan di tingkat aplikasi sesuai dengan daftar asli ID yang Anda gunakan saat mencari catatan.
Berdasarkan banyak jawaban bagus untuk Mengurutkan array sesuai dengan elemen array lain , saya merekomendasikan solusi berikut:
Atau jika Anda membutuhkan sesuatu yang sedikit lebih cepat (tetapi bisa dibilang agak kurang dapat dibaca) Anda dapat melakukan ini:
sumber
Ini tampaknya berfungsi untuk postgresql ( sumber ) - dan mengembalikan relasi ActiveRecord
dapat diperpanjang untuk kolom lain juga, misalnya untuk
name
kolom, gunakanposition(name::text in ?)
...sumber
["position((',' || somethings.id::text || ',') in ?)", ids.join(',') + ',']
versi lengkap yang berfungsi untuk saya:scope :for_ids_with_order, ->(ids) { order = sanitize_sql_array( ["position((',' || somethings.id::text || ',') in ?)", ids.join(',') + ','] ) where(:id => ids).order(order) }
terima kasih @gingerlime @IrishDubGuySeperti yang saya jawab di sini , saya baru saja merilis permata ( order_as_specified ) yang memungkinkan Anda melakukan pengurutan SQL asli seperti ini:
Sejauh yang saya bisa uji, ini berfungsi secara native di semua RDBMS, dan mengembalikan relasi ActiveRecord yang bisa dirantai.
sumber
Sayangnya, tidak mungkin dalam SQL yang akan berfungsi di semua kasus, Anda harus menulis temuan tunggal untuk setiap catatan atau urutan dalam ruby, meskipun mungkin ada cara untuk membuatnya berfungsi menggunakan teknik berpemilik:
Contoh pertama:
SANGAT CANGGIH
Contoh kedua:
sumber
sorted = arr.map { |val| Model.find(val) }
sorted = arr.map{|id| unsorted.detect{|u|u.id==id}}
Jawaban @Gunchars bagus, tetapi tidak berhasil di luar kotak di Rails 2.3 karena kelas Hash tidak dipesan. Solusi sederhana adalah dengan memperluas kelas Enumerable '
index_by
untuk menggunakan kelas OrderedHash:Sekarang pendekatan @Gunchars akan berhasil
Bonus
Kemudian
sumber
Dengan asumsi
Model.pluck(:id)
kembali[1,2,3,4]
dan Anda menginginkan urutan[2,4,1,3]
Konsepnya adalah dengan memanfaatkan
ORDER BY CASE WHEN
klausa SQL. Sebagai contoh:Di Rails, Anda dapat mencapai ini dengan memiliki metode publik dalam model Anda untuk membangun struktur yang serupa:
Kemudian lakukan:
Hal yang baik tentang ini. Hasil dikembalikan sebagai ActiveRecord Relations (yang memungkinkan Anda untuk menggunakan metode seperti
last
,count
,where
,pluck
, dll)sumber
Ada permata find_with_order yang memungkinkan Anda melakukannya secara efisien dengan menggunakan kueri SQL asli.
Dan itu mendukung
Mysql
danPostgreSQL
.Sebagai contoh:
Jika Anda menginginkan hubungan:
sumber
Di bawah kap,
find
dengan serangkaian id akan menghasilkan klausaSELECT
withWHERE id IN...
, yang seharusnya lebih efisien daripada perulangan melalui id.Jadi permintaan terpenuhi dalam satu perjalanan ke database, tetapi
SELECT
tanpaORDER BY
klausa tidak diurutkan. ActiveRecord memahami ini, jadi kami memperluasfind
sebagai berikut:Jika urutan id dalam array Anda sewenang-wenang dan signifikan (yaitu Anda ingin urutan baris dikembalikan agar sesuai dengan array Anda terlepas dari urutan id yang terkandung di dalamnya) maka saya pikir Anda akan menjadi server terbaik dengan pasca-pemrosesan hasilnya di kode - Anda dapat membuat
:order
klausa tetapi itu akan menjadi sangat rumit dan sama sekali tidak mengungkapkan niat.sumber
:order => id
)Meskipun saya tidak melihatnya disebutkan di mana pun di CHANGELOG, sepertinya fungsi ini telah diubah dengan rilis versi
5.2.0
.Di sini komit memperbarui dokumen yang ditandai dengan
5.2.0
Namun tampaknya juga telah di- backport ke versi5.0
.sumber
Dengan mengacu pada jawabannya di sini
Object.where(id: ids).order("position(id::text in '#{ids.join(',')}')")
bekerja untuk Postgresql.sumber
Ada klausa order di find (: order => '...') yang melakukan ini saat mengambil record. Anda juga bisa mendapatkan bantuan dari sini.
teks tautan
sumber