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

NestJS中使用Mongoose聚合按数组字段分组处理MongoDB数据

实现方案

方法一:MongoDB聚合查询(推荐)

直接通过MongoDB聚合管道在数据库层面完成数据转换,性能更优,适合大数据量场景。在NestJS的Service中可以这样实现:

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { YourDocument, YourModelName } from './schemas/your.schema'; // 替换为你的模型类型

@Injectable()
export class YourService {
  constructor(
    @InjectModel(YourModelName) private readonly yourModel: Model<YourDocument>,
  ) {}

  async findByIdWithGroupedRooms(id: string) {
    const result = await this.yourModel.aggregate([
      // 匹配目标文档
      { $match: { _id: this.yourModel.Types.ObjectId(id) } },
      // 展开rooms数组,将每个元素转为独立文档
      { $unwind: '$rooms' },
      // 按根文档字段+rooms.name分组,收集对应的workId
      {
        $group: {
          _id: {
            _id: '$_id',
            organization: '$organization',
            name: '$rooms.name',
          },
          workIds: { $push: '$rooms.workId' },
        },
      },
      // 调整字段结构,拆分嵌套字段
      {
        $project: {
          _id: '$_id._id',
          organization: '$_id.organization',
          name: '$_id.name',
          workIds: 1,
        },
      },
      // 重新按根文档字段分组,组装新的rooms数组
      {
        $group: {
          _id: { _id: '$_id', organization: '$organization' },
          rooms: { $push: { name: '$name', workIds: '$workIds' } },
        },
      },
      // 替换根文档,生成最终输出格式
      {
        $replaceRoot: {
          newRoot: {
            _id: '$_id._id',
            organization: '$_id.organization',
            rooms: '$rooms',
          },
        },
      },
    ]);

    return result[0] || null; // 聚合返回数组,取第一个匹配结果
  }
}

方法二:代码层面处理查询结果

如果不需要数据库层面的聚合,也可以先查询原始数据,再在代码中手动处理数组:

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { YourDocument, YourModelName } from './schemas/your.schema';

@Injectable()
export class YourService {
  constructor(
    @InjectModel(YourModelName) private readonly yourModel: Model<YourDocument>,
  ) {}

  async findByIdWithGroupedRooms(id: string) {
    // 查询原始文档
    const doc = await this.yourModel.findById(id).exec();
    if (!doc) return null;

    // 用Map按name分组收集workId
    const roomGroup = new Map<string, string[]>();
    doc.rooms.forEach(item => {
      if (!roomGroup.has(item.name)) {
        roomGroup.set(item.name, []);
      }
      roomGroup.get(item.name)?.push(item.workId);
    });

    // 转换为目标格式的rooms数组
    const formattedRooms = Array.from(roomGroup.entries()).map(([name, workIds]) => ({
      name,
      workIds,
    }));

    // 返回最终结构
    return {
      _id: doc._id,
      organization: doc.organization,
      rooms: formattedRooms,
    };
  }
}

内容的提问来源于stack exchange,提问作者Takiuddin Ahmed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 22:24:28