Please help me write right query

ok, I have users and projects tables which relations is has_many throw users_projects table.

also I have current_user, which is logged in.
I need to get All users with preloaded projects, but projects must be only common/general/shared(don’t know how to say it correctly) with current user.

for ex: user1: {projects: a, b, c} and user2: {projects: a,b, d}.
if user1 is logged in hi must see only user2: {projects: a,b}

here is my unsuccessful try:

project_ids = current_user
|> Repo.preload(:projects)
|> Map.get(:projects)
|> Enum.map(fn (p) -> Integer.to_string(p.id) end)

query = (
  from u in User,
  where: u.id != ^current_user.id,
  join: p in assoc(u, :projects), where: p.id in ^project_ids,
  select: %{
    user: %{
      id: u.id,
      name: u.name,
      email: u.email,
      projects: p
    }
  }
)

Why is it unsuccessful? Is there an error? If so, what does the error say?

in example user1: {projects: a, b, c} and user2: {projects: a, b, d}.
if i use my query in this situation I get this:

[{
  user_name: user1 //user values
  projects: a, //one project, but need [a,b]
 },
 {
  user_name: user1 //user values
  projects: b, //one project, but need [a,b]
 }
 ]

I need this:

[
name: user1
projects: [a, b]
]

Can you collect them afterwards?

With something like Enum.reduce or Enum.group_by?

Enum.group_by(
  results,
  fn %{user_name: user_name} -> user_name end,
  fn %{projects: projects} -> projects end
)
iex(2)> results = [%{
...(2)>   user_name: "user1",
...(2)>   projects: "a",
...(2)>  },
...(2)>  %{
...(2)>   user_name: "user1",
...(2)>   projects: "b"
...(2)>  }
...(2)> ]
[%{projects: "a", user_name: "user1"}, %{projects: "b", user_name: "user1"}]
iex(3)> Enum.group_by(results, fn %{user_name: user_name} -> user_name end, fn %{projects: projects} -> projects end)
%{"user1" => ["a", "b"]}
1 Like

I’ll try to do it, thanks

Together with the query

my_projects_subquery =
  Project
  |> join(:inner, [p], up in UserProject, p.id == up.project_id)
  |> where([p, up], up.user_id == ^current_user.id)
  |> select([p, up], p)

User
|> where([u], u.id != ^current_user.id)
|> join(:inner, [u], up in UserProject, u.id == up.user_id)
|> join(:inner, [u, up], p in Project, up.project_id == p.id)
|> join(:inner, [u, up, p], my_p in subquery(my_projects_subquery), my_p.id == p.id)
|> select([u, up, p, my_p], {u, p})
|> Repo.all()
|> Enum.group_by(
  fn {%User{id: user_id}, _project} -> user_id end,
  fn {_user, project} -> project end
)

Maybe it’s possible to replace Enum.group_by with some SQL …

1 Like

thanks thanks thanks !!! your save my university project )

If you only need the names of the projects, maybe you can use string_agg(expression, delimiter). Then you wouldn’t need the last Enum.group_by

Not tested:

defmacrop string_agg(expression, partition_by) do
  quote do
    fragment("string_agg(?, ' ')", unquote(expression))
  end
end

my_projects_subquery =
  Project
  |> join(:inner, [p], up in UserProject, p.id == up.project_id)
  |> where([p, up], up.user_id == ^current_user.id)
  |> select([p, up], p)

User
|> where([u], u.id != ^current_user.id)
|> join(:inner, [u], up in UserProject, u.id == up.user_id)
|> join(:inner, [u, up], p in Project, up.project_id == p.id)
|> join(:inner, [u, up, p], my_p in subquery(my_projects_subquery), my_p.id == p.id)
|> select([u, up, p, my_p], %{user: u, projects: string_agg(p.name}) # assuming projects have a `name` column
|> Repo.all()