Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QueryBuilder joins not working as expected #3812

Closed
fifi98 opened this issue Dec 2, 2022 · 6 comments
Closed

QueryBuilder joins not working as expected #3812

fifi98 opened this issue Dec 2, 2022 · 6 comments
Labels
bug Something isn't working

Comments

@fifi98
Copy link

fifi98 commented Dec 2, 2022

Describe the bug
I am using MikroORM with Nest.js and PostgreSQL. This is how my database model looks like:

image

I want to use the query builder to fetch all the serial numbers and do left join with the log entity. The query I am using is as follows:

this.serialNumberRepository
      .createQueryBuilder('sn')
      .select('*')
      .leftJoinAndSelect('sn.logs', 'l')
      .leftJoinAndSelect('l.step', 's')
      .getResultList();

However it seems that MikroORM is also populating sn.logs.serialNumber relation which it shouldn't according to my query.

Minimal reproduction repo - https://github.com/fifi98/mikro-orm-repro. The query is being executed in the serial-numbers service (src/serial-numbers/serial-numbers.controller.ts).

Stack trace
None

To Reproduce

  1. Clone the minimal reproduction repo - https://github.com/fifi98/mikro-orm-repro
  2. run yarn and then yarn start:dev
  3. Sending a GET request to http://localhost:3000/items/123/serial-numbers will execute the query which populates sn.logs.serialNumber

Expected behavior
The expected results of the executed query should be as follows:

{
            "id": "559fccf7-11f0-4e5a-8e15-ae29b98ddeb3",
            "createdAt": "2022-11-30T15:54:51.000Z",
            "updatedAt": "2022-11-30T15:54:51.000Z",
            "deletedAt": null,
            "serialNumber": 1231,
            "item": "4c81229b-ecc3-4440-a4c7-cf97a35c0ce0",
            "logs": [
                {
                    "id": "a73827bc-70aa-4a2b-aa9f-055fc387a022",
                    "createdAt": "2022-11-30T15:55:06.000Z",
                    "updatedAt": "2022-11-30T15:55:06.000Z",
                    "deletedAt": null,
                    "step": {
                        "id": "1728c421-cf23-4618-9518-e12c385a39ff",
                        "createdAt": "2022-11-30T14:26:16.000Z",
                        "updatedAt": "2022-11-30T14:26:16.000Z",
                        "deletedAt": null,
                        "name": "ASSY",
                        "number": "3",
                        "order": 0,
                        "type": "ASSY"
                    },
                    "operationDate": null,
                    "current": true,
                    "returned": true,
                    "serialNumber": "559fccf7-11f0-4e5a-8e15-ae29b98ddeb3"
                },
                {
                    "id": "728fc9bb-c44a-4310-a397-7a4a2f4620c2",
                    "createdAt": "2022-11-30T15:54:53.000Z",
                    "updatedAt": "2022-11-30T15:55:06.000Z",
                    "deletedAt": "2022-11-30T15:55:06.000Z",
                    "step": {
                        "id": "1728c421-cf23-4618-9518-e12c385a39ff",
                        "createdAt": "2022-11-30T14:26:16.000Z",
                        "updatedAt": "2022-11-30T14:26:16.000Z",
                        "deletedAt": null,
                        "name": "ASSY",
                        "number": "3",
                        "order": 0,
                        "type": "ASSY"
                    },
                    "operationDate": "2022-11-30",
                    "current": false,
                    "returned": false,
                    "serialNumber": "559fccf7-11f0-4e5a-8e15-ae29b98ddeb3"
                },
                {
                    "id": "f29aa5d1-6036-4e00-938f-483581b72184",
                    "createdAt": "2022-11-30T15:55:04.000Z",
                    "updatedAt": "2022-11-30T15:55:06.000Z",
                    "deletedAt": null,
                    "step": {
                        "id": "09b64d55-d583-44ed-8407-65968a150057",
                        "createdAt": "2022-11-30T14:26:16.000Z",
                        "updatedAt": "2022-11-30T14:26:16.000Z",
                        "deletedAt": null,
                        "name": "ASSY",
                        "number": "50",
                        "order": 1,
                        "type": "ASSY"
                    },
                    "operationDate": "2022-11-30",
                    "current": false,
                    "returned": false,
                    "serialNumber": "559fccf7-11f0-4e5a-8e15-ae29b98ddeb3"
                }
            ]
        }

However this is what the query returns:

{
            "id": "559fccf7-11f0-4e5a-8e15-ae29b98ddeb3",
            "createdAt": "2022-11-30T15:54:51.000Z",
            "updatedAt": "2022-11-30T15:54:51.000Z",
            "deletedAt": null,
            "serialNumber": 1231,
            "item": "4c81229b-ecc3-4440-a4c7-cf97a35c0ce0",
            "logs": [
                {
                    "id": "a73827bc-70aa-4a2b-aa9f-055fc387a022",
                    "createdAt": "2022-11-30T15:55:06.000Z",
                    "updatedAt": "2022-11-30T15:55:06.000Z",
                    "deletedAt": null,
                    "step": {
                        "id": "1728c421-cf23-4618-9518-e12c385a39ff",
                        "createdAt": "2022-11-30T14:26:16.000Z",
                        "updatedAt": "2022-11-30T14:26:16.000Z",
                        "deletedAt": null,
                        "name": "ASSY",
                        "number": "3",
                        "order": 0,
                        "type": "ASSY"
                    },
                    "operationDate": null,
                    "current": true,
                    "returned": true,
                    "serialNumber": {
                        "id": "559fccf7-11f0-4e5a-8e15-ae29b98ddeb3",
                        "createdAt": "2022-11-30T15:54:51.000Z",
                        "updatedAt": "2022-11-30T15:54:51.000Z",
                        "deletedAt": null,
                        "serialNumber": 1231,
                        "item": "4c81229b-ecc3-4440-a4c7-cf97a35c0ce0"
                    }
                },
                {
                    "id": "728fc9bb-c44a-4310-a397-7a4a2f4620c2",
                    "createdAt": "2022-11-30T15:54:53.000Z",
                    "updatedAt": "2022-11-30T15:55:06.000Z",
                    "deletedAt": "2022-11-30T15:55:06.000Z",
                    "step": {
                        "id": "1728c421-cf23-4618-9518-e12c385a39ff",
                        "createdAt": "2022-11-30T14:26:16.000Z",
                        "updatedAt": "2022-11-30T14:26:16.000Z",
                        "deletedAt": null,
                        "name": "ASSY",
                        "number": "3",
                        "order": 0,
                        "type": "ASSY"
                    },
                    "operationDate": "2022-11-30",
                    "current": false,
                    "returned": false,
                    "serialNumber": {
                        "id": "559fccf7-11f0-4e5a-8e15-ae29b98ddeb3",
                        "createdAt": "2022-11-30T15:54:51.000Z",
                        "updatedAt": "2022-11-30T15:54:51.000Z",
                        "deletedAt": null,
                        "serialNumber": 1231,
                        "item": "4c81229b-ecc3-4440-a4c7-cf97a35c0ce0"
                    }
                },
                {
                    "id": "f29aa5d1-6036-4e00-938f-483581b72184",
                    "createdAt": "2022-11-30T15:55:04.000Z",
                    "updatedAt": "2022-11-30T15:55:06.000Z",
                    "deletedAt": null,
                    "step": {
                        "id": "09b64d55-d583-44ed-8407-65968a150057",
                        "createdAt": "2022-11-30T14:26:16.000Z",
                        "updatedAt": "2022-11-30T14:26:16.000Z",
                        "deletedAt": null,
                        "name": "ASSY",
                        "number": "50",
                        "order": 1,
                        "type": "ASSY"
                    },
                    "operationDate": "2022-11-30",
                    "current": false,
                    "returned": false,
                    "serialNumber": {
                        "id": "559fccf7-11f0-4e5a-8e15-ae29b98ddeb3",
                        "createdAt": "2022-11-30T15:54:51.000Z",
                        "updatedAt": "2022-11-30T15:54:51.000Z",
                        "deletedAt": null,
                        "serialNumber": 1231,
                        "item": "4c81229b-ecc3-4440-a4c7-cf97a35c0ce0"
                    }
                }
            ]
        }

Additional context
The query that gets executed by MikroORM seems to be correct:

[Nest] 64056 - 02/12/2022, 11:45:53 LOG [MikroORM] [query] select "sn".*, "l"."id" as "l__id", "l"."created_at" as "l__created_at", "l"."updated_at" as "l__updated_at", "l"."deleted_at" as "l__deleted_at", "l"."serial_number_id" as "l__serial_number_id", "l"."step_id" as "l__step_id", "l"."operation_date" as "l__operation_date", "l"."current" as "l__current", "l"."returned" as "l__returned", "s"."id" as "s__id", "s"."created_at" as "s__created_at", "s"."updated_at" as "s__updated_at", "s"."deleted_at" as "s__deleted_at", "s"."name" as "s__name", "s"."number" as "s__number", "s"."order" as "s__order", "s"."type" as "s__type" from "serial_number" as "sn" left join "log" as "l" on "sn"."id" = "l"."serial_number_id" left join "step" as "s" on "l"."step_id" = "s"."id" [took 180 ms]

Thank you for the great ORM and the hard work that's been put into this, I really like using MikroORM! Please let me know if there is any additional information that I should provide.

Versions

Dependency Version
node 16.16.0
typescript 4.7.4
mikro-orm 5.5.3
@mikro-orm/postgresql 5.5.3
@B4nan
Copy link
Member

B4nan commented Dec 2, 2022

Reproductions should be minimal, this is really huge. You don't need a web server, you don't need 10 entities, you don't need dozens of nested folders and files to reproduce this...

Please simplify this to bare minimum, I don't mind a nest app per se, but it should have only one module and controller, no abstractions, no services and other unrelated stuff.

@fifi98
Copy link
Author

fifi98 commented Dec 2, 2022

Hey B4nan, thanks for the fast response! I updated the repo and changed the structure to make it easier and left only 3 related entites which are enough to reproduce the issue. The query builder is now being executed directly in src/serial-numbers/serial-numbers.controller.ts

I thought of leaving nest as it might be a serialization problem related to it.
Thank you!

@fobos531
Copy link

fobos531 commented Dec 2, 2022

Hey, thanks for the awesome ORM, Martin! Really liking it so far.

I've ran into pretty much the same issue as the OP, so I'm definitely subscribing to news on this!

@B4nan
Copy link
Member

B4nan commented Dec 6, 2022

Got it reproduced, this is indeed caused by the missing populate hints, e.g.

// works
const res = await orm.em.find(SerialNumber, {}, { populate: ['logs.step'] });

// fails
const res = await orm.em
  .createQueryBuilder(SerialNumber, 'sn')
  .select('*')
  .leftJoinAndSelect('sn.logs', 'l')
  .leftJoinAndSelect('l.step', 's')
  .getResultList();

Which should be fixable, we should be able to construct the populate hint from qb.leftJoinAndSelect() calls too.

@B4nan B4nan added the bug Something isn't working label Dec 6, 2022
@B4nan B4nan closed this as completed in bfa4962 Dec 8, 2022
@fifi98
Copy link
Author

fifi98 commented Dec 10, 2022

Hey B4nan, thank you for the fix! I can confirm that logs.serialNumber is now not populated in 5.6.0 which is fine, but the leftJoinAndSelect still doesn't work as expected. In the same example repo, the l.step property doesn't seem to be populated (even though the correct query is being executed). Thank you!

@tonyjaimep
Copy link
Contributor

@B4nan getting the same issue as ☝️ where joinAndSelected and leftJoinAndSelected relations are not populated.

My setup looks something like

erDiagram

author ||--o{ book : oneToMany
book ||--o{ comment : oneToMany
book }o--|| bookTemplate : manyToOne


export class AuthorRepository extends EntityRepository<Author> {
  async findAllWithRelations(commenterId: string): Promise<Author[]> {
    return this.qb()
      .select('*')
      .joinAndSelect('books', 'b')
      .joinAndSelect('b.bookTemplate', 'bt')
      .leftJoinAndSelect('b.comments', 'bc', {
        'bc.commenterId': commenterId,
      });
  }
}

The expected result is

[
  {
    id: 'some-author-id',
    books: [
      {
        id: 'some-book-id',
        bookTemplate: {
          id: 'some-book-template-id',
          foo: 'bar',
        },
        comments: [
          {
            id: 'some-comment-id',
            bar: 'baz'
          },
          {
            id: 'other-comment-id',
            bar: 'baz'
          },
        ]
      }
    ]
  }
]

But getting

[
  {
    id: 'some-author-id',
    books: [
      {
        id: 'some-book-id',
        bookTemplate: 'some-book-template-id', // <<< expecting populated bookTemplate
        comments: [
          {
            id: 'some-comment-id',
            bar: 'baz'
          },
          {
            id: 'other-comment-id',
            bar: 'baz'
          },
        ]
      }
    ]
  }
]

Should we configure it to be populated, even if it is already selected with joinAndSelect?
This stopped working as expected as of bfa4962. Reverting this commit in my local environment fixed the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants