Join tables connected through join through

I have 3 tables.

This is just simple representation of relations.

 User (many to many :roles  join through :user_roles)
 Role (many to many :users join through :user_roles)
 Role(belongs_to :prof)

I want to filter out users where prof meets specific condition.

But I am confused about how to join these three . The confusion is how i combine the user and role table.
Thanks.

What you are looking for is this:

https://stackoverflow.com/questions/49279809/join-3-tables-using-ecto-query

This will not fully solve the @leaf problem, as it in many-to-many relation when you want to filter out specific relation it will not be so easy. Simplest solution would be if Ecto would support existential queries directly which would greatly simplify this query. Instead we need to write something like:

filtered_user_ids =
  from role in Roles,
    inner_join: ur in "user_roles", on: role.id == ur.role_id,
    group_by: ur.user_id,
    having: ^requested_prof_id not in fragment("array_agg(?)", role.prof_id),
    select: %{user_id: ur.user_id}

from user in User,
  inner_join: ur in subquery(^filtered_user_ids), on: user.id == ur.user_id

I haven’t tested that query though, so it can have some issues.


If you want to use existential queries anyway then you can do:

from user in User,
  where: fragment("""
    NOT EXIST (
      SELECT 1
      FROM user_roles
      INNER JOIN roles
        ON roles.id = user_roles.role_id
      WHERE roles.prof_id = ?
        AND user_roles.user_id = ?
    """,
    ^requested_prof_id,
    user.id
  )

This seems to be working for me:

    |> join(
    :right,
    [u],
    user_roles in UserRoles, on: user_roles.user_id == u.id
  )
  |> join(
    :right,
    [u, user_roles],
    roles in Role, on: user_roles.role_id == role.id
  )
  |> join(
    :right,
    [u, user_roles, roles],
    prof in Prof, on: role.prof_id == prof.id and prof.id in ^prof_ids