MongodB $只从数组中提取一个元素

时间:2013-12-09 04:15:12

标签: arrays mongodb database nosql

我有一个包含数组的文档,如下所示:

"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方法可以在这里工作?

3 个答案:

答案 0 :(得分:4)

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

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

  1. 找到您想要的元素,并将userTags作为foo
  2. 遍历userTags并从中删除一个foo
  3. 使用新用户标记更新该元素
  4. 请记住,此操作会破坏原子性,但由于Mongo没有提供本机方法,因此您将以任何方式破坏原子性。

    我为新答案移动了一个替代解决方案,因为它没有回答这个问题,而是代表重构现有架构的方法之一。它也变得如此之大,开始比原来的答案大得多。

答案 1 :(得分: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}})

此外,当您输出元素的所有标记时,您可能会遇到同样的问题。因此,您需要清理应用程序层上的输出。

答案 2 :(得分:0)

我知道这是一种解决方法而不是真正的解决方案但是,当我遇到同样的问题时,我发现至少在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。