Adding prefix to field values obtained by Ecto.Query.select

I have this:

books =
  Book
  |> MangoWeb.Filters.Books.build_query(conn, _params)
  |> Ecto.Query.select([u], %{code: u.code, title: u.title})
  |> Repo.paginate(page: _params["page"], page_size: _params["per_page"])

My question, how to return u.code as _some_prefix_#{u.code}

I have tried that but getting this error:

(Ecto.Query.CompileError) "_some_prefix_#{u.code()}" is not a valid query expression.

Maybe use some sql function for string concatenation (more bytes sent).

defmacrop with_prefix(code) do
  quote do
    # https://www.postgresql.org/docs/current/static/functions-string.html
    fragment("_some_prefix_ || ?", unquote(code))
  end
end

books =
  Book
  |> MangoWeb.Filters.Books.build_query(conn, _params)
  |> Ecto.Query.select([u], %{code: with_prefix(u.code), title: u.title})
  |> Repo.paginate(page: _params["page"], page_size: _params["per_page"])

Or map over the result list with Enum.map and use elixir’s string interpolation (slow).

books =
  Book
  |> MangoWeb.Filters.Books.build_query(conn, _params)
  |> Ecto.Query.select([u], %{code: u.code, title: u.title})
  |> Repo.paginate(page: _params["page"], page_size: _params["per_page"])
  |> Enum.map(fn %{code: code} = book -> %{book | code: "_some_prefix_#{code}"} end)
2 Likes

In fact, done like this: Ecto.Query.select([u], %{code: fragment("concat('AAB', ?)", u.code), title: u.title})

Thank you.

I prefer using Enum.Map, however I am having some difficulties making it work since the expected structure for books should remain like this:

%Scrivener.Page{
  entries: [
    %Mango.Books.Book{
      __meta__: #Ecto.Schema.Metadata<:loaded, "books">,
      author: "jxhfkj s",
      code: 1130,
      id: 120,
      inserted_at: ~N[2018-05-11 08:55:59.777576],
      institute_id: 1,
      isbn: "dsafsafsd",
      title: "fdjfh",
      updated_at: ~N[2018-05-11 08:55:59.777593],
      year: 1980
    },
    %Mango.Books.Book{
      __meta__: #Ecto.Schema.Metadata<:loaded, "books">,
      author: "eee",
      code: 1129,
      id: 119,
      inserted_at: ~N[2018-05-10 12:21:16.732606],
      institute_id: 1,
      isbn: "eeee",
      title: "eeee",
      updated_at: ~N[2018-05-10 12:21:16.732619],
      year: 232
    },
    %Mango.Books.Book{
      __meta__: #Ecto.Schema.Metadata<:loaded, "books">,
      author: "bbb",
      code: 1128,
      id: 118,
      inserted_at: ~N[2018-05-09 20:48:31.292419],
      institute_id: 1,
      isbn: "56465456465",
      title: "aaaa",
      updated_at: ~N[2018-05-09 20:48:31.292435],
      year: 1980
    }
  ],
  page_number: 1,
  page_size: 20,
  total_entries: 15,
  total_pages: 1
}

I have tired to make some self-learning, but so far no results, only errors :joy:. Books are nested under entries.

%Scrivener.Page{entries: books} = page =
  Book
  |> MangoWeb.Filters.Books.build_query(conn, _params)
  |> Ecto.Query.select([u], %{code: u.code, title: u.title})
  |> Repo.paginate(page: _params["page"], page_size: _params["per_page"])

prefixed_books = Enum.map(books, fn %Mango.Books.Book{code: code} = book ->
  %{book | code: "_some_prefix_#{code}"}
end)

%{page | entries: prefixed_books}
1 Like

Thank you very much. I still need to learn more about maps. What worked for me eventually, of course based on the code you provided above is this:

books =
  Book
  |> MangoWeb.Filters.Books.build_query(conn, _params)
  |> Repo.paginate(page: _params["page"], page_size: _params["per_page"])

prefixed_books = Enum.map(books, fn %Mango.Books.Book{code: code} = book ->
  %{book | code: "_some_prefix_#{code}"}
end)

books = Map.put(books, :entries, prefixed_books)

Scrivener implements the Enumerable protocol, which is why you are able to iterate directly over it.

1 Like