2

背景:

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

线是具有以下字段的对象:

  • inLine- 一系列客户
  • currentCustomer- 一个客户
  • processed- 一系列客户

集合“行”包含作为行对象的文档。


问题:

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

  1. currentCustomerprocessed
  2. 设置currentCustomer为第一个元素inLine
  3. 弹出第一个元素inLine

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

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

天真的方法

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:管道阶段规范对象必须只包含一个字段。

多阶段聚合方法

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

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

我尝试只使用$set阶段来制作它,但它最终变得非常丑陋,我仍然无法让它工作。

4

2 回答 2

2

根据turivishal 的回答,它是这样解决的:

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'],
                    ],
                }
            ],
        },
    },
}]);
于 2020-11-16T17:10:02.870 回答
1

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

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

  • currentCustomer检查条件,如果大小inLine为 0,则返回 null,否则使用$arrayElemAtinLine从数组中获取第一个元素,
  • inLine检查条件如果大小为 0 然后返回 [] 否则使用$slice$size从数组中inLine删除第一个元素inLine
  • processed使用$concatArrays连接两个数组,$ifNull检查字段是否为 null 然后返回空白数组,检查条件如果为currentCustomernull 然后返回 [] 否则返回currentCustomer
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"]
            ]
          }
        ]
      }
    }
  }]
);

操场

于 2020-11-16T16:30:01.087 回答