typeorm / typeorm

ORM for TypeScript and JavaScript. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, SAP Hana, WebSQL databases. Works in NodeJS, Browser, Ionic, Cordova and Electron platforms.
http://typeorm.io
MIT License
34.03k stars 6.27k forks source link

loadManyToManyRelationIdsAndGroup returns undefined `related` for existing entities #7152

Open Aleeexx opened 3 years ago

Aleeexx commented 3 years ago

Issue Description

Until yesterday all of our entities had an integer id. Now, some of them do have an UUID instead. So, their id property is of type string and saved in a BIN(16) column. In order to query an UUID entity, we need to transform the UUID string into a Buffer. Sadly TypeORM does not do that on querying relations (having a binary id) but we can work around that by, among other places, using a transformer in the entity.

@RelationId((table1Entity: Table1Entity) => table1Entity.table2Entity)
@Column({ transformer: new UUIDTransformer() })   // <-- Transform string to buffer and from buffer to string
public propertyName;

Since we do use a DataLoader, we use RelationIdLoader#loadManyToManyRelationIdsAndGroup to perform batch loading.

// If we have entityMetadatas, we can create the loaders for the typeorm relations
    if (connection.entityMetadatas) {
      // For every entity, we create an entry in the loaders
      connection.entityMetadatas.forEach(entityMetadata => {
        const resolverName = entityMetadata.targetName;
        if (!resolverName) return;

        // Add the loader for the entity
        if (!loaders[resolverName]) loaders[resolverName] = {};

        // For every relation, we create a dataLoader
        entityMetadata.relations.forEach(relation => {
          if (!loaders[resolverName][relation.propertyName]) {
            // Create the new loader
            loaders[resolverName][relation.propertyName] = new DataLoader((entities: any[]) => {
              return connection.relationIdLoader.loadManyToManyRelationIdsAndGroup(relation, entities).then(groups => groups.map(group => group.related));
            });
          }
        });
      });
    }

Expected Behavior

Using RelationIdLoader#loadManyToManyRelationIdsAndGroup should give us the related entities of the given UUID entities, as it still works for entities with an integer id (not needing a transformer).

Actual Behavior

The related property of RelationIdLoader#loadManyToManyRelationIdsAndGroup `s return value is undefined for every entity, since the transformer is not applied for the ids (see first comment in this issue).

const result = await this.relationIdLoader.loadManyToManyRelationIdsAndGroup(relation, entities);
console.log(result);
/*
[
  {
    entity: { id: <Buffer 10 eb 35 83 dd 7e 6c 50 87 5e 71 2c 19 bb a1 5a> },
    related: undefined,
  },
  ...
]
*/

My Environment

Dependency Version
Node.js version v10.20.1
Typescript version v3.8.3
TypeORM version v0.2.24
<!-- Operating System -->

Relevant Database Driver(s)

Are you willing to resolve this issue by submitting a Pull Request?

Aleeexx commented 3 years ago

Further investigation showed, the specific load*() methods (like loadForOneToManyAndOneToOneNotOwner etc.) from the RelationIdLoader try to match the child entity with a parent entity by on the one hand the relationIds and on the other the relatedEntities, which are fetched by getRawMany().

The entries of the relationIds array do have still have the ID as a buffer (RowDataPacket) but the IDs of the entries of the relatedEntities array were transformed back into an uuid (Instantiated entities).

So, the comparison will never successfully match for related Entities and therefore related will always be undefined.