MongoDB 3.6环境下如何为日期字段值增加1秒?
错误写法的根源
你遇到的所有问题,本质是混淆了两类update语法的规则:
- 普通非管道式update的参数是更新操作符对象,这里面写在
$set后面的所有值,都是你传入的常量——MongoDB不会把值里的$updatedAt解析为「引用当前文档的同名字段」,只会把它当普通字符串处理。你写"$updatedAt" + 1的时候,这是mongoshell的JS层面做的字符串拼接,结果就是普通字符串$updatedAt1,自然会被直接赋值。 - 如果你不带$、不带引号直接写
updatedAt: updatedAt + 1,那这是mongoshell的JS语法,等号右边的updatedAt是你本地脚本里的未定义变量,自然会抛出ReferenceError。 - 你可能会想到用
$inc直接给字段加值,但3.6版本的$inc只支持数字类型字段,对Date类型操作会直接抛类型不匹配的错误,这条路本身就走不通。
MongoDB 3.6 下给日期字段加1秒的实现方案
因为3.6没有4.2版本的管道式update能力,直接走「查原文档取字段 → 计算新值 → 回写更新」的标准流程即可:
- 先匹配到要更新的文档,通过find拿到游标
- 遍历每一条查到的文档,取出原来的Date类型
updatedAt,转成毫秒值加1000(1秒对应的毫秒数),生成新的Date对象 - 对单条文档执行
$set回写新的时间值
示例代码:
db.getCollection("therecords").find({ // 匹配指定_id的文档,同时过滤出updatedAt确实是日期类型的,避免空值/类型错误 "_id": ObjectId("123etc"), "updatedAt": { $type: "date" } }).forEach(originalDoc => { // 计算新的时间:原时间的毫秒值 + 1000毫秒(1秒) const newUpdatedAt = new Date(originalDoc.updatedAt.getTime() + 1000); // 回写更新 db.getCollection("therecords").update( { "_id": originalDoc._id }, { $set: { "updatedAt": newUpdatedAt } } ); });
小提示:示例里的
ObjectId("123etc")是占位写法,实际使用时要替换为真实的24位十六进制格式的ObjectId,不然查询会匹配不到对应文档。如果是批量更新,只要修改find的查询条件即可,比如要给所有2023年产生的文档都加1秒,直接把find条件换成对应的日期范围即可。
通用问题:update的$set阶段能不能直接复用当前文档的字段值?
这个要看你使用的MongoDB版本,差异非常大:
- 对于你当前用的3.6这类 <4.2 的版本:常规的非管道式update下是没法直接实现的。所有写在
$set后面的键值对,值都是你提前在本地脚本计算好的常量、变量,MongoDB不会在服务端把$updatedAt这类字符串自动解析为当前文档的同名字段。所有需要复用文档自身字段的更新操作,都可以用上面提到的「查原文档、本地算新值、回写」的流程处理。 - 对于MongoDB 4.2及以上版本:可以直接用管道式update语法,不需要遍历文档,单条语句就能完成字段引用计算,比如给日期加1秒的写法为(你当前3.6版本不支持,仅做参考):
db.getCollection("therecords").update( { "_id": ObjectId("123etc") }, [ { $set: { updatedAt: { $add: ["$updatedAt", 1000] } }} ] );
这里update参数传的是数组形式的聚合管道,管道内的$updatedAt才会被正确识别为当前文档的字段值,和普通update里的字符串完全是两个概念。
内容的提问来源于stack exchange,提问作者Gabriel R.
相关产品推荐
相关产品推荐

