我试图删除分类在多个键内的对象内的项目。 例如,从每个项目部分删除ObjectId(" c")
这是结构:
{
"somefield" : "value",
"somefield2" : "value",
"objects" : {
"/" : {
"color" : "#112233",
"items" : [
ObjectId("c"),
ObjectId("b")
]
},
"/folder1" : {
"color" : "#112233",
"items" : [
ObjectId("c"),
ObjectId("d")
]
},
"/folder2" : {
"color" : "112233",
"items" : []
},
"/testing" : {
"color" : "112233",
"items" : [
ObjectId("c"),
ObjectId("f")
]
}
}
}
我尝试拉和未设置如下:
db.getCollection('col').update(
{},
{ $unset: { 'objects.$.items': ObjectId("c") } },
{ multi: true }
)
和
db.getCollection('col').update(
{},
{ "objects": {"items": { $pull: [ObjectId("c")] } } },
{ multi: true }
)
有什么想法吗?谢谢!
答案 0 :(得分:2)
这里的问题很大程度上取决于文档的当前结构。 MongoDB不能遍历路径"以一种有效的方式,你的结构目前有一个"对象" ('对象')已命名"键"。这意味着访问"项目"在每个"键"需要每个键的显式路径才能看到该元素。这里没有通配符:
db.getCollection("coll").find({ "objects./.items": Object("c") })
这是"匹配"的基本原则。你无法做到的事情"所有的钥匙"没有导致JavaScript代码,这真的很糟糕。
更改结构。而不是"对象键",使用"数组"相反,像这样:
{
"somefield" : "value",
"somefield2" : "value",
"objects" : [
{
"path": "/",
"color" : "#112233",
"items" : [
"c",
"b"
]
},
{
"path": "/folder1",
"color" : "#112233",
"items" : [
"c",
"d"
]
},
{
"path": "/folder2",
"color" : "112233",
"items" : []
},
{
"path": "/testing",
"color" : "112233",
"items" : [
"c",
"f"
]
}
]
}
从长远来看,它更加灵活,并且还允许您进行索引"像" path"用于查询匹配。
然而,它在这里对你没什么帮助,即使是一致的查询路径,即:
db.getCollection("coll").find({ "objects.items": Object("c") })
哪个更好,但问题仍然存在,即在同一个单一操作中,来自多个源(无论是对象还是数组)的$pull
是不可能的。而且,#34;永远不会增加#34;跨多个文件。
所以,你所能到达的最好的基本上是"尝试" "多重更新"概念,直到选项用尽,没有任何东西可以更新"。随着"修改"结构呈现然后你可以这样做:
var bulk = db.getCollection("coll").initializeOrderedBulkOp(),
count = 0,
modified = 1;
while ( modified != 0 ) {
bulk.find({ "objects.items": "c"}).update({
"$pull": { "objects.$.items": "c" }
});
count++;
var result = bulk.execute();
bulk = db.getCollection("coll").initializeOrderedBulkOp();
modified = result.nModified;
}
print("iterated: " + count);
使用"Bulk" operations API(实际上所有shell方法现在都使用它)基本上得到了更好的写响应"它为您提供有关"更新"尝试。
关键是基本上是"循环"并尝试根据"查询"匹配文档。更新的一部分然后从匹配的数组索引$pull
尝试来自"内部数组的项目"符合$pull
的条件(作为"查询"本身,仅在数组项目上)。
在每次迭代中,您基本上都会获得" nModified"来自响应的值,当这最终为0
时,操作就完成了。
在给定的样本(重组)上,这将进行4
次迭代,每次迭代一次"外部"数组成员。更新是"多"正如批量.update()
(而不是.updateOne()
)暗示的那样,因此"最大值"迭代由"最大值"数组元素存在于"外部"整个集合中的数组。所以如果有"一个"记录出来的一千个"如果有20
个条目,则迭代将为20
,并且只是因为该文档仍然具有可以匹配和修改的内容。
您当前结构下的备用案例并未提及。这很简单,不可能"无:
单独检索文档
提取当前密钥
为该密钥下的数组运行个人$pull
获取下一份文件,冲洗并重复
所以"多"是"马上出去"作为一种选择,如果没有一些可能的"预知"可能的"键"在"对象"关键在文件中。
所以请"改变你的结构"并了解可用的一般限制。
你不可能在"一个"更新,但至少如果最大的"数组条目"你的文件是" 4"然后最好做"四"更新超过"千"文件比"四千"否则就是必需的。
另外。请不要"混淆"帖子中的ObjectId
值。人们喜欢复制/粘贴"用于测试自己的代码和数据。使用ObjectId("c")
这样不的有效ObjectId
值会明显导致错误,因此对于人们来说不实用。
做什么"我做了"在列表中,如果你想抽象/混淆,那么用"普通值"正如我所展示的那样。
答案 1 :(得分:1)
您可以采用的一种方法是使用JavaScript本机方法(如 reduce )来创建将在更新中使用的文档。 您基本上需要如下操作:
var itemId = ObjectId("55ba3a983857192828978fec");
db.col.find().forEach(function(doc) {
var update = {
"object./.items": itemId,
"object./folder1.items": itemId,
"object./folder2.items": itemId,
"object./testing.items": itemId
};
db.col.update(
{ "_id": doc._id },
{
"$pull": update
}
);
})
因此,要创建更新对象,需要使用 reduce 方法将数组转换为对象:
var update = Object.getOwnPropertyNames(doc.objects).reduce(function(o, v, i) {
o["objects." + v + ".items"] = itemId;
return o;
}, {});
总体而言,您需要使用 Bulk 操作来实现上述更新:
var bulk = db.col.initializeUnorderedBulkOp(),
itemId = ObjectId("55ba3a983857192828978fec"),
count = 0;
db.col.find().forEach(function(doc) {
var update = Object.getOwnPropertyNames(doc.objects).reduce(function(o, v, i) {
o["objects." + v + ".items"] = itemId;
return o;
}, {});
bulk.find({ "_id": doc._id }).updateOne({
"$pull": update
})
count++;
if (count % 1000 == 0) {
bulk.execute();
bulk = db.col.initializeUnorderedBulkOp();
}
})
if (count % 1000 != 0) { bulk.execute(); }