typeorm: leftJoinAndMapOne does not add target property to entity

Issue type:

[ x] question [ ] bug report [ ] feature request [ ] documentation issue

Database system/driver:

[ ] cordova [ ] mongodb [ ] mssql [x] mysql / mariadb [ ] oracle [ ] postgres [ ] cockroachdb [ ] sqlite [ ] sqljs [ ] react-native [ ] expo

TypeORM version:

[x] latest [ ] @next [ ] 0.x.x (or put your version here)

The above will return the User, but without the lastPurchase property

      const users = await this.repository.createQueryBuilder("user")
      .leftJoinAndMapOne("user.lastPurchase", (subQuery) => {
        return subQuery
          .select()
          .from(Purchase, "purchase")
          .orderBy("created", "DESC");
      }, "purchase", "purchase.userId = user.id")
      .getManyAndCount();

any ideas?

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 12
  • Comments: 19 (6 by maintainers)

Commits related to this issue

Most upvoted comments

@FFsim

BTW, to achieve join and map last rows like following code…

      const users = await this.repository.createQueryBuilder("user")
      .leftJoinAndMapOne("user.lastPurchase", (subQuery) => {
        return subQuery
          .select()
          .from(Purchase, "purchase")
          .orderBy("created", "DESC");
      }, "purchase", "purchase.userId = user.id")
      .getManyAndCount();

You can use it.

const users = await repository.createQueryBuilder("user")
    .leftJoinAndMapOne(
      "user.lastPurchase",
      Purchase,
      "purchase",
      "purchase.userId=user.id"
    )
    .where(
      (query) =>
        "purchase.id=" +
        query
          .subQuery()
          .select("id")
          .from(Purchase, "p")
          .where("user.id=p.userId")
          .orderBy("id", "DESC")
          .limit(1)
          .getQuery()
    )
    .orWhere("purchase.id is null")
    .getManyAndCount();

I’ve posted a potential solution for this: https://github.com/typeorm/typeorm/pull/9354

Found a workaround, let’s assume that you have join like this

.leftJoinAndMapOne('order.firstOrderId',
  qb => qb.select('created_by, min(id) as first_order_id')
    .from(Order, 'n2')
    .groupBy('created_by'),
  'fo', 'fo.created_by = order.created_by'
)

So you need to get raw results and mapped entities via .getRawAndEntities() and map custom properties manually:

const { entities, raw } = await query.getRawAndEntities();

return entities.map((entity, index) => {
  entity.firstOrderId = raw[index].first_order_id;
  return entity;
});

The solution is not clean, you still can’t combine it with count query, but this is the less painful way to achieve a result

Don’t forget to add your custom properties in @Entity()

Same here. Using the same query, results from .getRawMany() include the mapped field but those from .getMany() don’t

const contracts = await this.repository.createQueryBuilder('contract')
    .leftJoinAndMapOne(
        'contract.firstLesson',
        (qb) => qb.select(['l.contractId', 'MIN(l.startAt) as startAt']).from(Lesson, 'l').groupBy('l.contractId'),
        'lesson',
        'lesson.contractId = contract.id'
    )
   .getMany()

Luckily, Contract table has OneToMany relationship to Lesson table, so the following query

const contracts = await this.repository.createQueryBuilder("contract")
    .leftJoinAndSelect("contract.lessons", "lesson")
    .where(
        "lesson.startAt = (" +
            this.lessonRepository.createQueryBuilder("lesson")
            .select("MIN(startAt)")
            .where("lesson.contractId = contract.id")
            .getQuery() + ")"
    )
    .getMany()

gets me roughly what I want with the first lesson inside contract.lessons array

Still I would highly appreciate if someone can address this issue or tell me what I did wrong in the first query. Thanks!

@imnotjames due to project deadline I have fixed this other way using raw query.