这是一种可能性(需要Mongo 4.2 或更好):
// { key: { 0: 2, 1: 5, 2: 100, 4: 15 } }
db.collection.update(
{},
[
{ $set: {
newkey: {
$toString: {
$min: {
$setDifference: [
{ $range: [0, 10] },
{ $map: {
input: { $objectToArray: "$key" },
as: "x",
in: { $toInt: "$$x.k" }
}}
]
}
}
}
}},
{ $set: {
key: {
$arrayToObject: {
$concatArrays: [
{ $objectToArray: "$key" },
[{ k: "$newkey", v: 1000 }]
]
}
}
}},
{ $unset: "newkey" }
])
// { key: { 0: 2, 1: 5, 2: 100, 4: 15, 3: 1000 } }
请注意,我们正在使用带有聚合管道(由 update({}, [...]) 中的方括号表示)的 update,该管道从 Mongo 4.2 开始可用,没有它我们无法根据自己的文档更新文档内容。
第一个$set 阶段用于创建一个临时值,该值是添加新值的新键(在我们的例子中为3):
-
首先将key 对象转换为{ k: "key", v: "value" } 对象数组,以便轻松访问键(请参阅$objectToArray):
{ $objectToArray: "$key" }
// { key: [{ k: "0", v: 2 }, { k: "1", v: 5 }, { k: "2", v: 100 }, { k: "4", v: 15 }] }
-
然后我们将k 键转换为整数数组(参见$map 和$toInt):
{ $map: { input: { ... }, as: "x", in: { $toInt: "$$x.k" } } }
// [ 0, 1, 2, 4 ]
-
然后我们与 0 到任何值(此处为 10)之间的范围相交(参见 $setDifference 和 $range):
$setDifference: [{ $range: [0, 10] }, {...}]
// [ 3, 5, 6, 7, 8, 9 ]
-
但如果您希望根据数组的最大键获得一个上限,也可以):只需将 10 替换为:
{ $max: { $map: { input: { $objectToArray: "$key" }, as: "x", in: { $toInt: "$$x.k" } } } }
// 4
-
我们可以从中提取最小值:
$min: { ... }
// 3
-
我们转换成字符串:
$toString: { ... }
// "3"
-
这样文档的中间版本就变成了:
{ key: { 0: 2, 1: 5, 2: 100, 4: 15 }, newkey: "3" }
第二个$set 阶段将key 对象替换为现有的key 对象,并附加了新的键值:
-
首先,将key 对象转换为{ k: "key", v: "value" } 对象数组:
{ $objectToArray: "$key" }
// { key: [{ k: "0", v: 2 }, { k: "1", v: 5 }, { k: "2", v: 100 }, { k: "4", v: 15 }] }
-
为了通过$concatArrays 操作附加新的键/值:
$concatArrays: [{...}, [{ k: "$newkey", v: 1000 }]]
// { key: [{ k: "0", v: 2 }, { k: "1", v: 5 }, { k: "2", v: 100 }, { k: "4", v: 15 }, { k: "3", v: 1000 }] }
-
我们最终可以转换回对象的数组(参见$arrayToObject):
$arrayToObject: { ... }
// { 0: 2, 1: 5, 2: 100, 4: 15, 3: 1000 }
最后一个阶段,$unset 阶段,用于删除中间变量 newkey。
请注意,可以通过将"$newkey" 替换为第一个$set 的内容,将第一个$set 阶段插入第二个阶段,但我认为这样可读性会降低。