benonymus

benonymus

Hey

I have a users - teams many to many setup through a users_teams table.

In the team schema I have the following fields mapping to the users:

    has_many :active_users_teams, UserTeam
    has_many :active_users, through: [:active_users_teams, :user]
    field :active_member_count, :integer, virtual: true, default: nil

In the users table I have these fields that I fill in queries based on the given team:

    field :role_in_selected_team, :string, virtual: true
    field :status_in_selected_team, :string, virtual: true

These work great separately.

Now I am trying to combine the two, so I can preload the users under a team and have these fields on the user loaded as well.

This code is just from messing around:

    user_with_role =
      from(
        ut in UserTeam,
        join: u in assoc(ut, :user),
        select: %User{u | role_in_selected_team: ut.role, status_in_selected_team: ut.status}
      )

    team_with_member_count =
      from(
        t in Team,
        join: ut in assoc(t, :active_users_teams),
        group_by: t.id,
        preload: [:owner, active_users: ^user_with_role],
        select: %Team{t | active_member_count: count(ut.id)}
      )

    teams = Repo.all(team_with_member_count)
    team = Enum.find(teams, fn team -> team.id == 3 end)

    IO.inspect(team.active_users |> length())
    IO.inspect(team.active_users_teams |> length())
    members = Repo.all(user_with_role)
    IO.inspect(members |> length())

I observed this strange thing, that as you see I print out 3 numbers.

The team in question has 25 members.

The output is somehow this:

5
25
25

As you see the latter 2 numbers are 25, the last is the query executed by itself that is used in the active_users field preload.

The middle one is the active_users_teams field form the team schema that gets loaded when the active_users automatically.

Now as you se the first number is not 25, when I use the user_with_role query in the preload of the active_users field.
Note: For running the first query by itself I supplied a static team_id(that matches the incoming one) in the query, else it returns all of them.

Just selecting u in the first query does not change the result.

If I don’t do the custom preload query for active_users it returns 25 users as well.

What am I messing up here?

Any ideas?

Showing Posts 1 to 4

LostKobrakai

LostKobrakai

I’d suggest turning on logging of queries and compare the actual queries issued to the db.

benonymus

benonymus OP

OK here are the logs, I think only this part is relevant from each, only this is different:
1 - custom select in preload:

[debug] QUERY OK source="users_teams" db=0.3ms idle=6.6ms
SELECT u0."id", u1."id", u1."email", u1."name", u0."role", u0."status" 
FROM "users_teams" AS u0 
INNER JOIN "users" AS u1 ON u1."id" = u0."user_id" 
WHERE (u0."team_id" = 3) AND (u0."id" = ANY($1)) 
[[1, 2, 46, 81, 136, 44, 45, 135, 123, 82, 43, 42, 139, 138, 137, 89, 90, 133, 92, 93, 95, 96, 114, 115, 122, 134, 132, 129]]

2 - regular preload:

[debug] QUERY OK source="users" db=0.3ms idle=6.7ms
SELECT u0."id", u0."email", u0."name", u0."id" 
FROM "users" AS u0 WHERE (u0."id" = ANY($1)) 
[[1, 2, 46, 81, 136, 44, 45, 135, 123, 82, 43, 42, 139, 138, 137, 89, 90, 133, 92, 93, 95, 96, 114, 115, 122, 134, 132, 129]]

3 - preload query by itself:

[debug] QUERY OK source="users_teams" db=0.4ms idle=6.8ms
SELECT u1."id", u1."email", u1."name", u0."role", u0."status" 
FROM "users_teams" AS u0 
INNER JOIN "users" AS u1 ON u1."id" = u0."user_id" WHERE (u0."team_id" = 3) []
benonymus

benonymus OP

Ok so the problem seems to lie in the fact that in the 1st one, even though u0 refers to the users_teams table, at the end of the query the list of ids it is being checked again is in fact the users ids.

Which explains why in the regular preload we have the same list of ids at the end of the log, and why that works.

I checked against the db and it confirms this.

Now as to why this happens I don’t really have an idea yet.
Could have something to do with the through in the team schema:

    has_many :active_users, through: [:active_users_teams, :user]
benonymus

benonymus OP

In the docs here: Ecto.Query — Ecto v3.14.0

There is this part:

For has_many :through - it behaves similarly to a regular has_many but note that the IDs received are of the last association. Imagine, for example, a post has many comments and each comment has an author. Therefore, a post may have many comments_authors, written as has_many :comments_authors, through: [:comments, :author]. When preloading authors with a custom function via :comments_authors, the function will receive the IDs of the authors as the last step

It seems to be reasonable to assume that this is what is happening in my query as well.
The user ids are being passed.

What I am trying to do here might not be an indented use case.

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews