如何在MongoDB的$group中带条件使用$addToSet?
解决MongoDB分组时条件生成数组元素的问题
原始文档
_id: ObjectId('641316fd596514e63a2121ca'), orgId: 3, devices { _id: ObjectId('64131139596514e63a2121c1') name: 'Device A' }
```javascript _id: ObjectId('641316fd596514e63a2121ca'), orgId: 3 ```
```javascript _id: ObjectId('64131b2b596514e63a2121ce'), orgId: 4, devices { _id: ObjectId('64131c1e596514e63a2121d0') name: 'Device B' } ```
```javascript _id: ObjectId('64131d38596514e63a2121d1'), orgId: 5 ```
```javascript _id: ObjectId('64131d38596514e63a2121d1'), orgId: 5 ```
需求
按_id和orgId对文档分组,生成名为endpoints的数组:
- 仅当文档存在
devices字段时,向数组添加元素:{ type: 'linux', refId: '$devices._id' } - 无
devices字段的文档分组后,endpoints为空数组
预期结果:
_id: { id: ObjectId('641316fd596514e63a2121ca'), orgId: 3 }, endpoints: [ { type: 'linux', refId: ObjectId('64131139596514e63a2121c1') } ]
```javascript _id: { id: ObjectId('64131b2b596514e63a2121ce'), orgId: 4 }, endpoints: [ { type: 'linux', refId: ObjectId('64131c1e596514e63a2121d0') } ] ```
```javascript _id: { id: ObjectId('64131d38596514e63a2121d1'), orgId: 5 }, endpoints: [] ```
尝试的代码
{ $group: { _id: { id: '$_id', orgId: '$orgId', }, endpoints: { $addToSet: { $cond: { if: { $ne: [ '$devices', undefined ] }, then: { type: 'linux', refId: '$devices._id' }, else: '$$REMOVE' } } } } }
问题
执行后,无devices字段的分组中,endpoints数组出现了仅含type: 'linux'的对象,而非预期的空数组:
_id: { id: ObjectId('64131d38596514e63a2121d1'), orgId: 5 }, endpoints: [ { type: 'linux' } ]
解决方案
修正后的聚合管道
[ { $group: { _id: { id: '$_id', orgId: '$orgId' }, endpoints: { $push: { $cond: { // 准确判断devices字段是否存在 if: { $exists: ['$devices', true] }, then: { type: 'linux', refId: '$devices._id' }, else: '$$REMOVE' } } } } }, { $addFields: { // 过滤数组中的空元素,确保无符合条件的元素时数组为空 endpoints: { $filter: { input: '$endpoints', cond: { $ne: ['$$this', null] } } } } } ]
关键修正点
- 字段存在性判断:用
$exists: ['$devices', true]替代$ne: ['$devices', undefined],MongoDB中字段缺失时不会被识别为undefined,$exists能准确判断字段是否存在。 - 数组元素过滤:添加
$addFields阶段配合$filter,移除endpoints数组中因$$REMOVE产生的空元素,确保无符合条件的文档时数组为空。 - 数组操作选择:用
$push替代$addToSet(若需去重可保留$addToSet),更直接地收集符合条件的元素。
内容的提问来源于stack exchange,提问作者TheStranger
相关产品推荐
相关产品推荐

