【问题标题】:MongodDB $pull only one element from array [duplicate]MongodB $从数组中仅拉出一个元素[重复]
【发布时间】:2017-07-06 14:48:15
【问题描述】:

我有一个里面有一个数组的文档,像这样:

"userTags" : [
        "foo",
        "foo",
        "foo",
        "foo",
        "moo",
        "bar"
    ]

如果我执行db.products.update({criteriaToGetDocument}, {$push: {userTags: "foo"}}},我可以正确地将foo 的另一个实例插入到数组中。

但是,如果我执行db.products.update({criteriaToGetDocument}, {$pull: {userTags: "foo"}}},那么它会从数组中删除foo所有 个实例,留下:

"userTags" : [
        "moo",
        "bar"
    ]

这根本行不通,我只想从数组中提取一项而不是全部。如何更改命令以便只删除一个foo?是否有某种$pullOnce 方法可以在这里工作?

【问题讨论】:

    标签: arrays mongodb database nosql


    【解决方案1】:

    不,目前没有这样的事情。很多人已经请求了该功能,您可以在mongodb Jira 跟踪它。据你所知,它没有解决,也没有安排(这意味着你在不久的将来没有运气)。

    唯一的选择是使用应用程序逻辑来实现这一点:

    1. 找到您想要的并且 userTags 为 foo 的元素
    2. 遍历 userTags 并从中删除一个 foo
    3. 使用新的 userTag 更新该元素

    请记住,此操作会破坏原子性,但由于 Mongo 没有提供这样做的本机方法,因此您将以任何方式破坏原子性。

    我将另一种解决方案移至新答案,因为它不回答这个问题,但代表了重构现有架构的方法之一。它也变得如此之大,开始比原来的答案大得多。

    【讨论】:

    • 这……很烦人。我想另一种方法是改变我的数据设计,使userTags 是一个带有{tag: (numberofinstances) 的对象,并在必要时增加/减少这个数字,但我真的不想这样做,因为它会弄乱很多其他东西!
    • 我同意你的看法。看起来这个功能真的很有用,如果不阅读文档,可能会误解 $pull 的想法。但是 mongo 中的人们在每次发布时都在增加 mongo 的功能做得很好。所以我相信有一天你会看到它开箱即用。附: - 为 JIRA 中的功能投票。
    • 不错的编辑。你能建议任何命令来检查标签是否存在然后检查它是否大于一?或者我是否必须在一次查找中找到tag: {$exists: true},然后使用新标签号进行更新?
    • @Jascination 答案在另一个答案中。我把它放在那里是因为它开始比原来的答案更大。
    【解决方案2】:

    如果你真的想使用这个功能,你必须考虑修改你的架构。一种方法是做这样的事情:

    "userTags" : {
       "foo" : 4,
       "moo": 1,
       "bar": 1
    }
    

    其中数字是标签的数量。这样您就可以使用 atomic $inc 来添加或删除标签。虽然添加新标签可能是适当的,但删除标签时您必须小心。您必须检查标签是否存在,并且 >= 然后 1。

    你可以这样做:

    db.a.insert({
      _id : 1,
      "userTags" : {
         "foo" : 4,
         "moo": 1,
         "bar": 1
      }
    })
    

    要添加一个标签,您只需这样做:

    db.a.update({_id : 1}, {$inc : {'userTags.zoo' : 1}})
    

    如果标签在创建之前不存在

    要删除标签,您需要做更多的事情:

    db.a.update({
      _id : 1,
      'userTags.moo' : {$gte : 1 }
    }, {
       $inc : {'userTags.moo' : -1}
    })
    

    在这里,您正在检查元素是否存在并且大于 1(无需检查是否存在,因为 $gte 也在这样做)。

    请记住,这种方法有一个缺点。您可以将一些标签列为字段,但不是真正的标签(它们的值为 0)。因此,如果您想找到所有带有标签 foo 的元素,您必须记住这一点并执行以下操作:

    db.a.find({'userTags.zoo' : { $gte : 1}})
    

    同样,当你输出一个元素的所有标签时,你可能会遇到同样的问题。所以你需要清理应用层的输出。

    【讨论】:

      【解决方案3】:

      我知道这是一种解决方法,而不是真正的解决方案,但是当我遇到同样的问题时,我发现至少在 PHP 中,您可以执行以下操作:

      // Remove up to two elements "bar" from an array called "foo" 
      // in a document that matches the query {foo:"bar"}
      
      $un_bar_qty = 2;
      $un_bar_me  = $db->foo->findOne(array("foo"=>"bar"));
      foreach($un_bar_me['bar'] as $foo => $bar) {
          if($bar == 'bar') {
              unset($un_bar_me['bar'][$foo]); 
              $un_bar_qty--;
          }
          if(!$un_bar_qty) break;
      }
      $db->foo->update(array('_id'=>$un_bar_me['_id']),$bar);
      

      之前的文档:

      { 
          { foo: "bar" },
          { bar: [ "bar" , "bar" , "foo", "bar" ] }
      }
      

      之后的文档:

      { 
          { foo: "bar" },
          { baz: [ "foo", "bar" ] }
      }
      

      这样做的方法还不错。对我来说,肯定比搞乱标签和增量更麻烦,YMMV。

      【讨论】:

        猜你喜欢
        • 2018-08-19
        • 2021-05-16
        • 1970-01-01
        • 2021-10-29
        • 1970-01-01
        • 2021-06-29
        • 2018-11-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多