Dynamically fetch data with associations in simple query

ecto

#1

Is it possible to dynamically fetch data using Ecto?

I found &Ecto.Query.API.map/2, but I can’t setup it properly for in dynamic single query.

Let’s say we have simple blog app.

With first example:

query = from p in Post,
  inner_join: a in assoc(p, :author),
  select: map(p, [:author_id, :title, author: [:first_name, :last_name]])
Repo.all(query)

I have list of maps with only :author_id and :title fields.

With second example:

query = from p in Post,
  inner_join: a in assoc(p, :author),
  select: map(p, [:title, :author_id, author: [:first_name, :last_name]])
Repo.all(query)

I received KeyError for :first_name (association not loaded).

Finally with third example:

query = from p in Post,
  preload: :author,
  select: map(p, [:title, :author_id, author: [:first_name, :last_name]])
Repo.all(query)

I received all info which I want, but not without any problems:

  1. Firstly I achieved it in 2 queries instead of 1.
  2. Secondly I was need to add extra field :author_id (as in documentation).

It’s not a big problem with writing such query manually, but I tried to find a way to fetch data dynamically. Can you help me with it?


#2

yes its possible to dynamically fetch data but you have to build your query and then pass it inside the select if you want to fetch the data dynamically.like this:

fields =  fields ++ [{relation_name, Enum.map(value, &String.to_existing_atom/1)}]

here fields are the names of the the fields of the schema you want to select . relation_name is the preload table and Enum.map take fields of that preload table. then you can store this inside the variable and pass it inside select

         from q in queryable,
         select: map(q, ^Enum.uniq(fields))

The main point is to make your code generic for every possible select params.


#3

@script: You got me wrong. I know how to pass variable to query etc.

In original post there are explained my 3 failures.
In my function I’m generating list like:

[:title, author: [:first_name, :last_name]]
# I'm generating everything without relation id.

I would like to dynamically create query with join (i.e. one SQL query) with parameters generated like in example (without relation id witch I do not need), but I could not get work join and select: map(…) together.