ORM są wspaniałe, dopóki wyciek . W końcu wszyscy to robią. Ecto jest młody (np. zyskał tylko umiejętność OR
klauzule where razem 30 dni temu
), więc po prostu nie jest wystarczająco dojrzałe, aby opracować API, które uwzględnia zaawansowane gyrationy SQL.
Przeglądając możliwe opcje, nie jesteś sam w zapytaniu. Niemożność zrozumienia list we fragmentach (czy to jako część order_by
lub where
lub gdziekolwiek indziej) został wymieniony w Ecto nr 1485
, na StackOverflow
, na Elixir Forum
i to post na blogu
. To ostatnie jest szczególnie pouczające. Wiecej o tym za chwile. Najpierw wypróbujmy kilka eksperymentów.
Eksperyment nr 1: Można najpierw spróbować użyć Kernel.apply/3
przekazać listę do fragment
, ale to nie zadziała:
|> order_by(Kernel.apply(Ecto.Query.Builder, :fragment, ^ids))
Eksperyment nr 2: Wtedy być może uda nam się go zbudować za pomocą manipulacji ciągami. Co powiesz na podanie fragment
ciąg zbudowany w czasie wykonywania z wystarczającą liczbą symboli zastępczych, aby można go było pobrać z listy:
|> order_by(fragment(Enum.join(["FIELD(id,", Enum.join(Enum.map(ids, fn _ -> "?" end), ","), ")"], ""), ^ids))
Co dałoby FIELD(id,?,?,?)
podane ids = [1, 2, 3]
. Nie, to też nie działa.
Eksperyment nr 3: Stworzenie całego, ostatecznego kodu SQL zbudowanego z identyfikatorów, umieszczając surowe wartości identyfikatorów bezpośrednio w złożonym ciągu. Poza tym, że jest okropny, to też nie działa:
|> order_by(fragment(Enum.join(["FIELD(id,", Enum.join(^ids, ","), ")"], "")))
Eksperyment nr 4: To prowadzi mnie do tego wpisu na blogu, o którym wspomniałem. W nim autor oszukuje brak or_where
przy użyciu zestawu predefiniowanych makr opartych na liczbie warunków do połączenia:
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
Chociaż to działa i używa ORM „z ziarnem”, że tak powiem, wymaga posiadania skończonej, możliwej do zarządzania liczby dostępnych pól. To może, ale nie musi, zmienić grę.
Moja rada:nie próbuj żonglować wyciekami ORM. Znasz najlepsze zapytanie. Jeśli ORM go nie zaakceptuje, napisz go bezpośrednio za pomocą surowego SQL i udokumentuj, dlaczego ORM nie działa. Osłoń go za funkcją lub modułem, aby w przyszłości zarezerwować sobie prawo do zmiany jego implementacji. Pewnego dnia, gdy ORM nadrobi zaległości, możesz po prostu ładnie go przepisać bez wpływu na resztę systemu.