在 Ecto 片段中使用 table alias/query 表达式?
Using a table alias/query expression in an Ecto fragment?
以此查询作为 Ecto 版本的基础:
select folder_id, json_agg(p.*)
from folder_memberships inner join profiles p
on p.id=folder_memberships.profile_id
where folder_id in (1234) group by folder_id;
我有这个代码:
# ids=[1234]
from(p in Profile,
join: link in FolderMembership, on: link.profile_id == p.id,
select: [link.folder_id, fragment("json_agg(?) as members", p)],
group_by: link.folder_id,
where: link.folder_id in ^ids
)
|> Repo.all
这让我出现以下错误:
== Compilation error on file lib/profile.ex ==
** (Ecto.Query.CompileError) variable `p` is not a valid query expression.
Variables need to be explicitly interpolated in queries with ^
(ecto) expanding macro: Ecto.Query.select/3
我确定我错过了小学课程,但如果我知道它是什么,我会发疯的。我尝试了多种选择,但我看到的所有示例都在做类似 fragment("json_agg(?)", p.some_field)
的事情,而不是 p
本身。
该解决方案并不完美,因为它需要明确列出所有字段,并且不允许您从结果 JSON.
中排除字段
# ids=[1234]
from(p in Profile,
join: link in FolderMembership, on: link.profile_id == p.id,
select: [link.folder_id, fragment("json_agg((?, ?, ?)::profiles) as members", p.id, p.name, p.created_at)],
group_by: link.folder_id,
where: link.folder_id in ^ids
)
|> Repo.all
json_agg
中问号的数量应与配置文件table中的列数完全相同,并且table中的列顺序应对应fragment
个参数的顺序。我不知道你的架构,所以我 "made up" 3 列 - 我希望你明白了。
我自己在一个简化示例(没有连接)上尝试了这种方法。我用作游乐场的应用程序的源代码是 there.
defmodule Magic do
import Ecto.Query
alias Badging.{Badge, Repo}
@fields Badge.__schema__(:fields)
@source Badge.__schema__(:source)
@questions Enum.map_join(@fields, ", ", fn _ -> "?" end)
@json_agg "json_agg((#{@questions})::#{@source})"
def run do
fields = Badge.__schema__(:fields)
source = Badge.__schema__(:source)
questions = Enum.map_join(fields, ", ", fn _ -> "?" end)
json_agg = "json_agg((#{questions})::#{source})"
from(
b in Badge,
select: [
b.id,
fragment(
"json_agg((?, ?, ?, ?, ?, ?, ?, ?, ?)::badges)",
b.id,
b.identifier,
b.subject,
b.status,
b.color,
b.svg,
b.svg_downloaded_at,
b.inserted_at,
b.updated_at
)
],
group_by: b.id
) |> Repo.all
end
end
我也尝试通过使用 Badge.__schema__(:fields)
和 Badge.__schema__(:source)
使其更加灵活,但偶然发现了 the inability of fragment
to accept variable number of arguments。
这是我目前得到的:
defmodule Magic do
import Ecto.Query
alias Badging.{Badge, Repo}
fields = Badge.__schema__(:fields)
source = Badge.__schema__(:source)
questions = Enum.map_join(fields, ", ", fn _ -> "?" end)
@json_agg "json_agg((#{questions})::#{@source})"
def run do
from(
b in Badge,
select: [
b.id,
fragment(
@json_agg,
field(b, :id), # or just b.id
b.identifier,
b.subject,
b.status,
b.color,
b.svg,
b.svg_downloaded_at,
b.inserted_at,
b.updated_at
)
],
group_by: b.id
) |> Repo.all
end
end
我认为 技术上 可以依赖 __schema__(:fields)
而不是明确列出所有字段。字段列表在编译时已知。我只是在 Elixir/Ecto 中不太擅长宏(目前)。
以此查询作为 Ecto 版本的基础:
select folder_id, json_agg(p.*)
from folder_memberships inner join profiles p
on p.id=folder_memberships.profile_id
where folder_id in (1234) group by folder_id;
我有这个代码:
# ids=[1234]
from(p in Profile,
join: link in FolderMembership, on: link.profile_id == p.id,
select: [link.folder_id, fragment("json_agg(?) as members", p)],
group_by: link.folder_id,
where: link.folder_id in ^ids
)
|> Repo.all
这让我出现以下错误:
== Compilation error on file lib/profile.ex ==
** (Ecto.Query.CompileError) variable `p` is not a valid query expression.
Variables need to be explicitly interpolated in queries with ^
(ecto) expanding macro: Ecto.Query.select/3
我确定我错过了小学课程,但如果我知道它是什么,我会发疯的。我尝试了多种选择,但我看到的所有示例都在做类似 fragment("json_agg(?)", p.some_field)
的事情,而不是 p
本身。
该解决方案并不完美,因为它需要明确列出所有字段,并且不允许您从结果 JSON.
中排除字段 # ids=[1234]
from(p in Profile,
join: link in FolderMembership, on: link.profile_id == p.id,
select: [link.folder_id, fragment("json_agg((?, ?, ?)::profiles) as members", p.id, p.name, p.created_at)],
group_by: link.folder_id,
where: link.folder_id in ^ids
)
|> Repo.all
json_agg
中问号的数量应与配置文件table中的列数完全相同,并且table中的列顺序应对应fragment
个参数的顺序。我不知道你的架构,所以我 "made up" 3 列 - 我希望你明白了。
我自己在一个简化示例(没有连接)上尝试了这种方法。我用作游乐场的应用程序的源代码是 there.
defmodule Magic do
import Ecto.Query
alias Badging.{Badge, Repo}
@fields Badge.__schema__(:fields)
@source Badge.__schema__(:source)
@questions Enum.map_join(@fields, ", ", fn _ -> "?" end)
@json_agg "json_agg((#{@questions})::#{@source})"
def run do
fields = Badge.__schema__(:fields)
source = Badge.__schema__(:source)
questions = Enum.map_join(fields, ", ", fn _ -> "?" end)
json_agg = "json_agg((#{questions})::#{source})"
from(
b in Badge,
select: [
b.id,
fragment(
"json_agg((?, ?, ?, ?, ?, ?, ?, ?, ?)::badges)",
b.id,
b.identifier,
b.subject,
b.status,
b.color,
b.svg,
b.svg_downloaded_at,
b.inserted_at,
b.updated_at
)
],
group_by: b.id
) |> Repo.all
end
end
我也尝试通过使用 Badge.__schema__(:fields)
和 Badge.__schema__(:source)
使其更加灵活,但偶然发现了 the inability of fragment
to accept variable number of arguments。
这是我目前得到的:
defmodule Magic do
import Ecto.Query
alias Badging.{Badge, Repo}
fields = Badge.__schema__(:fields)
source = Badge.__schema__(:source)
questions = Enum.map_join(fields, ", ", fn _ -> "?" end)
@json_agg "json_agg((#{questions})::#{@source})"
def run do
from(
b in Badge,
select: [
b.id,
fragment(
@json_agg,
field(b, :id), # or just b.id
b.identifier,
b.subject,
b.status,
b.color,
b.svg,
b.svg_downloaded_at,
b.inserted_at,
b.updated_at
)
],
group_by: b.id
) |> Repo.all
end
end
我认为 技术上 可以依赖 __schema__(:fields)
而不是明确列出所有字段。字段列表在编译时已知。我只是在 Elixir/Ecto 中不太擅长宏(目前)。