Reputation: 2287
I'm using Nest.js, and considering migrating from TypeORM to Mikro-ORM. I'm using the nestjs-mikro-orm
module. But I'm stuck on something that seems very simple...
I've 3 entities, AuthorEntity
, BookEntity
and BookMetadata
. From my Author
module, I try to left join the Book
and BookMetadata
tables with the createQueryBuilder
method. But when running my query, I'm getting an error where Collection<BookEntity> of entity AuthorEntity[3390] not initialized
. However columns from the Author
table are well retrieved.
My 3 entities:
@Entity()
@Unique({ properties: ['key'] })
export class AuthorEntity {
@PrimaryKey()
id!: number;
@Property({ length: 255 })
key!: string;
@OneToMany('BookEntity', 'author', { orphanRemoval: true })
books? = new Collection<BookEntity>(this);
}
@Entity()
export class BookEntity {
@PrimaryKey()
id!: number;
@ManyToOne(() => AuthorEntity)
author!: AuthorEntity;
@OneToMany('BookMetadataEntity', 'book', { orphanRemoval: true })
bookMetadata? = new Collection<BookMetadataEntity>(this);
}
@Entity()
@Unique({ properties: ['book', 'localeKey'] })
export class BookMetadataEntity {
@PrimaryKey()
id!: number;
@Property({ length: 5 })
localeKey!: string;
@ManyToOne(() => BookEntity)
book!: BookEntity;
}
And the service file where I run my query:
@Injectable()
export class AuthorService {
constructor(
@InjectRepository(AuthorEntity)
private readonly authorRepository: EntityRepository<AuthorEntity>,
) {}
async findOneByKey(props: { key: string; localeKey: string; }): Promise<AuthorEntity> {
const { key, localeKey } = props;
return this.authorRepository
.createQueryBuilder('a')
.select(['a.*', 'b.*', 'c.*'])
.leftJoin('a.books', 'b')
.leftJoin('b.bookMetadata', 'c')
.where('a.key = ?', [key])
.andWhere('c.localeKey = ?', [localeKey])
.getSingleResult();
}
}
Am I missing something? Might be not related, but I also noticed that there is a special autoLoadEntities: true
for TypeORM users using Nest.js. Is there something similar for Mikro-ORM? Thanks ;)
Upvotes: 2
Views: 6321
Reputation: 18389
Mapping of multiple entities from single query is not yet supported, it is planned for v4. You can subscribe here: https://github.com/mikro-orm/mikro-orm/issues/440
In v3 you need to use 2 queries to load 2 entities, which for your use case is much easier without the QB involved.
return this.authorRepository.findOne({ key }, ['books']);
Or you could use qb.execute()
to get the raw results and map them yourself, but you would also have to manually alias all the fields to get around duplicities (Author.name
vs Book.name
), as doing qb.select(['a.*', 'b.*'])
will result in query select a.*, b.* ...
and the duplicate columns would not be correctly mapped.
https://mikro-orm.io/docs/query-builder/#mapping-raw-results-to-entities
About the autoLoadEntities
thing, never heard of that, will take a look how it works, but in general, the nestjs adapter is not developed by me, so if its something only nest related, it would be better to ask on their GH repo.
Or you could use folder based discovery (entitiesDirs
).
here is the new example with 3 entities:
return this.authorRepository.findOne({
key,
books: { bookMetadata: localeKey } },
}, ['books.bookMetadata']);
This will produce 3 queries, one for each db table, but the first one will auto-join books and bookMetadata to be able to filter by them. The condition will be propagated down in the second and third query.
If you omit the populate parameter (['books.bookMetadata']
), then only the first query will be fired and you will end up with books not being populated (but the Author will be queried with the joined condition).
Upvotes: 1