ORM са прекрасни, докато не изтекат . Всички го правят, в крайна сметка. Ecto е млад (т.е. придобива способност само да OR
). където клаузите заедно преди 30 дни
), така че просто не е достатъчно зрял, за да е разработил API, който отчита напреднали SQL обороти.
Проучвайки възможните опции, не сте сами в заявката. Невъзможността за разбиране на списъци на фрагменти (независимо дали като част от order_by
или where
или някъде другаде) е споменат в Ecto issue #1485
, на StackOverflow
, във Elixir форум
и това публикация в блог
. Последното е особено поучително. Повече за това след малко. Първо, нека опитаме някои експерименти.
Експеримент №1: Човек може първо да опита да използва Kernel.apply/3
за да предадете списъка на fragment
, но това няма да работи:
|> order_by(Kernel.apply(Ecto.Query.Builder, :fragment, ^ids))
Експеримент №2: Тогава може би можем да го изградим с манипулация на низове. Какво ще кажете за даването на fragment
низ, вграден по време на изпълнение с достатъчно заместители, за да може да изтегли от списъка:
|> order_by(fragment(Enum.join(["FIELD(id,", Enum.join(Enum.map(ids, fn _ -> "?" end), ","), ")"], ""), ^ids))
Което би произвело FIELD(id,?,?,?)
даден ids = [1, 2, 3]
. Не, това също не работи.
Експеримент №3: Създаване на целия, окончателен SQL, изграден от идентификаторите, поставяне на необработените стойности на ID директно в съставения низ. Освен че е ужасен, той също не работи:
|> order_by(fragment(Enum.join(["FIELD(id,", Enum.join(^ids, ","), ")"], "")))
Експеримент №4: Това ме отвежда до тази публикация в блога, която споменах. В него авторът заобикаля липсата на or_where
използване на набор от предварително дефинирани макроси въз основа на броя на условията за събиране:
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
Въпреки че това работи и използва ORM, така да се каже, "със зърно", изисква да имате краен, управляем брой налични полета. Това може или не може да промени играта.
Моята препоръка:не се опитвайте да заобикаляте течовете на ORM. Знаете най-добрата заявка. Ако ORM не го приеме, напишете го директно с необработен SQL и документирайте защо ORM не работи. Защитете го зад функция или модул, за да можете да си запазите правото в бъдеще да промените изпълнението му. Един ден, когато ORM настигне, можете просто да го пренапишете добре, без да има ефект върху останалата част от системата.