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:删除另一个数组中的数组索引[重复]的主要内容,如果未能解决你的问题,请参考以下文章

在mongodb中删除数组中的一个项目[重复]

从 mongodb 数组中的所有重复项中拉出一个元素

节点/猫鼬 - 从数组中删除 ObjectId [重复]

如何在 MONGODB 中删除数组中的重复值?

如何从js中的数组中删除元素[元素来自mongodb] [重复]

如何从js中的数组中删除元素[元素来自mongodb] [重复]