Ecto.Queryを返すパイプラインでつなぐ際にjoinが重複するときがある
例えば、
def query(condition \\ []) do
Hoge.query_all()
|> Hoge.query_user_name(Keyword.get(condition, :user_name))
|> Hoge.query_user_code(Keyword.get(condition, :user_code))
end
defmodule Hoge do
import Ecto.Query
def query_user_name(query, nil), do: query
def query_user_name(query, name) do
from q in query,
join: u in assoc(q, :user),
where: u.name == ^name
end
def query_user_code(query, nil), do: query
def query_user_name(query, code) do
from q in query,
join: u in assoc(q, :user),
where: u.code == ^code
end
end
のような実装をして、user_name
とuser_code
の両方があるとjoinが重複する。
- Railsのスコープチェインライクのイメージで作っていて、ふとSQLをみると重複していた
- そもそも実装の設計が悪いかもしれない
- つまり、そのコンテキストに沿ったきちんと専用の関数を用意すべきかもしれない
結論
- named_binding を使用して、かつ、そのチェックをすると重複回避ができる
- ただし、名前付けを統一する必要がある
- つまるところ、結合度があがるので、少なくとも同じモジュール内での使用にとどめるべき
def query_user_name(query, name) do
if has_named_binding?(query, :user) do
from q in query,
where: as(:user).name == ^name
else
from q in query,
join: u in assoc(q, :user), as: :user,
where: u.name == ^name
end
end
def query_user_name(query, code) do
if has_named_binding?(query, :user) do
from q in query,
where: as(:user).code == ^code
else
from q in query,
join: u in assoc(q, :user), as: :user,
where: u.code == ^code
end
end