Skip to content

skip and take don't work for join tables if primary key is not selected #11662

@kucharskimaciej

Description

@kucharskimaciej

Issue description

Combining a join table with skip and take only returns a single entity of the join table if the primary key is not selected.

Expected Behavior

Assuming this setup of entites:

Organization {
  id, name
}

Workspace {
  id, name
}

OrganizationWorkspaceAssignment {
  organizationId, workspaceId, meta
}

and this QueryBuilder:

const qb = this.repo.createQueryBuilder("organization")
  .leftJoin("organization.workspaceAssignments", "workspaceAssignments")
  .leftJoin("workspaceAssignments.workspace", "workspace")
  .addSelects([
    "organization.id", "organization.name", 
    "workspaceAssignments.meta", 
    "workspace.id", "workspace.name"
  ])
  .skip(0)
  .take(20)

const result = qb.getMany();

I expected this result:

[
  {
     id: "o1",
     name: "Organization 1",
     workspaceAssignments: [
        { meta, workspace: { id: "w1", name: "Workspace 1 } },
        { meta, workspace: { id: "w2", name: "Workspace 2 } },
        { meta, workspace: { id: "w3", name: "Workspace 3 } }
     ]
  },
 ...
]

Actual Behavior

Instead of correctly pulling multiple OrganizationWorkspaceAssignment entities, the output only contained one:

[
  {
     id: "o1",
     name: "Organization 1",
     workspaceAssignments: [
        { meta, workspace: { id: "w1", name: "Workspace 1 } }
     ]
  },
 ...
]

Steps to reproduce

  1. Setup entities and join table:
@Entity("entity1")
class Entity1 {
   @PrimaryColumn({
        type: "uuid",
    })
    id!: string;

  @Column({
        type: "varchar",
        nullable: false,
    })
    name!: string;

   @OneToMany(
        () => JoinTable,
        (jt) => jt.entity1,
        {
            eager: false,
        },
    )
    assignments?: JoinTable[];
}

@Entity("entity2")
class Entity2 {
   @PrimaryColumn({
        type: "uuid",
    })
    id!: string;

  @Column({
        type: "varchar",
        nullable: false,
    })
    name!: string;

    @OneToMany(
        () => JoinTable,
        (jt) => jt.entity2,
        {
            eager: false,
        },
    )
    assignments?: JoinTable[];
}

@Entity("join_table")
class JoinTable {
  @PrimaryColumn({
        type: "uuid",
    })
    entity1Id!: string;

    @PrimaryColumn({
        type: "uuid",
    })
    entity2Id!: string;
    
    @Column({
        type: "jsonb",
        nullable: true,
    })
    meta!: AssignmentAttributes | null;
   
   @ManyToOne(() => Entity1, {
        onDelete: "CASCADE",
        eager: false,
    })
    @JoinColumn({
        name: "entity1Id",
        referencedColumnName: "id",
    })
    readonly entity1?: Entity1;

    @ManyToOne(() => Entity2, {
        onDelete: "CASCADE",
        eager: false,
    })
    @JoinColumn({
        name: "entity2Id",
        referencedColumnName: "id",
    })
    readonly entity2?: Entity2;
}

Add data, so that there's one Entity1 row and multiple Entity2 rows, all assigned via JoinTable to entity1.

Create a QueryBuilder on Entity1 repo, and use .leftJoin, with skip and take without selecting the join_table.entity2Id:

const qb = this.repo.createQueryBuilder("entity1")
  .leftJoin("entity1.assignments", "assignments")
  .leftJoin("assignments.entity2", "entity2")
  .addSelects([
    "entity1.id", "entity1.name", 
    "assignments.meta", 
    "entity2.id", "entity2.name"
  ])
  .skip(0)
  .take(20)

const result = qb.getMany();

Notice that despite there being multiple assignments, result only shows one element in assignments array.

My Environment

Dependency Version
Operating System macOs 15.6.1
Node.js version 22.11.0
Typescript version 5.5.4
TypeORM version 0.3.25

Additional Context

  • if I remove skip and take it does produce the correct result (excluding pagination)
  • if I manually add "workspaceAssignments.workspaceId" select, it produces the correct result
  • if I change .leftJoin to .leftJoinAndSelect it produces the correct result (but pulls extra fields)

Running the query that i got from qb.getQueryAndParams() returns the same number of rows, regardless of presence of the "workspaceAssignments.workspaceId" select, so the base query is not a problem here.

Relevant Database Driver(s)

  • aurora-mysql
  • aurora-postgres
  • better-sqlite3
  • cockroachdb
  • cordova
  • expo
  • mongodb
  • mysql
  • nativescript
  • oracle
  • postgres
  • react-native
  • sap
  • spanner
  • sqlite
  • sqlite-abstract
  • sqljs
  • sqlserver

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

Yes, I have the time, but I don't know how to start. I would need guidance.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions