如何将正则匹配的unset逻辑移入MongoDB更新管道?
问题描述
给定简化后的Timesheets数组(实际包含70+字段):
const timesheets = [ { "_id": "123", "WORKDATE": "2023-06-06", "ACTIVITY": 0 }, { "_id": "456", "WORKDATE": "0000-00-00", "ACTIVITY": 5 } ]
目前通过JavaScript批量更新实现两个目标:
- 移除值为空字符串、空数组或
0的字段(已通过$replaceRoot步骤实现) - 移除值为
'0000-00-00'且字段名匹配正则/(DAT|LAST|AT|FIN|START)/的字段(当前通过JS的unset函数筛选字段名,再传入管道的$unset操作)
当前的批量更新代码如下:
const handleWindow = (async (timesheets) => { if(!Array.isArray(timesheets) || timesheets.length === 0) return; const unset = (obj) => ( // 返回需要在管道中unset的字段名数组 Object.entries(obj).filter(([key, value]) => ( value === '0000-00-00' && /(DAT|LAST|AT|FIN|START)/.test(key) )).map((a) => a.shift()) ); // 批量更新/插入工时表,移除空值 const bulkOperations = timesheets.map(timesheet => ({ updateOne: { filter: { _id: timesheet._id }, update: [ { $set: timesheet }, { $replaceRoot: { newRoot: { $arrayToObject: { $filter: { input: { $objectToArray: '$$ROOT' }, cond: { $not: { $in: ['$$this.v', ['', [], 0] ]}} }}}}}, { $unset: unset(timesheet) } ], upsert: true } } )); return await context.services.get('mongodb-atlas').db('TimesheetsDB').collection('Timesheets').bulkWrite(bulkOperations); })(timesheets)
想咨询两个问题:
- 如何将上述
unset的筛选逻辑直接移入MongoDB的更新管道中? - MongoDB管道中是否存在类似JavaScript里
RegExp.test()的正则匹配方法?
解决方案
1. MongoDB管道中的正则匹配方法
MongoDB提供了$regexMatch操作符,功能和JavaScript的RegExp.test()完全一致,用于判断字符串是否匹配指定正则表达式,返回布尔值。
2. 将unset逻辑整合到更新管道中
可以把两个字段移除逻辑合并到同一个$replaceRoot步骤里,不需要单独的$unset操作。核心思路是在$filter的条件中同时排除两种字段:
- 值为空字符串、空数组或
0的字段 - 值为
'0000-00-00'且字段名匹配指定正则的字段
修改后的更新管道如下:
const bulkOperations = timesheets.map(timesheet => ({ updateOne: { filter: { _id: timesheet._id }, update: [ { $set: timesheet }, { $replaceRoot: { newRoot: { $arrayToObject: { $filter: { input: { $objectToArray: '$$ROOT' }, cond: { $and: [ // 排除值为空、空数组或0的字段 { $not: { $in: ['$$this.v', ['', [], 0] ]}}, // 排除值为'0000-00-00'且字段名匹配正则的字段 { $not: { $and: [ { $eq: ['$$this.v', '0000-00-00'] }, { $regexMatch: { input: '$$this.k', regex: /(DAT|LAST|AT|FIN|START)/ } } ] }} ] } } } } } } ], upsert: true } }));
逻辑说明
$objectToArray:把当前文档转换成键值对数组(每个元素是{k: 字段名, v: 字段值})$filter:遍历这个数组,只保留满足条件的元素:- 字段值不在
['', [], 0]集合中 - 不同时满足「字段值是
'0000-00-00'」和「字段名匹配指定正则」
- 字段值不在
$arrayToObject:把过滤后的键值对数组重新转换成文档$replaceRoot:用这个新文档替换原文档,完成字段移除
验证示例数据效果
对于示例中的两个文档:
_id: "123":ACTIVITY值为0会被移除,WORKDATE值正常保留,最终文档是{ "_id": "123", "WORKDATE": "2023-06-06" }_id: "456":WORKDATE值为'0000-00-00'且字段名包含DAT,会被移除,ACTIVITY值保留,最终文档是{ "_id": "456", "ACTIVITY": 5 }
内容的提问来源于stack exchange,提问作者Mikael G
相关产品推荐
相关产品推荐

