我想向数组的所有元素添加一个id
字段。没有值添加它没问题:
db.getCollection('mycollection').update({}, {$set : {"data.gastroscopy.$[].id": null }}
)
但是我真正需要的是将id值设置为元素索引,以获得类似这样的信息:
{
data: {
gastroscopy: [
{
id: 1,
foo: 'bar'
},
{
id: 2,
foo: 'zar'
},
{
id: 3,
foo: 'car'
},
]
}
}
I could not find a way to refer the index and use it. Is it possible?
thank you
答案 0 :(得分:0)
这是“可能的” ,但是唯一可以在单条语句中实现的方法是using MongoDB 4.2 or greater:
db.getCollection('mycollection').updateMany(
{},
[{ "$set": {
"data.gastrocopy": {
"$map": {
"input": {
"$range": [0, { "$size": "$data.gastrocopy" }]
},
"in": {
"$mergeObjects": [
{ "$arrayElemAt": [ "$data.gastocopy", "$$this" ] },
{ "id": { "$add": [ "$$this", 1 ] } }
]
}
}
}
}}]
)
所以主要的技巧是$range
运算符,它可以生成本质上是索引值的数组,然后将其与$map
一起使用,以便将这些值转换为原始数组内容。但是请注意,在任何“更新”语句中使用这些聚合运算符是一项相对较新的功能(在撰写本文时),并且需要MongoDB 4.2或更高版本。
否则,您基本上就是循环收集并回写每个文档,最好使用bulkWrite()
:
var batch = [];
db.getCollection('mycollection').find().forEach(doc => {
batch.push({
"updateOne": {
"filter": { _id: doc._id },
"udpate": {
"$set": { "data.gastrocopy": doc.data.gastrocopy.map((e,i) => ({ ...e, id: i+1 })) }
}
}
});
if (batch.length >= 1000) {
db.getCollection('mycollection').bulkWrite(batch);
batch = [];
}
})
if (batch.length > 0) {
db.getCollection('mycollection').bulkWrite(batch);
batch = [];
}
基本上是相同的基本机制,但是当然要反复进行,并通过将每个文档读回客户端,因为在早期的MongoDB版本中没有其他方法可以做到这一点。
我也不喜欢这两种方法,因为两者都存在覆盖现有内容的固有风险,这些现有内容可能是通过在读取和读取之间的另一个过程添加的 写。 MongoDB 4.2“聚合”语法的出现机会较少,但我相信在大容量环境中它的使用机会仍然较小。
因此,实施风险自负。您可以通过位置更新使某些事情变得更好,但对“索引值”的任何更新都不是真正可靠的。同样,这完全取决于您对“所述”数组的实际使用情况。
我会注意到,尽管如此,我仍然很难看到这种更新的实用性。数组毕竟是 Array ,因此固有地具有索引位置(特别是n-1
,这是标准约定)。如果要按索引位置匹配事物,则MongoDB中已经内置了操作,而无需添加任意值进行存储。
您可能会查看"dot notation"和$slice
之类的运算符进行投影甚至聚合$slice
来意识到您要创建的内容已经存在。关于仅 值得使用的地方将是带有查询表达式的,例如:
{ "data.gastrocopy": { "$elemMatch": { "id": { "$gte": 0, "$lte": 3 } } } }
然后仅当您在该id
字段上创建索引时才可以。这将比已经提到的其他“基于数组索引”的方法具有更高的性能。
因此,实际上是否需要这样的更新取决于您要使用它的方式。