MongoDB:基于数组内timestamp生成Date类型dateTime字段
解决MongoDB数组元素添加dateTime字段的更新报错问题
原始文档结构
集合中的文档结构如下:
{ "_id":"123456", "history":[ { "date": 1674926893449, "name": "Hello" }, { "date": 1631548766655, "name": "Super" } ] }
需求说明
需要为history数组的每个元素添加dateTime字段:
- 若
date值大于0,将其转换为MongoDB的Date类型 - 若
date值为0,dateTime设为null
期望最终文档结构:
{ "_id":"123456", "history":[ { "date": 1674926893449, "name": "Hello", "dateTime": ISODate("2023-01-28T17:28:13.449Z") }, { "date": 1631548766655, "name": "Super", "dateTime": ISODate("2021-09-13T15:59:26.655Z") } ] }
错误尝试与报错信息
尝试了以下更新语句:
db.invoice.updateMany( {"history.dateTime":{$exists:false}}, {$set : { "history.$[elem].dateTime" : { $cond: { if: { $gt: [ "history.$[elem].$date", 0 ] }, then: { $toDate: "history.$[elem].$date" }, else: null } } }}, { "arrayFilters": [{ "elem.dateTime": {$exists:false} }] } )
执行后报错:
The dollar ($) prefixed field '$cond' in 'history.0.dateTime.$cond' is not valid for storage.
问题原因与解决方案
问题原因
普通的$set更新操作中,不能直接使用聚合操作符(如$cond、$toDate)来计算字段值,MongoDB会将这些聚合操作符当作普通的文档字段处理,导致报错。
正确的更新语句
需要使用聚合管道式更新(MongoDB 4.2及以上版本支持),通过聚合表达式处理数组元素:
db.invoice.updateMany( // 匹配条件:存在history数组,且数组中至少有一个元素没有dateTime字段 { "history": { "$exists": true }, "history.dateTime": { "$exists": false } }, [ { "$set": { "history": { "$map": { "input": "$history", "as": "item", "in": { // 保留原字段 "date": "$$item.date", "name": "$$item.name", // 根据date值生成dateTime "dateTime": { "$cond": { "if": { "$gt": ["$$item.date", 0] }, "then": { "$toDate": "$$item.date" }, "else": null } } } } } } } ] )
代码说明
- 匹配条件:筛选出需要更新的文档,确保
history数组存在且包含无dateTime字段的元素 - 聚合管道:使用
$set结合$map遍历history数组的每个元素 - 字段处理:
- 保留原有的
date和name字段 - 通过
$cond判断date值,大于0时用$toDate转换为Date类型,否则设为null
- 保留原有的
内容的提问来源于stack exchange,提问作者gstievenard
相关产品推荐
相关产品推荐

