Mongo 根据数组中子文档的值进行迁移以设置值

Mongo make migration to set value based on value of a subdocument in array

我有这样的文档结构:

_id: "xx"
statuses: [{
  status: "pending",
  timestamp: "1 january 10 pm",
}, {
  status: "accepted",
  timestamp: "2 january 2 am",
}]

现在我想在根结构中添加接受的时间戳。

_id: "xx"
statuses: [{
  status: "pending",
  timestamp: "1 january 10 pm",
}, {
  status: "accepted",
  timestamp: "2 january 2 am",
}]
last_accepted_at: "2 january 2 am"

我知道如何根据另一个根字段设置值,但不知道如何根据我必须过滤的数组项设置值。

我试过了:

db.task.findOneAndUpdate( { last_accepted_at: { $exists: false } }, { $set: { "last_accepted_at": "statuses.$[element].timestamp" }}, { arrayFilters: [ { "element.status": "accepted" } ] },  )

有错误

uncaught exception: Error: findAndModifyFailed failed: {
    "ok" : 0,
    "errmsg" : "The array filter for identifier 'element' was not used in the update { $set: { last_accepted_at: \"statuses.$[element].timestamp\" } }",
    "code" : 9,
    "codeName" : "FailedToParse"
} :

我也试过:

db.task.findOneAndUpdate( { last_accepted_at: { $exists: false } }, { $set: { "last_accepted_at": "statuses.$[element].timestamp" }}, { arrayFilters: [ { "element.$.status": "accepted" } ] },  )
uncaught exception: Error: findAndModifyFailed failed: {
    "ok" : 0,
    "errmsg" : "The array filter for identifier 'element' was not used in the update { $set: { last_accepted_at: \"statuses.$[element].timestamp\" } }",
    "code" : 9,
    "codeName" : "FailedToParse"
} :

如何实现?我也想使用 updateMany 因为它是一个迁移文件来更新所有旧数据。 谢谢

"The array filter for identifier 'element' was not used in the update { $set: { last_accepted_at: "statuses.$[element].timestamp" } }",

错误提示您不能将 arrayFilters 用作任何字段的值,您只能将其用作键的一部分,有关详细信息,请参阅 $[<identifier>]

其次,普通更新查询不允许内部字段作为另一个字段的值,需要使用update with aggregation pipeline从MongoDB 4.2开始,

  • $filter 迭代 statuses 数组的循环并获得“已接受”状态结果
  • $arrayElemAt 从上面过滤的结果中获取第一个元素
  • $set 从上面的 arrayElemAt 操作
  • 从 return 对象得到 timestamp
db.task.updateMany({
  last_accepted_at: { $exists: false },
  "statuses.status": "accepted"
},
[
  {
    $set: {
      last_accepted_at: {
        $arrayElemAt: [
          {
            $filter: {
              input: "$statuses",
              cond: { $eq: ["$$this.status", "accepted"] }
            }
          },
          0
        ]
      }
    }
  },
  {
    $set: { last_accepted_at: "$last_accepted_at.timestamp" }
  }
])

Playground