使用作为参数传递给 momentjs 的另一个位置字段更新 mongodb 位置字段

Update mongodb positional field with another positional field passed as parameter into momentjs

我正在尝试更新数组中的所有文档。我查看了位置运算符 $[],但问题是我正在尝试使用传递给日期库 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": "",

}

OP 提到了 1 次 activity。

运行 此查询来自 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
});

首先 - 在更新语句中一般无法使用 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
  • 建议将日期保留为数据库中的日期,并在表示层上转换为字符串。