【问题标题】:MongoDb: Delete index of an array within another array [duplicate]MongoDb:删除另一个数组中的数组索引[重复]
【发布时间】: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
             }
          ]
     }
  ]
}

【问题讨论】:

    标签: mongodb mongodb-query


    【解决方案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' }
                   ]
              }
          ]
      }
      

      【讨论】:

      • 谢谢,但我怎样才能将查询定义为仅删除第一次遇到(符合条件的项目)?
      • 太棒了,非常感谢您的精彩回答。
      猜你喜欢
      • 2020-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-24
      • 2021-05-07
      • 2015-07-27
      • 2020-03-02
      相关资源
      最近更新 更多