J'ai une exigence upsert, j'ai donc besoin d'appeler une procédure stockée postgres ou d'utiliser une expression de table commune. J'utilise également l'extension pgcrypto pour les mots de passe et je voudrais utiliser des fonctions postgres (telles que "crypt" pour encoder/décoder les mots de passe).
Mais je ne peux pas trouver un moyen d'amener Ecto à jouer avec sql brut en partie ou en totalité, est-il prévu que ecto ne supporte que l'élixir dsl et ne permette pas de décortiquer en sql brut lorsque le dsl n'est pas suffisant?
J'ai trouvé que je peux interroger via l'adaptateur (Rocket est le nom de l'application)
q = Ecto.Adapters.Postgres.query(Rocket.Repo,"select * from users limit 1",[])
Mais je ne sais pas comment obtenir cela sur le modèle. Je suis nouveau dans l'élixir et il semble que je devrais pouvoir utiliser Ecto.Model.Schem .schema/3 mais cela échoue
Rocket.User.__schema__(:load,q.rows |> List.first,0)
** (FunctionClauseError) no function clause matching in Rocket.User.__schema__/3
Sur Ecto 2.0 (beta) avec Postgres, vous pouvez utiliser Ecto.Adapters.SQL.query()
( docs actuels , 2.0-beta2 docs ) pour exécuter SQL arbitraire; en plus d'une liste des lignes elles-mêmes ("rows
"), il se trouve qu'il retourne une liste de noms de colonnes ("columns
").
Dans l'exemple ci-dessous, je
(Vous voudrez probablement exécuter la version query()
(sans le bang!) Et vérifier {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
Solution modifiée pour Ecto 2.0:
dans 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
Usage:
Repo.execute_and_load("SELECT * FROM users WHERE id = $1", [1], User)
Maintenant qu'Ecto 1.0 est sorti, cela devrait fonctionner pendant un certain temps:
Ajoutez les fonctions suivantes à votre module 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
Et utiliser comme tel:
Repo.execute_and_load("SELECT * FROM users WHERE id = $1", [1], User)
En plus de Ecto.Adapters.SQL.query/4 , il existe également Ecto.Query.API.fragment/1 , qui peut être utilisé pour envoyer des expressions de requête au base de données. Par exemple, pour utiliser la fonction tableau de Postgres array_upper
, on pourrait utiliser
Ecto.Query.where([x], fragment("array_upper(some_array_field, 1)]" == 1)
Ecto 2.2.8 fournit Ecto.Query.load/2
, vous pouvez donc faire quelque chose comme ceci:
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, au moins à partir de la version ~> 0.7, vous devez utiliser:
Ecto.Adapters.SQL.query/4
def query(repo, sql, params, opts \\ [])
Exécute une requête SQL personnalisée sur un référentiel donné.
En cas de succès, il doit retourner un: ok Tuple contenant une carte avec au moins deux clés:
•: num_rows - le nombre de lignes affectées •: rows - l'ensemble de résultats sous forme de liste. nil peut être renvoyé à la place de la liste si la commande ne donne aucune ligne comme résultat (mais donne toujours le nombre de lignes affectées, comme une commande de suppression sans retourner le ferait)
Les options
•: timeout - Le temps en millisecondes pour attendre la fin de l'appel,: l'infini attendra indéfiniment (par défaut: 5000) •: log - Si faux, n'enregistre pas la requête
Exemples
iex> Ecto.Adapters.SQL.query (MyRepo, "SELECT $ 1 + $ 2", [40, 2])
% {lignes: [{42}], num_rows: 1}
C'est https://stackoverflow.com/users/1758892/thousandsofthem échantillon, mais juste un peu rétréci (crédit: lui/elle)
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