在 MongoDB 个文档中移动元素
Move elements within MongoDB document
背景:
客户是具有名称字段的对象。
一行是具有以下字段的对象:
inLine
- 一组客户
currentCustomer
- 一位客户
processed
- 一组客户
集合 'line' 包含行对象文档。
问题:
我正在尝试实施一个将执行以下操作的程序:
- 将
currentCustomer
推到processed
- 将
currentCustomer
设置为inLine
中的第一个元素
- 弹出
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: 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"]
]
}
]
}
}
}]
);
基于,是这样解决的:
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'],
],
}
],
},
},
}]);
背景:
客户是具有名称字段的对象。
一行是具有以下字段的对象:
inLine
- 一组客户currentCustomer
- 一位客户processed
- 一组客户
集合 'line' 包含行对象文档。
问题:
我正在尝试实施一个将执行以下操作的程序:
- 将
currentCustomer
推到processed
- 将
currentCustomer
设置为inLine
中的第一个元素
- 弹出
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: 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"]
]
}
]
}
}
}]
);
基于
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'],
],
}
],
},
},
}]);