Getting and somehow merging data just for the logged in user (schema limitations)?

I have a lot of generic functions, i.e. the standard CRUD stuff(list, get, create, delete). Thing is, I often have a need to grab data only for the logged in user.

For example I have a list of items that the logged in user either follows or doesn’t follow. Of course, other users also may follow it. It wouldn’t make sense to get all of the follows from the :follows association. I’m not sure how this works with the idea of a “schema” in ecto. I can’t have a “followed_by_me” field in this case only because it doesn’t exist in the schema.

What’s the best way to deal with this? Should I load the follows for all posts through the Ecto.Query.preload/3 function? Should I load the follows separately from the posts using something like the function below?

def list_user_follows(items, user) do
  Item.for_items(items)
  |> Item.followed_by(user)
  |> Repo.all()
end

You likely need either:

  • a query with joins
  • a has_many with through (which is made out of joins)

but it’s hard to be specific without additional detail about the shapes of the tables & schemas involved.

How would you get the information you’re looking for in plain SQL? That can help guide the Ecto implementation.

I don’t mind using a has_many, but is there a way to load arbitrary data into my schema for the one-off situations such as getting the follows for a logged-in user? I’m trying not to have a bunch of list_x_for_user functions cramming up my contexts that are also growing out of control. Also imo it’s a bit weird semantically to have a :follows association that includes only data for the user that’s logged in. Is it common practice to add another struct that encapsulates the actual schema object and enriches it with other contextual data just for the view?

I’m finding that there’s a lot of information out there about how to use elixir’s various libraries, but there’s frighteningly little written about how to build “good” software given elixir’s functional constraints.

You might want to post the schema definitions. It’s hard for others to know what you’re asking, let alone give a good response when they can’t see the data.

One resource that might help is either reading the pragprog book with the bees on the front or googling composable ecto queries.

The association would be between the User schema and the Item schema and not specifically to a logged in user. It’s the same idea as a relationship between two tables in a database defined by foreign keys. You can join on any foreign key and not just the one corresponding to the logged in user.

I’m trying not to have a bunch of list_x_for_user functions cramming up my contexts

You don’t have to do it this way. You can have one function to load user information and have an argument that specified the preloads. Something like this:

def get_user(id, preloads \\ []) do
  from(u in User, where: u.id == ^id, preload: preloads) |> Repo.one()
end

Thanks, I’m doing it that way already for things like use profiles or organization membership because there is a fixed number. But I’m not sure about loading all of the users follows with every connection. They could have anywhere between 0 and hundreds of follows, I don’t want the query result size to be that variable.

How do people normally deal with loading “auxiliary” data in liveview sort of stuff?

If you don’t need it, then don’t preload it. In Joey’s example you choose what you preload. You can also apply a limit on the query if you want to paginate the follows.

If you don’t want to keep something around in the liveview assigns, you can use temporary_assigns to clear it.