MongoDb:删除另一个数组

时间:2018-05-13 17:29:29

标签: mongodb mongodb-query

假设以下结构:

{
  '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
             }
          ]
     }
  ]
}

2 个答案:

答案 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运算符从内部数组中删除该元素。最后,修改后的文档将保存到集合中。