如何使用Ecto / Elixir在子查询中选择虚拟属性

时间:2018-06-21 04:52:39

标签: elixir phoenix-framework ecto

job模式看起来像这样:

schema "jobs" do
  # more fields and associations
  field :fulfilled, :boolean, virtual: true
end

我有需要正常工作的SQL:

    SELECT jobs.*, (SELECT COUNT(1) = 0
            AS fulfilled
            FROM slots
            JOIN slot_groups on slots.slot_group_id = slot_groups.id
            JOIN scopes ON slot_groups.scope_id = scopes.id
            WHERE scopes.job_id = 1 AND slots.supplier_crew_member_id IS NULL)
    FROM jobs
    WHERE jobs.id = 1;

但是将其转换为Ecto.Query语法非常困难。

这与我的工作差不多:

def get_job(id) do
  Job
  |> where([j], j.id == ^id)
  |> select_merge([j], ^fulfilled(id))
  |> Repo.one()
end

defp fulfilled(job_id) do
  Api.Slots.Slot
  |> join(:inner, [slot], slot_group in assoc(slot, :slot_group))
  |> join(:inner, [slot, slot_group], scope in assoc(slot_group, :scope))
  |> where([slot, _slot_group, scope], scope.job_id == ^job_id and is_nil(slot.supplier_crew_member_id))
  |> select([_slot, _slot_group, _scope], %{fulfilled: fragment("count(1) = 0 AS fulfilled")})
end

但出现以下错误:

** (ArgumentError) expected a list of fields in `select/2` inside `select`, got: `#Ecto.Query<from s0 in Api.Slots.Slot, join: s1 in assoc(s0, :slot_group), join: s2 in assoc(s1, :scope), where: s2.job_id == ^2 and is_nil(s0.supplier_crew_member_id), select: %{fulfilled: fragment("count(1) = 0 AS fulfilled")}>`
(ecto) lib/ecto/query/builder/select.ex:160: Ecto.Query.Builder.Select.fields!/2
(ecto) lib/ecto/query/builder/select.ex:177: Ecto.Query.Builder.Select.select!/5
(api) lib/api/jobs/jobs.ex:28: Api.Jobs.get_job/1

任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:1)

实际上我最终弄清楚了我需要什么。原来是Subqueries are currently only supported in the from and join fields

我转为使用关键字语法,并创建了一个子查询,该子查询针对外部查询加入,如下所示:

job_slots_fulfilled_query =
  from(
    from jobs in Api.Jobs.Job,
      left_join: scopes in assoc(jobs, :scopes),
      left_join: slot_groups in assoc(scopes, :slot_groups),
      left_join: slots in assoc(slot_groups, :slots),
      where: jobs.organization_id == ^org_id,
      group_by: jobs.id,
      select: %{
        id: jobs.id,
        fulfilled: fragment("every(?)", not is_nil(slots.supplier_crew_member_id))
      }
  )

Repo.all(
  from(
    j in Job,
    left_join: s in subquery(job_slots_fulfilled_query),
    on: j.id == s.id,
    where: j.organization_id == ^org_id,
    select_merge: %{fulfilled: s.fulfilled},
    preload: [:organization, :owner, :job_type, :scopes]
  )
)