【问题标题】:Update mongodb positional field with another positional field passed as parameter into momentjs使用作为参数传递给momentjs的另一个位置字段更新mongodb位置字段
【发布时间】:2021-06-21 17:25:45
【问题描述】:

我正在尝试更新数组中的所有文档。我查看了位置运算符 $[],但问题是我正在尝试使用传递给日期库 momentjs 的先前值更新字段。 我尝试失败的查询是

 await collection.update(
  {},
  {
    $set: {
      'activities.$[].duration.startTime': moment(
        'activities.$[].duration.startTime',
        'DD/MM/YYYY HH:mm:ss',
      ).toISOString(),
    },
  },
  { multi: true },
);

这会将startTime 字段设置为空。所以我的问题是如何将activities.$[].duration.startTime 的现有值传递给矩函数。

示例文档(现有)

{
"_id": {
    "$oid": "6030b9244991b3001c678492"
},
"activities": [{
    "effort": null,
    "duration": {
        "startTime": "19/01/2021 20:11:35",
        "endTime": "19/01/2021 20:15:15",
    },
}],
"userId": "5fc8c7504b26e66f676ea50d",
"workoutPlanCode": "",

}

预期输出:

{
"_id": {
    "$oid": "6030b9244991b3001c678492"
},
"activities": [{
    "effort": null,
    "duration": {
        "startTime": "2021-01-19T20:58:49.010Z",
        "endTime": "2021-01-19T20:58:49.010Z",
    },
}],
"userId": "5fc8c7504b26e66f676ea50d",
"workoutPlanCode": "",

}

【问题讨论】:

  • 你能确认你意识到你的nodejs应用服务器上安装了momentjs模块并且更新是在mongodb服务器上执行的吗?
  • @AlexBlex 是的,但是如果我想在 $set 中使用自定义函数,我该怎么做?
  • 你不能。没有人可以。
  • 为了清楚起见,您正在使用聚合运算符的相对较新的更新。那里没有 npm 包,甚至没有原生 javascript。您只能使用these operators。如果您尝试将日期字段转换为字符串,则 $dateToString 应该可以完成这项工作。
  • @Totoro 添加您的示例数据查询mongoplayground.net 并使用链接更新故事。另外,添加预期的输出。

标签: javascript mongodb mongodb-query momentjs


【解决方案1】:

这是 OP 提到的 1 次活动。

从 shell 或 Robomongo 运行此查询

forEach

toArray

activities 所在的位置执行查找查询过滤记录,获取一个游标并转换为toArray,遍历记录检查startTime 是否在那里将其转换为日期。

function convertToDate (date) {
    if (!date.split || date.split('/') < 1) return date; //  if it's already date type ignore.
    date = date.split('/');
                var month = date[1];
                var day = date[0]
                date[0] = month;
                date[1] = day;
    return new Date(date);
}

db.data2.find({ activities: { $exists: true } }).toArray().forEach(function (doc) {
    doc.activities.forEach(function (d) {
        if (d.duration) {
            if (d.duration.startTime) {
                d.duration.startTime = convertToDate(d.duration.startTime);  
            }
            if (d.duration.endTime) {
                d.duration.endTime = convertToDate(d.duration.endTime);  
            }
        }
    });
    // print(doc); // to validate just perform print and comment save part
    db.data2.save(doc); // save the document back
});

【讨论】:

    【解决方案2】:

    首先 - 在更新语句中一般无法使用 momentjs 库和 javascript。

    可以使用 v4.2 中提供的 aggregation pipeline 更新具有从文档本身计算的值的字段。

    要更新数组中的项目,您需要使用$map 来迭代和更新各个子文档。

    最后,针对这个问题,moment的一些功能可以使用date expression operators来实现。

    整个查询将是:

    db.collection.update({},
    [
      {
        "$set": {
          "activities": {
            "$map": {
              input: "$activities",
              as: "a",
              in: {
                $mergeObjects: [
                  "$$a",
                  {
                    duration: {
                      startTime: {
                        $dateToString: {
                          date: {
                            $dateFromString: {
                              dateString: "$$a.duration.startTime",
                              format: "%d/%m/%Y %H:%M:%S",
                              onError: null
                            }
                          },
                          format: "%Y-%m-%dT%H:%M:%S.000Z",
                          onNull: "$$a.duration.startTime"
                        }
                      },
                      endTime: {
                        $dateToString: {
                          date: {
                            $dateFromString: {
                              dateString: "$$a.duration.endTime",
                              format: "%d/%m/%Y %H:%M:%S",
                              onError: null
                            }
                          },
                          format: "%Y-%m-%dT%H:%M:%S.000Z",
                          onNull: "$$a.duration.endTime"
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      }
    ],
    {
      "multi": true
    })
    

    注意事项:

    • 如果字符串格式错误或格式不正确,从字符串到日期的转换可能会出错。在这种情况下,原始字符串被保留。
    • 原始日期没有时区,假定为 UTC
    • 原始日期没有毫秒,假定为 000
    • 建议将日期作为日期保存在数据库中,并在表示层上转换为字符串。

    【讨论】:

    • 如果有位置运算符docs.mongodb.com/manual/reference/operator/update/…,为什么还要映射
    • 它用于定期更新,您使用常量value。它与过滤器部分(更新的第一个参数)紧密耦合,而不是聚合更新中的东西。你注意到更新的第二个参数是一个数组吗?请阅读我在答案中添加的链接后面的文档。如果它不重要,它不会费心搜索链接。
    猜你喜欢
    • 2011-04-27
    • 1970-01-01
    • 2020-06-10
    相关资源
    最近更新 更多