如何从对象数组中按ownerId筛选出endDate最新的文档?
问题描述
我有一个包含startDate、endDate、ownerId和ownerType字段的MongoDB集合,目前用以下查询获取未来30天内到期的文档:
var yesterdayDate = new Date(); yesterdayDate.setDate(yesterdayDate.getDate() - 1); var next30daysDate = new Date(); next30daysDate.setDate(next30daysDate.getDate() + 29); const insurances = await Insurances.find({endDate: {$gte: yesterdayDate, $lte: next30daysDate}});
查询返回结果如下:
[ { docIds: [ "6285c7ce9fb2940007e61ce2" ], _id: "6285c7cd9fb2940007e61cde", startDate: "2022-03-31T18:30:00.000Z", endDate: "2022-09-28T18:30:00.000Z", ownerId: "61de54e8ae38620006457674", ownerType: "vendor", createdAt: "2022-05-19T04:30:06.040Z", updatedAt: "2022-09-06T09:42:37.497Z", __v: 0, name: "download" }, { docIds: [ "631edc3da1c7060007242fca" ], _id: "631edc3da1c7060007242fc2", startDate: "2022-09-13T18:30:00.000Z", endDate: "2022-09-27T18:30:00.000Z", ownerId: "6156aeeb152a3040b82fbc50", ownerType: "vendor", createdAt: "2022-09-12T07:14:05.616Z", updatedAt: "2022-09-12T12:04:57.897Z", __v: 0, name: "bid manager" }, { docIds: [ "631f203b71f6f042243bcb26" ], _id: "631f203871f6f042243bcb1f", startDate: "2022-09-13T06:30:00.000Z", endDate: "2022-09-29T06:30:00.000Z", name: "Javascript Training", ownerId: "6156aeeb152a3040b82fbc50", ownerType: "vendor", createdAt: "2022-09-12T12:04:10.938Z", updatedAt: "2022-09-12T12:04:11.557Z", __v: 0 }, { docIds: [ "631f203b71f6f042243bcb28" ], _id: "631f203871f6f042243bcb20", startDate: "2022-09-12T18:30:00.000Z", endDate: "2022-09-27T18:30:00.000Z", name: "SP-UserAccounts-200622-1021", ownerId: "6156aeeb152a3040b82fbc50", ownerType: "vendor", createdAt: "2022-09-12T12:04:10.943Z", updatedAt: "2022-09-12T12:04:47.312Z", __v: 0 }, { docIds: [ "632155e5488a188c34ba5473" ], _id: "632155e1488a188c34ba546f", startDate: "2022-09-13T06:30:00.000Z", endDate: "2022-09-24T06:30:00.000Z", name: "bid-project-details", ownerId: "6253c6a067a5b83c84855f99", ownerType: "vendor", createdAt: "2022-09-14T04:17:41.082Z", updatedAt: "2022-09-14T04:17:41.674Z", __v: 0 }, { docIds: [ "6321fe4317dfea0007e828fe" ], _id: "6321fe2217dfea0007e828c9", startDate: "2022-09-14T00:00:00.000Z", endDate: "2022-09-15T00:00:00.000Z", ownerId: "5bca41cbe5a5c1808da49b1c", ownerType: "vendor", createdAt: "2022-09-14T16:15:30.986Z", updatedAt: "2022-09-14T16:16:03.604Z", __v: 0 }, { docIds: [ "6321ff3e17dfea0007e829dc" ], _id: "6321fe2217dfea0007e828c6", startDate: "2022-09-15T00:00:00.000Z", endDate: "2022-09-15T00:00:00.000Z", ownerId: "5bca41cbe5a5c1808da49b1c", ownerType: "vendor", createdAt: "2022-09-14T16:15:31.215Z", updatedAt: "2022-09-14T16:20:14.721Z", __v: 0 }, { docIds: [ "6321fef84377f600061f7f43" ], _id: "6321fe2217dfea0007e828c8", startDate: "2022-09-14T00:00:00.000Z", endDate: "2022-09-24T00:00:00.000Z", ownerId: "5bca41cbe5a5c1808da49b1c", ownerType: "vendor", createdAt: "2022-09-14T16:15:31.233Z", updatedAt: "2022-09-14T16:19:04.341Z", __v: 0 }, { docIds: [ "6321ff5c17dfea0007e829f7" ], _id: "6321fe2217dfea0007e828c7", startDate: "2022-09-14T00:00:00.000Z", endDate: "2022-09-15T00:00:00.000Z", ownerId: "5bca41cbe5a5c1808da49b1c", ownerType: "vendor", createdAt: "2022-09-14T16:15:31.298Z", updatedAt: "2022-09-14T16:20:44.631Z", __v: 0 } ]
现在需要从结果中按ownerId分组,每个ownerId只保留endDate最新的那一条文档,该怎么实现?
解决方案
方式一:MongoDB聚合查询(推荐)
直接在数据库层面完成筛选和分组,效率更高,避免拉取大量数据到前端处理。使用aggregate管道实现:
var yesterdayDate = new Date(); yesterdayDate.setDate(yesterdayDate.getDate() - 1); var next30daysDate = new Date(); next30daysDate.setDate(next30daysDate.getDate() + 29); const latestInsurances = await Insurances.aggregate([ // 过滤日期范围内的文档 { $match: { endDate: { $gte: yesterdayDate, $lte: next30daysDate } } }, // 按endDate降序排序,确保最新的文档排在每组最前面 { $sort: { endDate: -1 } }, // 按ownerId分组,保留每组第一个文档(即endDate最新的) { $group: { _id: "$ownerId", latestDoc: { $first: "$$ROOT" } } }, // 将最新文档作为根文档输出,结构与原文档一致 { $replaceRoot: { newRoot: "$latestDoc" } } ]);
方式二:前端JavaScript处理
如果已经拿到原查询的insurances数组,可以在前端通过JS处理:
// 先按endDate降序排序,确保每个ownerId的最新文档排在前面 const sortedInsurances = [...insurances].sort((a, b) => b.endDate - a.endDate); // 用对象去重,只保留每个ownerId的第一个文档 const result = Object.values( sortedInsurances.reduce((acc, doc) => { if (!acc[doc.ownerId]) { acc[doc.ownerId] = doc; } return acc; }, {}) );
内容的提问来源于stack exchange,提问作者Somasekhar
相关产品推荐
相关产品推荐

