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

Entities with multiple One-to-One relations aren't loaded from non-owner side. #2821

Closed
jamesmeneghello opened this issue Feb 25, 2022 · 2 comments

Comments

@jamesmeneghello
Copy link
Contributor

jamesmeneghello commented Feb 25, 2022

Describe the bug

Relations on entities that have multiple one-to-one relations to the same entity aren't being loaded from the non-owner side when specifically requested through populate.

Stack Trace

GH issue 28xx › multiple 1-1 relations should be loaded from non-owner side

    expect(received).toBeTruthy()

    Received: null

      92 |
      93 |     const leg = await orm.em.findOne(Leg, 1, { populate: ['purchasePosition'] });
    > 94 |     expect(leg?.purchasePosition).toBeTruthy();
         |                                   ^
      95 |   });
      96 |
      97 |   test(`single 1-1 relations aren't loaded from non-owner side`, async () => {

      at Object.<anonymous> (tests/issues/GH28xx.test.ts:94:35)

To Reproduce

import { Entity, MikroORM, OneToOne, PrimaryKey } from '@mikro-orm/core';
import type { SqliteDriver } from '@mikro-orm/sqlite';

@Entity()
export class Position {

  @PrimaryKey()
  id!: number;

  // eslint-disable-next-line @typescript-eslint/no-use-before-define
  @OneToOne(() => Leg, (leg: Leg) => leg.purchasePosition, { owner: true, nullable: true })
  purchase?: any;

  // eslint-disable-next-line @typescript-eslint/no-use-before-define
  @OneToOne(() => Leg, (leg: Leg) => leg.salePosition, { owner: true, nullable: true })
  sale?: any;

}

@Entity()
export class Leg {

  @PrimaryKey()
  id!: number;

  @OneToOne(() => Position, (position: Position) => position.purchase, { nullable: true })
  purchasePosition?: Position;

  @OneToOne(() => Position, (position: Position) => position.sale, { nullable: true })
  salePosition?: Position;

}

@Entity()
export class Position2 {

  @PrimaryKey()
  id!: number;

  // eslint-disable-next-line @typescript-eslint/no-use-before-define
  @OneToOne(() => Leg2, (leg: Leg2) => leg.position, { owner: true, nullable: true })
  leg?: any;

}

@Entity()
export class Leg2 {

  @PrimaryKey()
  id!: number;

  @OneToOne(() => Position2, (position: Position2) => position.leg, { nullable: true })
  position?: Position2;

}

describe('GH issue 2821', () => {

  let orm: MikroORM<SqliteDriver>;

  beforeAll(async () => {
    orm = await MikroORM.init({
      type: 'sqlite',
      dbName: ':memory:',
      entities: [Position, Leg, Position2, Leg2],
    });
    await orm.getSchemaGenerator().createSchema();
  });

  beforeEach(async () => {
    await orm.getSchemaGenerator().refreshDatabase();
  });

  afterAll(async () => {
    await orm.close(true);
  });

  test(`multiple 1-1 relations should be loaded from non-owner side`, async () => {
    const p = orm.em.create(Leg, {});
    orm.em.persist(p);

    const s = orm.em.create(Leg, {});
    orm.em.persist(s);

    const pos = orm.em.create(Position, { purchase: p, sale: s });
    await orm.em.persistAndFlush(pos);

    orm.em.clear();

    const posReload = await orm.em.findOne(Position, 1, { populate: true });
    expect(posReload?.purchase).toBeTruthy();

    const leg = await orm.em.findOne(Leg, 1, { populate: ['purchasePosition'] });
    expect(leg?.purchasePosition).toBeTruthy();
  });

  test(`single 1-1 relations are loaded from non-owner side`, async () => {
    const p = orm.em.create(Leg2, {});
    orm.em.persist(p);

    const pos = orm.em.create(Position2, { leg: p });
    await orm.em.persistAndFlush(pos);

    orm.em.clear();

    const posReload = await orm.em.findOne(Position2, 1, { populate: true });
    expect(posReload?.leg).toBeTruthy();

    const leg = await orm.em.findOne(Leg2, 1, { populate: ['position'] });
    expect(leg?.position).toBeTruthy();
  });

});

Expected behavior

As in test, purchasePosition should be accessible from the non-owner side.

Additional context

Versions

Dependency Version
node 16.10
typescript 4.4.4
mikro-orm master, 5.0.5-dev7
your-driver postgres, sqlite
@jamesmeneghello
Copy link
Contributor Author

Forgot to add, seems to work fine when there's just a single relation between the tables - as soon as it goes to two, it breaks.

@B4nan B4nan added bug Something isn't working and removed bug Something isn't working labels Feb 25, 2022
@B4nan
Copy link
Member

B4nan commented Feb 25, 2022

This is actually false positive, the failing test is always getting ID 1, but with multi insert, the IDs have different order than you expect.

  test(`multiple 1-1 relations should be loaded from non-owner side`, async () => {
    const p = orm.em.create(Leg, {});
    orm.em.persist(p);

    const s = orm.em.create(Leg, {});
    orm.em.persist(s);

    const pos = orm.em.create(Position, { purchase: p, sale: s });
    await orm.em.persistAndFlush(pos);

    orm.em.clear();

    const posReload = await orm.em.findOne(Position, 1, { populate: true });
    expect(posReload?.purchase).toBeTruthy();

-    const leg = await orm.em.findOne(Leg, 1, { populate: ['purchasePosition'] });
+    const leg = await orm.em.findOne(Leg, p.id, { populate: ['purchasePosition'] });
    expect(leg?.purchasePosition).toBeTruthy();
  });

@B4nan B4nan closed this as completed Feb 25, 2022
B4nan added a commit that referenced this issue Feb 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants