如何编写MongoDB聚合管道按公共值分组嵌套对象数组并保留父字段
实现方案
你可以在现有聚合管道的基础上增加$group和$project阶段完成分组重组,完整管道如下:
[ { '$match': { '_id': new ObjectId('60dd583e38a494001e7cbe01') } }, { '$unwind': { 'path': '$bookmarks', 'preserveNullAndEmptyArrays': false } }, // 第一阶段分组:按用户+国家聚合同国书签 { '$group': { '_id': { 'userId': '$_id', 'country': '$bookmarks.country' }, // 保留所有用户原有字段 'firstLogIn': { '$first': '$firstLogIn' }, 'lastLoggedIn': { '$first': '$lastLoggedIn' }, 'name': { '$first': '$name' }, 'provider': { '$first': '$provider' }, 'googleID': { '$first': '$googleID' }, 'email': { '$first': '$email' }, 'picture': { '$first': '$picture' }, '__v': { '$first': '$__v' }, // 同国书签汇总到数组 'countryBookmarks': { '$push': '$bookmarks' } } }, // 第二阶段分组:按用户聚合所有国家的书签对 { '$group': { '_id': '$_id.userId', 'firstLogIn': { '$first': '$firstLogIn' }, 'lastLoggedIn': { '$first': '$lastLoggedIn' }, 'name': { '$first': '$name' }, 'provider': { '$first': '$provider' }, 'googleID': { '$first': '$googleID' }, 'email': { '$first': '$email' }, 'picture': { '$first': '$picture' }, '__v': { '$first': '$__v' }, 'bookmarkPairs': { '$push': { 'k': '$_id.country', 'v': '$countryBookmarks' } } } }, // 重组输出结构 { '$project': { 'firstLogIn': 1, 'lastLoggedIn': 1, 'name': 1, 'provider': 1, 'googleID': 1, 'email': 1, 'picture': 1, '__v': 1, // 将国家-书签字典对转为对象 'bookmarks': { '$arrayToObject': '$bookmarkPairs' } } } ]
格式说明
你之前期望的bookmarks为数组且直接包含"US":[]的写法不符合JSON规范,上述管道输出的合法结构如下,bookmarks为对象,键为国家代码,值为对应国家的书签数组:
{ "_id": ObjectId("60dd583e38a494001e7cbe01"), "firstLogIn": ISODate("2021-07-01T05:51:30.253Z"), "lastLoggedIn": ISODate("2021-07-01T05:51:30.253Z"), "name": "First Last", "provider": "google", "googleID": "some google ID", "email": "email@gmail.com", "picture": "link to picture", "__v": 0, "bookmarks": { "US": [/* 所有美国地区的书签 */], "MX": [/* 所有墨西哥地区的书签 */], "HN": [/* 所有洪都拉斯地区的书签 */] // 其余国家以此类推 } }
如果确实需要bookmarks为数组格式,可将最后一步$project阶段的bookmarks字段替换为如下配置:
'bookmarks': { '$map': { 'input': '$bookmarkPairs', 'in': { '$arrayToObject': [['$$this.k', '$$this.v']] } } }
输出结构为:
"bookmarks": [ {"US": [/* 美国地区书签 */]}, {"MX": [/* 墨西哥地区书签 */]} // 其余国家以此类推 ]
内容的提问来源于stack exchange,提问作者Enn Vee
相关产品推荐
相关产品推荐

