Hey guys! How do you deal with virtual fields in relationship in Absinthe and Ecto? Say you want to fetch an Artist with their Tracks but each track as a virtual field, poster_url, to fetch to url of the poster column. What’s the easiest way to do that?
The way that Ecto loads to relationship it’s just not obvious for me how to do that. Because the transformer function that I have for populating the poster_url from the poster column is not called when relationships are preloaded wich kind of make sense.
artist = Repo.get(from(Artist, preload: :tracks), artist_id)
tracks = Enum.map(artist.tracks, &with_poster_url/1)
artist = Map.put(artist, :tracks, tracks)
Depending upon what you’re doing, you might want to just query for the tracks separately and then decorate. This would be no different than preloading since both involve two queries. It’s just that preload will automatically add the tracks to the artist struct.
yes it’s almost the same thing except that now it’s for nested relationships. I’m guessing I could do it manually with different queries, but it’s tricky because of GraphQL. The client decides what they want to query, so I don’t always know what they want and mutate that data for that. But your response makes me see something I haven’t before. Thanks!
It’s hard even in the resolver because I don’t know what relationship will be queried from the GraphQL API by the client. And Scrivener Ecto library that I’m using for pagination just doesn’t support pagination for relationships.
I fill the virtual fields recursively after fetching data, e.g.
|> # some Ecto query with dynamic preloads
|> # Repo call
And the fill_virtual_fields/1 function will fill the virtual fields for the entity and all the nested entities; this allows the Context not having to care about which field is virtual or not, and how to fill it. fill_virtual_fields/1 will call an implemented behaviour on all entities that have a need to fill virtual fields.