我有一个upsert要求,所以我需要调用postgres存储过程或使用公用表表达式。我还使用pgcrypto exgtension作为密码,并希望使用postgres函数(例如“crypt”来编码/解码密码)。
但我无法找到一种方法让Ecto部分或全部使用原始sql,它是否意味着ecto只支持elixir dsl并且当dsl不足时不允许shell-out到原始sql? / p>
我发现我可以通过适配器查询(Rocket是应用程序的名称)
q = Ecto.Adapters.Postgres.query(Rocket.Repo,"select * from users limit 1",[])
但不确定如何将其送到模型中。我是elixir的新手,似乎我应该可以使用Ecto.Model.Schem。架构 / 3但是这个失败了
Rocket.User.__schema__(:load,q.rows |> List.first,0)
** (FunctionClauseError) no function clause matching in Rocket.User.__schema__/3
答案 0 :(得分:32)
在Postgres的Ecto 2.0(测试版)上,您可以使用Ecto.Adapters.SQL.query()
(current docs,2.0-beta2 docs)来执行任意SQL;除了行本身列表(“rows
”)之外,它还会返回列名列表(“columns
”)。
在下面的例子中,我
(您可能希望运行query()
版本(没有爆炸!)并检查{ok, res}
。)
qry = "SELECT * FROM users"
res = Ecto.Adapters.SQL.query!(Repo, qry, []) # a
cols = Enum.map res.columns, &(String.to_atom(&1)) # b
roles = Enum.map res.rows, fn(row) ->
struct(MyApp.User, Enum.zip(cols, row)) # c
end
答案 1 :(得分:9)
Ecto 2.0的修改解决方案:
在repo.ex中:
def execute_and_load(sql, params, model) do
Ecto.Adapters.SQL.query!(__MODULE__, sql, params)
|> load_into(model)
end
defp load_into(response, model) do
Enum.map(response.rows, fn row ->
fields = Enum.reduce(Enum.zip(response.columns, row), %{}, fn({key, value}, map) ->
Map.put(map, key, value)
end)
Ecto.Schema.__load__(model, nil, nil, nil, fields,
&Ecto.Type.adapter_load(__adapter__, &1, &2))
end)
end
用法:
Repo.execute_and_load("SELECT * FROM users WHERE id = $1", [1], User)
答案 2 :(得分:7)
现在Ecto 1.0已经出来了,这应该会有一段时间了:
将以下功能添加到Repo
模块中:
def execute_and_load(sql, params, model) do
Ecto.Adapters.SQL.query!(__MODULE__, sql, params)
|> load_into(model)
end
defp load_into(response, model) do
Enum.map response.rows, fn(row) ->
fields = Enum.reduce(Enum.zip(response.columns, row), %{}, fn({key, value}, map) ->
Map.put(map, key, value)
end)
Ecto.Schema.__load__(model, nil, nil, [], fields, &__MODULE__.__adapter__.load/2)
end
end
并按原样使用:
Repo.execute_and_load("SELECT * FROM users WHERE id = $1", [1], User)
答案 3 :(得分:6)
除Ecto.Adapters.SQL.query/4外,还有Ecto.Query.API.fragment/1,可用于将查询表达式发送到数据库。例如,要使用Postgres的数组函数array_upper
,可以使用
Ecto.Query.where([x], fragment("array_upper(some_array_field, 1)]" == 1)
答案 4 :(得分:5)
Ecto 2.2.8提供了Ecto.Query.load/2
,所以你可以这样做:
use Ecto.Repo
def execute_and_load(sql, params, model) do
result = query!(sql, params)
Enum.map(result.rows, &load(model, {result.columns, &1}))
end
答案 5 :(得分:4)
Ecto,至少从版本〜> 0.7你应该使用:
Ecto.Adapters.SQL.query / 4
def query(repo, sql, params, opts \\ [])
在给定的repo上运行自定义SQL查询。
如果成功,它必须返回一个包含至少一个地图的ok元组 两把钥匙:
•:num_rows - 受影响的行数 •:rows - 将结果集设置为列表。可以返回nil而不是 如果命令没有产生任何行作为结果列出(但仍然产生 受影响的行数,如没有返回的删除命令)
选项
•:timeout - 等待呼叫完成的时间(以毫秒为单位), :无穷大将无限期等待(默认值:5000) •:log - 如果为false,则不记录查询
实施例
IEX> Ecto.Adapters.SQL.query(MyRepo," SELECT $ 1 + $ 2",[40,2])
%{rows:[{42}],num_rows:1}
答案 6 :(得分:3)
这是https://stackoverflow.com/users/1758892/thousandsofthem样本,但只是缩小了一点(信用:他/她)
defmodule MyApp.Repo do
[...]
def execute_and_load(sql, params, schema) do
response = query!(sql, params)
Enum.map(response.rows, fn row ->
fields = Enum.zip(response.columns, row) |> Enum.into(%{})
Ecto.Schema.__load__(schema, nil, nil, nil, fields,
&Ecto.Type.adapter_load(__adapter__(), &1, &2))
end)
end
end
答案 7 :(得分:-3)
至少使用ecto 4.0,您可以使用适配器进行查询,然后将结果提供给Ecto.Model。架构 / 3:
q = Ecto.Adapters.Postgres.query(Rocket.Repo,"select * from users limit 1",[])
Rocket.User.__schema__(:load,q.rows |> List.first,0)