MongoDb:删除另一个数组中的数组索引[重复]
Posted
技术标签:
【中文标题】MongoDb:删除另一个数组中的数组索引[重复]【英文标题】:MongoDb: Delete index of an array within another array [duplicate] 【发布时间】:2018-10-23 10:23:36 【问题描述】:假设如下结构:
'Tests': [ // Top Array
'Name': 'A',
'Data': [ // Second Array
'Fact': '1'
]
]
当我尝试删除任何与值 1
匹配的 Fact
时,我编写了以下查询并且它有效!
此查询使用 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
]
]
【问题讨论】:
【参考方案1】:$pull 表达式将条件应用于数组的每个元素,就好像它是***文档一样。因此,它将测试数组为空。 为了克服这个问题,我们需要一些脚本。假设集合名称是 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); );
上面的代码查找嵌套元素事实,然后使用拼接运算符从内部数组中删除该元素。最后,修改后的文档被保存到集合中。
【讨论】:
【参考方案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
数组中第一个匹配条目内的第一个匹配实例,那么我认为这不能在单个操作中完成。但是,这里有一个似乎可行的 hack:
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'
]
]
【讨论】:
谢谢,但我怎样才能将查询定义为仅删除第一次遇到(符合条件的项目)? 太棒了,非常感谢您的精彩回答。以上是关于MongoDb:删除另一个数组中的数组索引[重复]的主要内容,如果未能解决你的问题,请参考以下文章