elixir - docs - phoenix ecto
Cómo usar raw sql con ecto Repo (8)
Tengo un requisito de recuperación, así que necesito llamar a un procedimiento almacenado de postgres o usar una expresión de tabla común. También uso la exgtension pgcrypto para contraseñas y me gustaría utilizar funciones postgres (como "crypt" para codificar / decodificar contraseñas).
Pero no puedo encontrar una manera de hacer que Ecto juegue con sql en bruto en parte o en su totalidad, ¿se pretende que ecto solo admita el elixir dsl y no permita que se desvanezcan en sql en bruto cuando el dsl no sea suficiente?
Descubrí que puedo consultar a través del adaptador (Rocket es el nombre de la aplicación)
q = Ecto.Adapters.Postgres.query(Rocket.Repo,"select * from users limit 1",[])
Pero no estoy seguro de cómo conseguir esto para el modelo. Soy nuevo en elixir y parece que debería poder usar Ecto.Model.Schem. esquema / 3 pero esto falla
Rocket.User.__schema__(:load,q.rows |> List.first,0)
** (FunctionClauseError) no function clause matching in Rocket.User.__schema__/3
Además de Ecto.Adapters.SQL.query/4 , también existe Ecto.Query.API.fragment/1 , que se puede usar para enviar expresiones de consulta a la base de datos. Por ejemplo, para usar la función de matriz de Postgres array_upper
, uno podría usar
Ecto.Query.where([x], fragment("array_upper(some_array_field, 1)]" == 1)
Ahora que Ecto 1.0 está fuera, esto debería funcionar por un tiempo:
Agregue las siguientes funciones a su módulo 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
Y usar como tal:
Repo.execute_and_load("SELECT * FROM users WHERE id = $1", [1], User)
Con al menos ecto 4.0 puede consultar usando el adaptador y luego alimentar los resultados a Ecto.Model. esquema / 3:
q = Ecto.Adapters.Postgres.query(Rocket.Repo,"select * from users limit 1",[])
Rocket.User.__schema__(:load,q.rows |> List.first,0)
Ecto 2.2.8 proporciona Ecto.Query.load/2
, por lo que puede hacer algo como esto:
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
Ecto, al menos a partir de la versión ~> 0.7 deberías usar:
Ecto.Adapters.SQL.query / 4
def query(repo, sql, params, opts // [])
Ejecuta consultas SQL personalizadas en un repositorio dado.
En caso de éxito, debe devolver un: ok tupla que contenga un mapa con al menos dos claves:
•: num_rows: el número de filas afectadas •: rows: el resultado se establece como una lista. Se puede devolver nil en lugar de la lista si el comando no arroja ninguna fila como resultado (pero sigue produciendo el número de filas afectadas, como un comando de eliminación sin devolverlo)
Opciones
•: tiempo de espera - El tiempo en milisegundos para esperar que finalice la llamada,: infinito esperará indefinidamente (valor predeterminado: 5000) •: registro - Cuando es falso, no registra la consulta
Ejemplos
iex> Ecto.Adapters.SQL.query (MyRepo, "SELECCIONAR $ 1 + $ 2", [40, 2])
% {rows: [{42}], num_rows: 1}
En Ecto 2.0 (beta) con Postgres, puede usar Ecto.Adapters.SQL.query()
( documentos actuales , documentos 2.0-beta2 ) para ejecutar SQL arbitrario; además de una lista de las filas (" rows
"), resulta que devuelve una lista de nombres de columnas (" columns
").
En el siguiente ejemplo, yo
- ejecutar una consulta personalizada sin parámetros,
- convertir los nombres de columna del resultado de cadenas a átomos, y
- combine esos con cada fila de los resultados y Kernel.struct() en una estructura con Kernel.struct()
(Probablemente quiera ejecutar la versión query()
(¡sin el bang!) Y verificar {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
Esta es muestra, pero solo se encogió un poco (crédito: él / ella)
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
Solución modificada para Ecto 2.0:
en 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
Uso:
Repo.execute_and_load("SELECT * FROM users WHERE id = $1", [1], User)