假设以下结构:
{
'Tests': [ // Top Array
{
'Name': 'A',
'Data': [ // Second Array
{
'Fact': '1'
}
]
}
]
}
当我尝试删除与值Fact
匹配的任何1
时,我会编写以下查询并且它可以正常工作!
此查询使用C#驱动程序
运行
{$pull: {Tests: {Data: {Fact: '1'}}}}
这里的问题是,尽管它删除了Fact: 1
,但也删除了对象的父对象,在这种情况下,Tests
数组为空。
为了抛弃此问题,我尝试将查询更改为:
{$pull: {'Tests.Data.Fact': '1'}}
或者
{$pull: {'Tests.$.Data.Fact': '1'}} // and also {$pull: {'Tests.Data.$.Fact': '1'}}
或
{$pull: {'Tests.$.Data.$.Fact': '1'}}
但都失败了。
问题是,如果我只想删除Fact: '1'
并获得以下结果,那么我应遵循的语法是什么:
{
'Tests': [ // Top Array
{
'Name': 'A',
'Data': [ // Second Array
{
//Empty
}
]
}
]
}
答案 0 :(得分:2)
使用MongoDB> v3.6你可以使用"all positional operator" $[]来实现:
db.collection.update({}, { $pull: { "Tests.$[].Data": { "Fact": "1" } } })
更新以回复您的评论:
如果要从Tests
数组中的第一个匹配条目中提取所有匹配的实例,则可以这样做:
db.collection.update({"Tests.Data": { $elemMatch: { "Fact": "1" } } }, { $pull: { "Tests.$.Data": { "Fact": "1" } } })
让我们看一下以下示例文档:
{
"Tests" : [
{
"Name" : "A",
"Data" : [
{ 'Fact': '1' }, // first matching entry in "A"
{ 'Fact': '1' }, // second matching entry in "A"
{ 'Fact': '2' },
]
},
{
"Name" : "B",
"Data" : [
{ 'Fact': '1' }, // first matching entry in "B"
{ 'Fact': '1' }, // second matching entry in "B"
{ 'Fact': '2' },
]
}
]
}
运行上面的查询一次会给你:
{
"Tests" : [
{
"Name" : "A",
"Data" : [
// all matching items gone from "A"
{ 'Fact': '2' }
]
},
{
"Name" : "B",
"Data" : [
{ 'Fact': '1' }, // first matching entry in "B"
{ 'Fact': '1' }, // second matching entry in "B"
{ 'Fact': '2' }
]
}
]
}
第二次运行此操作也会清除"B"
中的所有实例。
{
"Tests" : [
{
"Name" : "A",
"Data" : [
// all matching items gone from "A"
{ 'Fact': '2' }
]
},
{
"Name" : "B",
"Data" : [
// all matching items gone from "B"
{ 'Fact': '2' }
]
}
]
}
但是,如果您只想更新Tests
数组中第一个匹配条目中的第一个匹配实例,那么我认为这不能在一次操作中完成。但是,这里看起来有点像黑客一样:
db.collection.update({"Tests.Data": { $elemMatch: { "Fact": "1" } } }, { $set: { "Tests.$.Data.0": { "delete_me": 1 } } }) // this will set the first found { Fact: "1" } document inside the Tests.Data arrays to { delete_me: 1 }
db.collection.update({}, { $pull: { "Tests.$[].Data": { "delete_me": 1 } } }) // this will just delete the marked records from all arrays
运行此查询一次将产生以下结果:
{
"Tests" : [
{
"Name" : "A",
"Data" : [
// first matching item gone from "A"
{ 'Fact': '1' }, // second matching entry in "A"
{ 'Fact': '2' }
]
},
{
"Name" : "B",
"Data" : [
{ 'Fact': '1' }, // first matching entry in Name "B"
{ 'Fact': '1' }, // second matching entry in Name "B"
{ 'Fact': '2' }
]
}
]
}
下次再次运行时,将删除另一个条目:
{
"Tests" : [
{
"Name" : "A",
"Data" : [
// all matching items gone from "A"
{ 'Fact': '2' }
]
},
{
"Name" : "B",
"Data" : [
{ 'Fact': '1' }, // first matching entry in Name "B"
{ 'Fact': '1' }, // second matching entry in Name "B"
{ 'Fact': '2' }
]
}
]
}
第三次运行:
{
"Tests" : [
{
"Name" : "A",
"Data" : [
// all matching items gone from "A"
{ 'Fact': '2' }
]
},
{
"Name" : "B",
"Data" : [
// first matching item gone from "B"
{ 'Fact': '1' }, // second matching entry in Name "B"
{ 'Fact': '2' }
]
}
]
}
最后,第四次运行:
{
"Tests" : [
{
"Name" : "A",
"Data" : [
// all matching items gone from "A"
{ 'Fact': '2' }
]
},
{
"Name" : "B",
"Data" : [
// all matching items gone from "B"
{ 'Fact': '2' }
]
}
]
}
答案 1 :(得分:0)
$ pull表达式将条件应用于数组的每个元素,就像它是顶级文档一样。因此,它将测试数组为null。 为了解决这个问题,我们需要一些脚本。假设集合名称为 TestArray ,javascript可以在mongo shell上运行:
db.TestArray.find({"_id":2}).forEach(function(doc){
var TestArray = doc.Test ;
for (var i =0 ; i < TestArray.length ; i++) {
DataArray = TestArray[i].Data;
for (var j =0 ; j < DataArray.length ; j++) {
DataElement = DataArray[j];
if (DataElement.fact == 1 )
DataArray.splice(j,1) ;
}
}
db.TestArray.save(doc); });
上面的代码查找嵌套元素事实,然后使用splice运算符从内部数组中删除该元素。最后,修改后的文档将保存到集合中。