ORM luar biasa, sampai mereka bocor . Semua lakukan, akhirnya. Ecto masih muda (misalnya, hanya memperoleh kemampuan untuk OR
di mana klausa bersama 30 hari yang lalu
), jadi belum cukup matang untuk mengembangkan API yang mempertimbangkan putaran SQL tingkat lanjut.
Mensurvei opsi yang memungkinkan, Anda tidak sendirian dalam permintaan tersebut. Ketidakmampuan untuk memahami daftar dalam fragmen (baik sebagai bagian dari order_by
atau where
atau di mana pun) telah disebutkan di Ecto issue #1485
, di StackOverflow
, di Forum Ramuan Ramuan
dan entri blog
. Yang kemudian sangat instruktif. Lebih lanjut tentang itu sebentar lagi. Pertama, mari kita coba beberapa eksperimen.
Eksperimen #1: Seseorang mungkin pertama kali mencoba menggunakan Kernel.apply/3
untuk meneruskan daftar ke fragment
, tapi itu tidak akan berhasil:
|> order_by(Kernel.apply(Ecto.Query.Builder, :fragment, ^ids))
Eksperimen #2: Maka mungkin kita bisa membangunnya dengan manipulasi string. Bagaimana kalau memberikan fragment
string yang dibuat saat runtime dengan placeholder yang cukup untuk ditarik dari daftar:
|> order_by(fragment(Enum.join(["FIELD(id,", Enum.join(Enum.map(ids, fn _ -> "?" end), ","), ")"], ""), ^ids))
Manakah yang akan menghasilkan FIELD(id,?,?,?)
diberikan ids = [1, 2, 3]
. Tidak, ini juga tidak berhasil.
Eksperimen #3: Membuat keseluruhan, SQL final yang dibangun dari id, menempatkan nilai ID mentah langsung di string yang dibuat. Selain mengerikan, itu juga tidak berhasil:
|> order_by(fragment(Enum.join(["FIELD(id,", Enum.join(^ids, ","), ")"], "")))
Eksperimen #4: Ini membawa saya ke posting blog yang saya sebutkan. Di dalamnya, penulis meretas kekurangan or_where
menggunakan sekumpulan makro yang telah ditentukan sebelumnya berdasarkan jumlah kondisi untuk digabungkan:
defp orderby_fragment(query, [v1]) do
from u in query, order_by: fragment("FIELD(id,?)", ^v1)
end
defp orderby_fragment(query, [v1,v2]) do
from u in query, order_by: fragment("FIELD(id,?,?)", ^v1, ^v2)
end
defp orderby_fragment(query, [v1,v2,v3]) do
from u in query, order_by: fragment("FIELD(id,?,?,?)", ^v1, ^v2, ^v3)
end
defp orderby_fragment(query, [v1,v2,v3,v4]) do
from u in query, order_by: fragment("FIELD(id,?,?,?)", ^v1, ^v2, ^v3, ^v4)
end
Meskipun ini berfungsi dan menggunakan ORM "dengan gandum" untuk berbicara, ini mengharuskan Anda memiliki jumlah bidang yang tersedia dan dapat dikelola. Ini mungkin atau mungkin bukan pengubah permainan.
Rekomendasi saya:jangan mencoba menyulap kebocoran ORM. Anda tahu pertanyaan terbaik. Jika ORM tidak menerimanya, tulis langsung dengan SQL mentah, dan dokumentasikan mengapa ORM tidak berfungsi. Lindungi di balik fungsi atau modul sehingga Anda dapat memiliki hak di masa mendatang untuk mengubah implementasinya. Suatu hari, ketika ORM menyusul, Anda dapat menulis ulang dengan baik tanpa efek pada sistem lainnya.