基于日期字段使用聚合将MongoDB文档拆分为多份文档
MongoDB按年份拆分含数组的文档并存入不同集合解决方案
问题分析
你用$match或$find没得到结果是因为这两个操作只能过滤现有文档,没法拆分文档内的数组、按字段重新分组生成新文档。要实现需求,需要结合MongoDB聚合管道处理数组和分组,再将结果写入对应年份的集合。
假设样例数据
先明确数据结构(以常见日期字段为例):
原文档:
{ "_id": ObjectId("60d21b4667d0d8992e610c85"), "flight_records": [ {"flight_no": "CA123", "flight_date": ISODate("2020-03-15T10:00:00Z"), "passengers": 150}, {"flight_no": "MU456", "flight_date": ISODate("2020-07-22T14:30:00Z"), "passengers": 180}, {"flight_no": "CZ789", "flight_date": ISODate("2021-01-05T08:15:00Z"), "passengers": 120}, {"flight_no": "CA321", "flight_date": ISODate("2022-11-30T16:45:00Z"), "passengers": 160}, {"flight_no": "MU654", "flight_date": ISODate("2023-05-10T09:20:00Z"), "passengers": 190} ] }
期望输出(每个年份一个独立文档):
// 2020年文档 { "year": 2020, "flight_records": [ {"flight_no": "CA123", "flight_date": ISODate("2020-03-15T10:00:00Z"), "passengers": 150}, {"flight_no": "MU456", "flight_date": ISODate("2020-07-22T14:30:00Z"), "passengers": 180} ] }
解决方案步骤
1. 用聚合管道拆分并分组数据
执行以下聚合命令,将原文档拆分为按年份聚合的独立文档:
db.your_source_collection.aggregate([ // 把flight_records数组拆分为单个记录的文档 { $unwind: "$flight_records" }, // 从航班日期中提取年份 { $addFields: { "flight_year": { $year: "$flight_records.flight_date" } } }, // 按年份分组,重新收集同一年的航班记录 { $group: { _id: "$flight_year", flight_records: { $push: "$flight_records" } } }, // 可选:把_id重命名为year,让文档结构更清晰 { $project: { _id: 0, year: "$_id", flight_records: 1 } } ])
2. 将分组结果存入对应年份的集合
因为MongoDB的$out操作只能输出到单个集合,所以需要用客户端代码遍历聚合结果,分别写入对应年份的集合(比如flight_2020、flight_2021)。
用Mongo Shell脚本实现:
// 执行聚合查询获取结果游标 const yearGroups = db.your_source_collection.aggregate([ { $unwind: "$flight_records" }, { $addFields: { "flight_year": { $year: "$flight_records.flight_date" } } }, { $group: { _id: "$flight_year", flight_records: { $push: "$flight_records" } } }, { $project: { _id: 0, year: "$_id", flight_records: 1 } } ]); // 遍历游标,写入对应集合 yearGroups.forEach(groupDoc => { const targetCollection = `flight_${groupDoc.year}`; db.getCollection(targetCollection).insertOne(groupDoc); });
用Node.js代码实现(适合批量处理):
const { MongoClient } = require('mongodb'); async function splitFlightData() { const client = await MongoClient.connect('mongodb://localhost:27017/your_database_name'); const db = client.db(); // 执行聚合 const cursor = db.collection('your_source_collection').aggregate([ { $unwind: "$flight_records" }, { $addFields: { "flight_year": { $year: "$flight_records.flight_date" } } }, { $group: { _id: "$flight_year", flight_records: { $push: "$flight_records" } } }, { $project: { _id: 0, year: "$_id", flight_records: 1 } } ]); // 逐个写入目标集合 await cursor.forEach(async doc => { const collName = `flight_${doc.year}`; await db.collection(collName).insertOne(doc); }); await client.close(); } splitFlightData().catch(console.error);
关键说明
$unwind:将数组拆分为单个元素的文档,为后续按年份分组做准备。- 若你的日期是字符串格式,需要先用
$toDate转换为Date类型再提取年份。 - 写入集合时,若目标集合不存在,MongoDB会自动创建。
内容的提问来源于stack exchange,提问作者SATA_CHAKRA
相关产品推荐
相关产品推荐

