Malformed update in query expression

I was humming along today when suddenly I got stuck on a strange new compile error.

== Compilation error in file lib/contexts/email_context.ex ==
** (Ecto.Query.CompileError) malformed update `%{status_id: "verified"}` in query expression, expected a keyword list with set/push/pop as keys with field-value pairs as values
    expanding macro: Ecto.Query.update/2
    lib/contexts/email_context.ex:54: Auth.Contexts.EmailContext.mark_verified/1
    (elixir) lib/kernel/parallel_compiler.ex:208: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6

the line it’s pointing at is

{:ok, %{id: id, status_id: "unverified"}} -> update(email_id, %{status_id: "verified"})

Here is the relevant module and its functions:

defmodule Auth.Contexts.EmailContext do

  import Ecto.Query, warn: false

  alias Auth.Repo
  alias Auth.Schemas.Email

  def get(email_id) do
    result = Repo.get_by(Email, %{id: email_id})

    case result do
      nil -> {:error, "Email address not found: #{email_id}"}
      _ -> {:ok, result}
    end
  end

  def mark_verified(email_id) do
    case get(email_id) do
      {:ok, %{id: id, status_id: "unverified"}} -> update(email_id, %{status_id: "verified"})
      {:ok, %{status_id: status_id}} -> {:error, "Email status cannot be updated."}
      {:error, msg} -> {:error, msg}
    end
  end


  def update(email_id, attrs) do
    Email
    |> Repo.get_by!(%{id: email_id})
    |> Email.update_changeset(attrs)
    |> Repo.update()
  end
end

Can someone point out what I’m missing here? I thought that was a decent way to enforce a wee bit of business logic on the update, but I was wrong. Thanks for any pointers!

I’m not an ecto user, but the examples in the docs use a keywordlist. Also they use a “command” to specify the kind of update…

Could it be a conflict between Ecto.Query.update and your defined update/2 function?

4 Likes

That was it. I don’t follow exactly why that cropped up but if I put in the fully qualified name to my update command, it worked:

{:ok, %{id: id, status_id: "unverified"}} -> Auth.Contexts.EmailContext.update(email_id, %{status_id: "verified"})

You might be able to use __MODULE__.update as well.

But there are even more options:

  1. import Ecto.Query, exclude: [update: 2]
  2. rename your local update/2 to something else
2 Likes

Glad you got it solved! Also, you should have a compilation warning in this case to say that there is a conflict between the two functions (I think).