You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeORM左连接一对多关联结果如何限制排序并映射为单个字段

TypeORM 一对多关联分组限制条数查询实现方案

你可以直接通过TypeORM QueryBuilder实现需求,也可以选择手动处理数据,两种方案各有适用场景,具体实现如下:

前提优化提示

你当前的Activity模型中@OneToMany装饰器下的@JoinColumn是冗余配置,@JoinColumn仅需要在@ManyToOne侧声明即可,可直接删除避免不必要的异常。


方案1:单QueryBuilder实现(推荐,性能更优)

借助数据库窗口函数ROW_NUMBER实现每个活动下最新3名参与人的过滤,直接通过TypeORM映射返回你需要的结构,无需手动拼接:

const result = await activityRep
  .createQueryBuilder('a')
  // 子查询给每个活动的参与人按加入时间倒序排名
  .leftJoin(
    (qb) =>
      qb
        .select('*')
        .addSelect(
          'ROW_NUMBER() OVER (PARTITION BY activity_id ORDER BY createdAt DESC)',
          'rn'
        )
        .from(ActivitiyAttendant, 'aj'),
    'aj_filter',
    'aj_filter.activity_id = a.activity_id AND aj_filter.rn <= 3'
  )
  // 把过滤后的参与人映射到自定义字段recent_attendants
  .leftJoinAndMapMany(
    'a.recent_attendants',
    ActivitiyAttendant,
    'aj',
    'aj.activity_attendant_id = aj_filter.activity_attendant_id'
  )
  // 指定返回字段
  .select([
    'a.activity_id',
    'a.title',
    'aj.activity_attendant_id',
    'aj.profile_picture',
  ])
  .getMany();

该方案仅执行一次SQL查询,性能最优,支持MySQL 8.0+、PostgreSQL、SQL Server等支持窗口函数的数据库,返回的result结构完全符合你给出的期望格式。


方案2:手动拼接实现(兼容性好)

如果你的数据库不支持窗口函数(如MySQL 5.7及更低版本),可以通过两次查询+手动组装的方式实现,写法更简单易维护:

// 1. 查询所有活动基础信息
const activities = await activityRep.find({
  select: ['activity_id', 'title'],
});
if (!activities.length) return [];

// 2. 查询所有关联的参与人,按加入时间倒序
const activityIds = activities.map((item) => item.activity_id);
const allAttendants = await attendantRep
  .createQueryBuilder('aj')
  .select(['aj.activity_id', 'aj.activity_attendant_id', 'aj.profile_picture'])
  .where('aj.activity_id IN (:...activityIds)', { activityIds })
  .orderBy('aj.createdAt', 'DESC')
  .getMany();

// 3. 按活动分组,每个活动仅保留前3名参与人
const attendantGroup = new Map();
allAttendants.forEach((item) => {
  if (!attendantGroup.has(item.activity_id)) {
    attendantGroup.set(item.activity_id, []);
  }
  const list = attendantGroup.get(item.activity_id);
  if (list.length < 3) {
    list.push({
      activity_attendant_id: item.activity_attendant_id,
      profile_picture: item.profile_picture,
    });
  }
});

// 4. 组装最终结果
const result = activities.map((item) => ({
  ...item,
  recent_attendants: attendantGroup.get(item.activity_id) || [],
}));

内容的提问来源于stack exchange,提问作者Azad Mamun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 02:15:05