Ecto Query as Map and All fields

Hi, is there a way to get the query in a map with all its fields and not declare it one by one:

select: map(q, [:id, :statement])

  def show(conn, %{"id" => chapter_id}) do
    questionsQuery = from q in Question, where: q.chapter_id == ^chapter_id, select: map(q, [:id, :statement])
    questions = Poison.encode!(Repo.all(questionsQuery))
    render(conn, "show.html",  questions: questions)
  end
3 Likes

I’m interested in this as well.

Isn’t the default that is returned when not using select a struct which contains all the fields? If you want to remove the struct name, run Map.from_struct/1 on it afterwards.

1 Like

The exact API in the post is supported: https://hexdocs.pm/ecto/Ecto.Query.API.html#map/2 (maybe not at the time the post was written though - I am too lazy to check it).

I think the OP is objecting to the API that he has presented. He doesn’t like needing to specify the keys.

Oh, thank you! @Qqwy is correct then!

I am not sure if necroing this thread is okay, but I think I have the very same question as OP, but the answer from @Qqwy does not satisfy my needs.

My use case is that I want to query some data from my repo. The data is basically just a defined schema, plus some additional fields coming from a join with another table.
Yes I could implement virtual fields, but it feels a bit weird polluting the general schema definition for a one-off join in one of my queries.

Therefore my idea was to simply select the struct as a map and select_merge additional key-value pairs on top of it.

Here’s the schema definition and my attempted Ecto Query, which is not supported on ecto 3.11.1:

  schema "sensors" do
    field :name, :string
    has_many :readings, Reading
  end

  schema "readings" do
    field :name, :string
    field :value, :float
    field :taken_at, :utc_datetime
    belongs_to :sensor, Sensor
  end

  from(sensor in Sensor,
    join: reading in assoc(sensor, :readings),
    # This API does not work, ** (Ecto.Query.CompileError) `map()` is not a valid query expression.
    select: map(sensor),
    select_merge: %{taken_at: reading.taken_at, value: reading.taken_at}
  )

Of course I could manually select all fields from sensors, but that seems cumbersome and easy to break when I add more fields to the sensors schema later. Maybe there is a better way of dealing with the use case of returning data from joined tables.

You can ask the schema for all its fields.

fields = Sensor.__schema__(:fields)

…,
select: map(sendor, ^fields)
4 Likes