I have this User
schema and a UserRelationship
schema that define a tutor-student relationship between users:
User
has_many :students UserRelationship, foreign_key: :instructor_id
has_many :instructors, UserRelationship, foreign_key: :student_id
UserRelationship
field :discipline, Ecto.Enum, values: [:maths, :science]
belongs_to :student, User, foreign_key: :student_id
belongs_to :instructor, User, foreign_key: :instructor_id
Now I want to query an user and get his/hers instructors, but It’d like the query to return a list of users in the instructors
field, like so:
%User {
name: "John Doe",
instructors: [
%User{name: "Jane Doe"}, %User{name: "Another John"}
]
}
But as it is, if I just query for the user and preload the instructors field, I get a list of UserRelationship
structs.
I’ve been playing with different ways to use preload and joins to achieve it, and I’m stuck.
It did however raised some questions.
This is the “closed” I got from an answer:
from(user in User,
where: user.username == ^username,
preload: [
instructors: ^from(relationship in UserRelationship
where: relationship.discipline == :maths,
select: relationship.instructor_id
]
|> Repo.one()
It returns a list of ID of the instructors, but:
-
If I try to use
relationship.instructor
onselect:
it gives me an error saying the field is a virtual field. I understand association fields are virtual, but why would this be an issue here? If I return justrelationship
on select, theinstructor
field will be available there (provided I preload it, of course), so why wouldn’t be available if I try to access it directly? -
If I preload
instructor
and don’t use aselect:
, everything works as expected (i.e. the field is preloaded and it returns all the rest of the query result with it). But if I keep thepreload:
and add aselect:
it gives me an error (even if I don’t reference the preloaded field):
(ArgumentError) you attempted to apply :__struct__ on <<182, 26, 243, 36, 249, 45, 70, 73, 178, 38, 184, 13, 194, 198, 141, 176>>. If you are using apply/3, make sure the module is an atom. If you are using the dot syntax, such as map.field or module.function(), make sure the left side of the dot is an atom or a map
I don’t understand this error, but it seems that just the fact you do a preload in a query if fundamentally changes the query structure?
- Finally, is there a way to apply
select
on preload fields?
I apologize this is huge. Haha
Thanks!