Vincit/objection.js

$fetchGraph returning empty array when it should return data

Open

#2258 opened on Apr 13, 2022

View on GitHub
 (7 comments) (0 reactions) (0 assignees)JavaScript (7,146 stars) (663 forks)batch import
bughelp wanted

Description

I tried asking this in Gitter several weeks ago but didn't get very far.

I'm having a strange issue with $fetchGraph() that I'm sure is something I'm doing wrong. Given these (simplified) models:

class User extends Model {
  static get tableName() {
    return 'users';
  }

  static get relationMappings() {
    return {
      hardwareTokenRegistrations: {
        relation: HasManyRelation,
        modelClass: HardwareTokenRegistration,
        join: {
          from: 'users.id',
          to: 'hardware_token_registrations.user_id',
        },
      },
    };
  }
};

class HardwareTokenRegistration extends Model {
  static get tableName() {
    return 'hardware_token_registrations';
  }

  static get relationMappings() {
    return {
      hardwareToken: {
        relation: HasOneRelation,
        modelClass: HardwareToken,
        join: {
          from: 'hardware_token_registrations.hardware_token_id',
          to: 'hardware_tokens.id',
        },
      },
      user: {
        relation: HasOneRelation,
        modelClass: User,
        join: {
          from: 'hardware_token_registrations.user_id',
          to: 'users.id',
        },
      },
    };
  }
};

Schema:

mfa=# \d users
                                       Table "public.users"
   Column   |           Type           | Collation | Nullable |              Default
------------+--------------------------+-----------+----------+-----------------------------------
 id         | integer                  |           | not null | nextval('users_id_seq'::regclass)
 uuid       | character varying(255)   |           | not null |
 username   | character varying(255)   |           | not null |
 created_at | timestamp with time zone |           |          | CURRENT_TIMESTAMP
 updated_at | timestamp with time zone |           |          | CURRENT_TIMESTAMP
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)
    "users_username_index" btree (username)
    "users_username_unique" UNIQUE CONSTRAINT, btree (username)
    "users_uuid_index" btree (uuid)
    "users_uuid_unique" UNIQUE CONSTRAINT, btree (uuid)
Referenced by:
    TABLE "hardware_token_registrations" CONSTRAINT "hardware_token_registrations_user_id_foreign" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
... others removed ...

mfa=# \d hardware_token_registrations;
                                          Table "public.hardware_token_registrations"
      Column       |           Type           | Collation | Nullable |                         Default
-------------------+--------------------------+-----------+----------+----------------------------------------------------------
 id                | integer                  |           | not null | nextval('hardware_token_registrations_id_seq'::regclass)
 user_id           | integer                  |           | not null |
 hardware_token_id | integer                  |           | not null |
 validation_code   | character varying(255)   |           | not null |
 created_at        | timestamp with time zone |           |          | CURRENT_TIMESTAMP
 updated_at        | timestamp with time zone |           |          | CURRENT_TIMESTAMP
Indexes:
    "hardware_token_registrations_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "hardware_token_registrations_hardware_token_id_foreign" FOREIGN KEY (hardware_token_id) REFERENCES hardware_tokens(id)
    "hardware_token_registrations_user_id_foreign" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE

I have an instance of User and want to fetch its HardwareTokenRegistrations:

let user = await User.query().findById(1);
let userWithRegistrations = await user.$fetchGraph('hardwareTokenRegistrations');

/* 
returns:

User {
  id: 1,
  uuid: 'grahamb',
  username: 'grahamb',
  createdAt: 2021-12-16T23:57:14.000Z,
  updatedAt: 2022-03-30T20:56:59.239Z,
  hardwareTokenRegistrations: []
}
*/

Note that the hardwareTokenRegistrations array is empty. In reality, there is one related record.

The query generated by the $fetchGraph query is:

{
  method: 'select',
  options: {},
  timeout: false,
  cancelOnTimeout: false,
  bindings: [ 1 ],
  __knexQueryUid: 'qCYLMv1SfgVFnMl4FCa_p',
  sql: 'select "hardware_token_registrations".* from "hardware_token_registrations" where "hardware_token_registrations"."user_id" in (?)'
}

If I run the same query manually against the database, the records are retrieved:

mfa=# select "hardware_token_registrations".* from "hardware_token_registrations" where "hardware_token_registrations"."user_id" in (1);
-[ RECORD 1 ]-----+-----------------------------
id                | 1
user_id           | 1
hardware_token_id | 4
validation_code   | 851728
created_at        | 2021-12-16 23:57:14+00
updated_at        | 2022-03-14 21:52:58.75705+00

I can't figure out what I'm doing wrong here. Any clues?

Contributor guide