如何在Mongoose与Node.js中从数组生成对象用于MongoDB聚合
处理动态字段数组并生成MongoDB聚合$group中的Sum字段(过滤指定字段)
首先,咱们先理清你遇到的核心问题:你需要从动态字段数组中过滤掉不区分大小写的timeStamp和deviceId,然后把剩下的字段转换成MongoDB聚合$group阶段需要的字段名: { $sum: "$字段名" }格式,最终整合到聚合查询里。你的现有代码有几个逻辑和写法上的问题,我帮你一步步修正:
现有代码的问题分析
- 过滤条件逻辑错误:你用了
!data.name.match(/time/g) || !data.name.match(/deviceId/g),这个逻辑会导致只要不匹配其中一个就保留,刚好和你要排除这两个字段的需求相反,而且正则没有加不区分大小写的标志,匹配不够准确。 - 不必要的
async forEach:这里没有异步操作,async会导致遍历过程无法同步执行(forEach本身也不支持异步等待),可能导致vList没有正确填充。 - 错误的字符串拼接与解析:你把聚合字段拼成字符串再
JSON.parse,最后得到的是字符串数组,没法直接插入$group对象中,完全没必要绕这个弯。
修正后的完整解决方案
第一步:过滤不需要的字段
用filter和map组合,结合不区分大小写的匹配,简洁高效地筛选出需要的字段:
const jsonObj = [ { name: "heat", value: "HEAT" }, { name: "humidity", value: "HUMIDITY" }, { name: "deviceId", value: "DEVICEID" }, { name: "timeStamp", value: "TIMESTAMP" } ]; // 定义要排除的字段(转成小写,方便不区分大小写匹配) const excludedFields = new Set(['timestamp', 'deviceid']); // 过滤出需要参与聚合的字段名 const targetFields = jsonObj .map(item => item.name) .filter(fieldName => !excludedFields.has(fieldName.toLowerCase()));
第二步:生成$group需要的聚合配置
直接构建一个对象,每个键是字段名,值是对应的$sum聚合规则,不需要任何字符串操作:
const groupSumFields = {}; targetFields.forEach(field => { groupSumFields[field] = { $sum: `$${field}` }; });
第三步:整合到MongoDB聚合查询中
用对象扩展运算符...把生成的聚合字段直接合并到$group阶段:
db.collection(templateId).aggregate([ { $match: { entryDayTime: { $lt: new Date(lastDay), $gte: new Date(firstDay) } } }, { $group: { _id: "$deviceId", Count: { $sum: 1 }, // 直接扩展生成的sum字段 ...groupSumFields } }, { $sort: { entryDayTime: -1 } } ]);
效果验证
最终生成的groupSumFields会是这样的对象:
{ heat: { $sum: "$heat" }, humidity: { $sum: "$humidity" } }
完美匹配你需要的$group阶段格式,而且自动排除了timeStamp和deviceId字段,不需要再处理字符串的单引号问题。
内容的提问来源于stack exchange,提问作者Aravind
相关产品推荐
相关产品推荐

