Vincit / objection.js

An SQL-friendly ORM for Node.js

Home Page:https://vincit.github.io/objection.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Query to Many-to-many using Objection not working

vividcode opened this issue · comments

My model is simple. I have Users, I have Games, and my User_Games table keep track of what game a user has played.

User_Games is a many to many relation table, which stores games played by users, with just 2 fields: userId, gameId.

I want a query that will give me what games the given user has not played so far.

The following query gives me what I want:

SELECT "Games".id FROM "Games"  LEFT JOIN "User_Games" ON "Games".id = "User_Games"."gameId" 
AND "User_Games"."userId" = '<my_user_id>' 
WHERE "User_Games"."userId" IS NULL LIMIT 5;

However, I am getting 0 rows with the Objection api with the following, with any type of user (whether or not he has entry in User_Games table):

const sqlQuery = knex('Games')
    .leftJoin('User_Games', 'Games.id', 'User_Games.gameId')
    .where('User_Games. userId', <my_user_id>)
    .andWhere(function() {
      this.whereNull('User_Games.gameId')
    })
   .select('Games.id');

What I have already tried:

  • Replacing leftJoin with other type of joins
  • Putting whereNull out of the closure

What am I missing?

P.S. This used to work before, wherein I fetched all games played by user, then fetch all games whose gameId was not in the previous result:

let userSubQuery = UserModel.query()
     .joinRelated("games")
     .select("Games.id")
     .where("Users.userId", "=", <my_user_id>);

const gamePromise = GameModel.query()
     .withGraphFetched("questions")
     .whereNotIn("id", userSubQuery)
     .limit(gameCount)

However, I feel that this approach is laden with performance hit in the first query that will be carried forward in the 2nd part. It would be best to do heavy work on 1st step itself, where I can throttle using the .limit(gameCount). But I am unable to come up with an Objection statement.

Thanks for the time...

Could you try adding .debug() to your query and see to what SQL statement Objection compiles it down to? Please log it here.

Appreciate your response...

Here is the debug mode output:

select "Games"."id" from "Games" left join "User_Games" on "Games"."id" = "User_Games"."gameId" where "User_Games"."userId" = '<my_user_id>' and ("User_Games"."gameId" is null)

To highlight the difference:
In my plain query, the join is performed on "User_Games"."userId" = '<my_user_id>' too, which keeps it outside where condition.

In Objection api call that I mentioned above, it is part of the where condition containing null comparison.

Any idea how to make Objection run my query above, with where condition made part of join?

Anyone?

Had to adopt the raw knex approach to make it work:

  const gameIds = await GameModel.query()
    .select('Games.id')
    .leftJoin('User_Games', join => {
      join
      .on('Games.id', '=', 'User_Games.gameId')
      .on(knex.raw('?? = ?', ['User_Games.userId', <my_user_id>]));
    })
    .whereNull('User_Games.gameId')

No points for guessing who helped arrive at the solution: ChatGPT 😎

Thanks everyone for looking..