ORM jsou skvělé, dokud neuniknou . Všichni nakonec ano. Ecto je mladý (např. získal pouze schopnost OR
kde klauzule spolu před 30 dny
), takže prostě není dostatečně vyspělý na to, aby vyvinul API, které zohledňuje pokročilé změny SQL.
Při průzkumu možných možností nejste v žádosti sami. Neschopnost porozumět seznamům ve fragmentech (ať už jako součást order_by
nebo where
nebo kdekoli jinde) byl zmíněn v Ecto vydání #1485
, na StackOverflow
, na Fóru Elixir
a toto příspěvek na blogu
. To pozdější je zvláště poučné. Více o tom za chvíli. Nejprve zkusme nějaké experimenty.
Experiment č. 1: Nejprve můžete zkusit použít Kernel.apply/3
pro předání seznamu fragment
, ale to nebude fungovat:
|> order_by(Kernel.apply(Ecto.Query.Builder, :fragment, ^ids))
Experiment č. 2: Pak to možná můžeme postavit pomocí manipulace s řetězci. Co takhle dát fragment
řetězec vestavěný za běhu s dostatkem zástupných symbolů, aby jej bylo možné stáhnout ze seznamu:
|> order_by(fragment(Enum.join(["FIELD(id,", Enum.join(Enum.map(ids, fn _ -> "?" end), ","), ")"], ""), ^ids))
Což by vytvořilo FIELD(id,?,?,?)
dané ids = [1, 2, 3]
. Ne, tohle taky nefunguje.
Experiment č. 3: Vytváření celého, konečného SQL sestaveného z ID, umístění nezpracovaných hodnot ID přímo do složeného řetězce. Kromě toho, že je to hrozné, to taky nefunguje:
|> order_by(fragment(Enum.join(["FIELD(id,", Enum.join(^ids, ","), ")"], "")))
Experiment č. 4: Tím se dostávám k příspěvku na blogu, který jsem zmínil. Autor v něm hackuje nedostatek or_where
pomocí sady předdefinovaných maker založených na počtu podmínek, které se mají spojit:
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
I když to funguje a používá ORM takříkajíc „s obilím“, vyžaduje to, abyste měli omezený a zvládnutelný počet dostupných polí. To může nebo nemusí změnit hru.
Moje doporučení:nesnažte se žonglovat s úniky ORM. Znáte nejlepší dotaz. Pokud to ORM nepřijme, napište to přímo pomocí nezpracovaného SQL a zdokumentujte, proč ORM nefunguje. Chraňte jej za funkci nebo modul, abyste si mohli vyhradit budoucí právo na změnu jeho implementace. Jednoho dne, až to ORM dožene, ho můžete jednoduše přepsat bez vlivu na zbytek systému.