在 MongoDB 个文档中移动元素

Move elements within MongoDB document

背景:

客户是具有名称字段的对象。

一行是具有以下字段的对象:

集合 'line' 包含行对象文档。


问题:

我正在尝试实施一个将执行以下操作的程序:

  1. currentCustomer推到processed
  2. currentCustomer设置为inLine
  3. 中的第一个元素
  4. 弹出inLine
  5. 的第一个元素

由于字段的新值取决于另一个字段的先前值,因此原子性在这里很重要。

到目前为止我尝试了什么:

天真的方法

db.collection('line').findOneAndUpdate({
    _id: new ObjectId(lineId),
}, {
    $set: {
        currentCustomer: '$inLine.0',
    },
    $pop: {
        inLine: -1,
    },
    $push: {
        processed: '$currentCustomer',
    },
});

但是,currentCustomer 设置为字面上为“$inLine.0”的字符串,processed 有一个字面上为“$currentCustomer”的字符串。

聚合方法

db.collection('line').findOneAndUpdate({
    _id: new ObjectId(lineId),
}, [{
    $set: {
        currentCustomer: '$inLine.0',
    },
    $pop: {
        inLine: -1,
    },
    $push: {
        processed: '$currentCustomer',
    },
}]);

但是,我收到以下错误:

MongoError: A pipeline stage specification object must contain exactly one field.

多阶段聚合方法

db.collection('line').findOneAndUpdate({
    _id: new ObjectId(lineId),
}, [{
    $set: {
        currentCustomer: '$inLine.0',
    },
}, {
    $pop: {
        inLine: -1,
    },
}, {
    $push: {
        processed: '$currentCustomer',
    },
}]);

但是,$pop$push 是无法识别的管道阶段名称。

我尝试只使用 $set 个阶段来制作它,但结果非常丑陋,我仍然无法让它工作。

我认为使用 $push or $pop 进行简单更新是不可能的。

根据您的实验,聚合无法支持根级别的直接 $push, $pop 阶段,因此我已更正您的查询,

  • currentCustomer 检查条件,如果 inLine 的大小为 0,则 return 为空,否则使用 $arrayElemAt、[=36 从 inLine 数组获取第一个元素=]
  • inLine 检查条件,如果 inLine 的大小为 0,则 return [] 否则使用 $slice and $size[=36 从 inLine 数组中删除第一个元素=]
  • processed 使用 $concatArrays, $ifNull 连接两个数组以检查字段是否为空然后 return 空白数组,检查条件如果 currentCustomer 为空则 return [ ]否则returncurrentCustomer
db.collection('line').findOneAndUpdate(
  { _id: new ObjectId(lineId), }, 
  [{
    $set: {
      currentCustomer: {
        $cond: [
          { $eq: [{ $size: "$inLine" }, 0] },
          null,
          { $arrayElemAt: ["$inLine", 0] }
        ]
      },
      inLine: {
        $cond: [
          { $eq: [{ $size: "$inLine" }, 0] },
          [],
          { $slice: ["$inLine", 1, { $size: "$inLine" }] }
        ]
      },
      processed: {
        $concatArrays: [
          { $ifNull: ["$processed", []] },
          {
            $cond: [
              { $eq: ["$currentCustomer", null] },
              [],
              ["$currentCustomer"]
            ]
          }
        ]
      }
    }
  }]
);

Playground

基于,是这样解决的:

db.collection('line').findOneAndUpdate({
    _id: new ObjectId(lineId),
}, [{
    $set: {
        // currentCustomer = inLine.length === 0 ? null : inLine[0]
        currentCustomer: {
            $cond: [
                { $eq: [{ $size: '$inLine' }, 0] },
                null,
                { $first: '$inLine' },
            ],
        },
        // inLine = inLine.slice(1)
        inLine: {
            $cond: [
                { $eq: [{ $size: '$inLine' }, 0] },
                [],
                { $slice: ['$inLine', 1, { $size: '$inLine' }] },
            ],
        },
        // if currentCustomer !== null then processed.push(currentCustomer)
        processed: {
            $cond: [
                {
                    $eq: ['$currentCustomer', null],
                },
                '$processed',
                {
                    $concatArrays: [
                        '$processed', ['$currentCustomer'],
                    ],
                }
            ],
        },
    },
}]);