MongodDB $只从数组中提取一个元素[duplicate]

时间:2022-07-28 03:36:42

This question already has an answer here:

这个问题已经有了答案:

I have a document with an array inside, like this:

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

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

If I perform db.products.update({criteriaToGetDocument}, {$push: {userTags: "foo"}}} I can correctly insert another instance of foo into the array.

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

However, if I do db.products.update({criteriaToGetDocument}, {$pull: {userTags: "foo"}}} then it removes all instances of foo from the array, leaving me with:

但是,如果我做db.products。更新({criteriaToGetDocument}, {$pull: {userTags: "foo"}})然后它从数组中删除foo的所有实例,留给我:

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

This won't do at all, I only want to pull one item from the array rather than all of them. How can I alter the command so that only one foo is removed? Is there some sort of $pullOnce method that can work here?

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

3 个解决方案

#1


4  

No, there is nothing like this at the moment. A lot of people already requested the feature and you can track it in mongodb Jira. As far as you can see it is not resolved and also not scheduled (which means you have no luck in the near future).

不,此刻没有这样的事。很多人已经请求了这个特性,您可以在mongodb Jira中跟踪它。就您所见,它没有解决,也没有计划(这意味着您在不久的将来没有运气)。

The only option is to use application logic to achieve this would be:

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

  1. find element that you want and that has userTags as foo
  2. 找到您想要的元素,并且有usertag作为foo。
  3. iterate through userTags and remove one foo from it
  4. 遍历userTags,并从中删除一个foo
  5. update that element with a new userTags
  6. 使用新的userTags更新该元素

Keep in mind that this operation breaks atomicity, but because Mongo has not provided a native method to do so, you will break atomicity in any way.

请记住,这个操作破坏了原子性,但是由于Mongo没有提供一个本地方法来这样做,您将以任何方式破坏原子性。

I moved one alternative solution to the new answer, because it does not answer this question, but represents one of the approaches to refactor existing schema. It also became so big, that started to be much bigger then the original answer.

我将一个替代解决方案移到新的答案中,因为它没有回答这个问题,而是表示重构现有模式的一种方法。它也变得如此巨大,开始变得比原来的答案大得多。

#2


1  

If you really want to use this feature, you have to consider modifying your schema. One way to go is to do something like this:

如果您真的想要使用这个特性,您必须考虑修改您的模式。一种方法是这样做:

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

Where the number is the quantity of tags. This way you can use atomic $inc to add or remove the tag. While adding the new tag can be appropriate, while deleting the tags you have to be careful. You have to check that the tag exist and that it is >= then 1.

其中数字是标签的数量。通过这种方式,您可以使用atomic $inc添加或删除标记。虽然添加新标记是适当的,但是删除标记时必须小心。您必须检查标记是否存在,它是>= 1。

Here is how you can do it:

你可以这样做:

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

To add one Tag you need to do just this:

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

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

If the tag did not existed before it will create it

如果标记在创建之前不存在,则创建它

To remove the Tag, you need to do a little bit more:

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

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

Here you are checking that the element exist and is bigger then 1 (no need to check for existence because $gte is doing this as well).

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

Keep in mind that this approach has one drawback. You can have some tags that are listed as fields, but are not real tags (they have a value of 0). So if you want to find all elements with tag foo you have to remember this and do something like this:

记住,这种方法有一个缺点。你可以列出一些标记作为字段,但不是真正的标记(它们的值为0)。

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

Also when you output all tags for an element, you might have the same problem. So you need to sanitize the output on application layer.

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

#3


0  

I know it's a workaround and not a real solution but, as I ran into this same issue, I found that in PHP at least, you can do something like this:

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

Document before:

文档:

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

Document after:

文档后:

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

Not too bad of a way to do it. Certainly seemed less of a hassle to me than messing around with tags and increments, YMMV.

这还不算太糟。当然,对我来说,这比摆弄标签和增量要省事,YMMV。

#1


4  

No, there is nothing like this at the moment. A lot of people already requested the feature and you can track it in mongodb Jira. As far as you can see it is not resolved and also not scheduled (which means you have no luck in the near future).

不,此刻没有这样的事。很多人已经请求了这个特性,您可以在mongodb Jira中跟踪它。就您所见,它没有解决,也没有计划(这意味着您在不久的将来没有运气)。

The only option is to use application logic to achieve this would be:

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

  1. find element that you want and that has userTags as foo
  2. 找到您想要的元素,并且有usertag作为foo。
  3. iterate through userTags and remove one foo from it
  4. 遍历userTags,并从中删除一个foo
  5. update that element with a new userTags
  6. 使用新的userTags更新该元素

Keep in mind that this operation breaks atomicity, but because Mongo has not provided a native method to do so, you will break atomicity in any way.

请记住,这个操作破坏了原子性,但是由于Mongo没有提供一个本地方法来这样做,您将以任何方式破坏原子性。

I moved one alternative solution to the new answer, because it does not answer this question, but represents one of the approaches to refactor existing schema. It also became so big, that started to be much bigger then the original answer.

我将一个替代解决方案移到新的答案中,因为它没有回答这个问题,而是表示重构现有模式的一种方法。它也变得如此巨大,开始变得比原来的答案大得多。

#2


1  

If you really want to use this feature, you have to consider modifying your schema. One way to go is to do something like this:

如果您真的想要使用这个特性,您必须考虑修改您的模式。一种方法是这样做:

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

Where the number is the quantity of tags. This way you can use atomic $inc to add or remove the tag. While adding the new tag can be appropriate, while deleting the tags you have to be careful. You have to check that the tag exist and that it is >= then 1.

其中数字是标签的数量。通过这种方式,您可以使用atomic $inc添加或删除标记。虽然添加新标记是适当的,但是删除标记时必须小心。您必须检查标记是否存在,它是>= 1。

Here is how you can do it:

你可以这样做:

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

To add one Tag you need to do just this:

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

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

If the tag did not existed before it will create it

如果标记在创建之前不存在,则创建它

To remove the Tag, you need to do a little bit more:

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

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

Here you are checking that the element exist and is bigger then 1 (no need to check for existence because $gte is doing this as well).

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

Keep in mind that this approach has one drawback. You can have some tags that are listed as fields, but are not real tags (they have a value of 0). So if you want to find all elements with tag foo you have to remember this and do something like this:

记住,这种方法有一个缺点。你可以列出一些标记作为字段,但不是真正的标记(它们的值为0)。

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

Also when you output all tags for an element, you might have the same problem. So you need to sanitize the output on application layer.

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

#3


0  

I know it's a workaround and not a real solution but, as I ran into this same issue, I found that in PHP at least, you can do something like this:

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

Document before:

文档:

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

Document after:

文档后:

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

Not too bad of a way to do it. Certainly seemed less of a hassle to me than messing around with tags and increments, YMMV.

这还不算太糟。当然,对我来说,这比摆弄标签和增量要省事,YMMV。