Queries with virtual fields in "inner" associations

select_merge works very well for virtual attributes:

from u in User, select_merge: %{virtual_field: true}

However working with associations it is not that easy. The only solution I found is something to this:

from p in Post,
  join u in assoc(p, :author),
  select_merge: %{author: %{
    id: u.id,
    name: u.name,
    ...
    virtual_field: true,
} }

So I have to write every field in each query, not very practical IMHO. I’m not sure how to do the same when the has_many association either:

from u in User,
  join: p in assoc(u, :posts),
  select_merge: %{posts: ??? }

And working with inner relations can be really hairy:

from u in User,
  join: p in assoc(u, :posts),
  join: i in assoc(p, :images),
  select_merge: # virtual field for images

virtual_field: true is a simple example, usually I work with other joins to build this virtual field —i.e. liked field if the current user likes or not the posts.

So my question: Is there an easier way to work with those virtual fields for “inner” associations??

1 Like

I’m not sure if there is a way to do this currently, but it would definitely be something nice to have. I think it’s worth making a feature request for. I feel like it could work the same as select merge does currently, but just recursively merge nested maps rather than overwriting (which is what I assume it does now). So you could just do:

from u in User,
  join: p in assoc(u, :posts),
  join: i in assoc(p, :images),
  select_merge: %{
    posts: %{
      images: %{
        virtual_field: "foo"
      }
    }
  }

Which would keep all of the existing posts and images fields, but just append virtual_field to each of the images.

It’s straightforward to recursively merge two maps, but I believe in ecto the map syntax is just part of the DSL and not really a map under the hood, so there are probably some challenges there. I could be wrong about that though.

As a workaround have you considered leaving off the virtual fields in the query and deriving them in the results instead? You lose the ability to reference them in query, so it might not be a possibility.