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

如何按年、月、日嵌套分组MongoDB集合数据?含Angular方案

按年-月-日嵌套分组实现时间线视图方案

方案一:MongoDB聚合管道实现

通过多层$group聚合操作,从最细的日期粒度逐步向上嵌套,同时将数字月份转换为英文缩写,最终得到符合要求的层级结构。

完整聚合代码:

db.collection.aggregate([
  // 1. 按年、月、日分组,收集当日所有文档
  {
    $group: {
      _id: {
        year: "$dShow.year",
        month: "$dShow.month",
        day: "$dShow.day"
      },
      dayList: { 
        $push: { 
          dShow: "$dShow", 
          title: "$title",
          // 按需添加其他需要的字段:_id, forever, text等
        } 
      }
    }
  },
  // 2. 按年、月分组,嵌套日期分组数据
  {
    $group: {
      _id: {
        year: "$_id.year",
        month: "$_id.month"
      },
      days: {
        $push: {
          day: "$_id.day",
          list: "$dayList"
        }
      }
    }
  },
  // 3. 按年分组,嵌套月份分组数据,同时转换数字月份为英文缩写
  {
    $group: {
      _id: "$_id.year",
      months: {
        $push: {
          month: {
            $dateToString: {
              format: "%b",
              date: {
                $dateFromParts: {
                  year: "$_id.year",
                  month: "$_id.month",
                  day: 1
                }
              }
            }
          },
          list: "$days"
        }
      }
    }
  },
  // 4. 调整输出结构,匹配期望格式
  {
    $project: {
      _id: 0,
      year: "$_id",
      list: "$months"
    }
  },
  // 可选:按年份升序排序
  { $sort: { year: 1 } }
])

步骤说明

  • 第一层$group:将同一天的文档归为一组,收集所有需要展示的字段。
  • 第二层$group:将同一月份下的日期组嵌套整合。
  • 第三层$group:将同一年份下的月份组嵌套整合,通过$dateFromParts将年月转为日期对象,再用$dateToString生成三位英文月份缩写。
  • 最后通过$project调整输出字段名,去掉多余的_id。

方案二:Angular自定义管道实现

如果不需要后端处理,可在前端通过自定义管道对原始数据进行分组,适合数据量较小或需要灵活调整分组逻辑的场景。

1. 创建自定义管道

import { Pipe, PipeTransform } from '@angular/core';

// 定义数据结构,按需扩展字段
interface TimelineItem {
  dShow: { year: number; month: number; day: number };
  title: string;
}

interface DayGroup {
  day: number;
  list: TimelineItem[];
}

interface MonthGroup {
  month: string;
  list: DayGroup[];
}

interface YearGroup {
  year: number;
  list: MonthGroup[];
}

@Pipe({ name: 'nestedDateGroup' })
export class NestedDateGroupPipe implements PipeTransform {
  // 月份英文缩写映射
  private monthAbbrs = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];

  transform(items: TimelineItem[]): YearGroup[] {
    if (!items?.length) return [];

    // 按年份分组
    const yearMap = items.reduce((map, item) => {
      const year = item.dShow.year;
      if (!map[year]) map[year] = [];
      map[year].push(item);
      return map;
    }, {} as Record<number, TimelineItem[]>);

    // 转换为年份分组数组并排序
    return Object.entries(yearMap)
      .map(([yearStr, yearItems]) => {
        const year = parseInt(yearStr);
        // 按月份分组
        const monthMap = yearItems.reduce((map, item) => {
          const month = item.dShow.month;
          if (!map[month]) map[month] = [];
          map[month].push(item);
          return map;
        }, {} as Record<number, TimelineItem[]>);

        // 转换为月份分组数组并排序
        const monthList = Object.entries(monthMap)
          .map(([monthStr, monthItems]) => {
            const monthIndex = parseInt(monthStr) - 1;
            // 按日期分组
            const dayMap = monthItems.reduce((map, item) => {
              const day = item.dShow.day;
              if (!map[day]) map[day] = [];
              map[day].push(item);
              return map;
            }, {} as Record<number, TimelineItem[]>);

            // 转换为日期分组数组并排序
            const dayList = Object.entries(dayMap)
              .map(([dayStr, dayItems]) => ({
                day: parseInt(dayStr),
                list: dayItems
              }))
              .sort((a, b) => a.day - b.day);

            return {
              month: this.monthAbbrs[monthIndex],
              list: dayList
            };
          })
          .sort((a, b) => this.monthAbbrs.indexOf(a.month) - this.monthAbbrs.indexOf(b.month));

        return { year, list: monthList };
      })
      .sort((a, b) => a.year - b.year);
  }
}

2. 使用管道

首先在你的Angular模块中声明该管道:

import { NgModule } from '@angular/core';
import { NestedDateGroupPipe } from './nested-date-group.pipe';

@NgModule({
  declarations: [NestedDateGroupPipe],
  exports: [NestedDateGroupPipe]
})
export class PipesModule {}

然后在模板中直接使用:

<div *ngFor="let yearGroup of timelineData | nestedDateGroup">
  <h3>{{ yearGroup.year }}</h3>
  <div *ngFor="let monthGroup of yearGroup.list">
    <h4>{{ monthGroup.month }}</h4>
    <div *ngFor="let dayGroup of monthGroup.list">
      <h5>Day {{ dayGroup.day }}</h5>
      <div *ngFor="let item of dayGroup.list">
        <p>{{ item.title }}</p>
      </div>
    </div>
  </div>
</div>

方案对比

  • MongoDB聚合:适合数据量大的场景,减少前端数据传输量和处理压力,性能更优。
  • Angular管道:无需修改后端逻辑,前端灵活调整分组规则,适合数据量较小或需要动态调整视图的场景。

内容的提问来源于stack exchange,提问作者DValdir Martins

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 16:45:32