Arrays MongodDB$仅从数组中提取一个元素
我有一个内部带有数组的文档,如下所示:Arrays MongodDB$仅从数组中提取一个元素,arrays,mongodb,database,nosql,Arrays,Mongodb,Database,Nosql,我有一个内部带有数组的文档,如下所示: "userTags" : [ "foo", "foo", "foo", "foo", "moo", "bar" ] // Remove up to two elements "bar" from an array called "foo" // in a document that matches the query {foo:"bar"} $un
"userTags" : [
"foo",
"foo",
"foo",
"foo",
"moo",
"bar"
]
// 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);
如果我执行db.products.update({criteriaToGetDocument},{$push:{userTags:“foo”}}}}
我可以将foo
的另一个实例正确插入数组
但是,如果我执行了db.products.update({criteriaToGetDocument},{$pull:{userTags:“foo”}}}}}
,那么它将从数组中删除foo
的所有实例,留给我:
"userTags" : [
"moo",
"bar"
]
这根本不行,我只想从数组中提取一个项目,而不是全部。我如何更改命令,以便只删除一个
foo
?是否有某种$pullOnce
方法可以在这里工作?不,目前没有类似的方法。很多人已经请求了该功能,您可以跟踪它。据你所知,它没有解决,也没有计划(这意味着你在不久的将来没有运气)
唯一的选择是使用应用程序逻辑来实现这一点,即:
我提出了一个新的答案的替代方案,因为它不回答这个问题,但它代表了重构现有模式的方法之一。它也变得如此之大,它开始比原来的答案大得多。
如果你真的想使用这个特性,你必须考虑修改你的模式。要这样做:"userTags" : {
"foo" : 4,
"moo": 1,
"bar": 1
}
db.a.find({'userTags.zoo' : { $gte : 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的所有元素,您必须记住这一点,并执行以下操作:
"userTags" : {
"foo" : 4,
"moo": 1,
"bar": 1
}
db.a.find({'userTags.zoo' : { $gte : 1}})
另外,当您输出一个元素的所有标记时,您可能会遇到同样的问题。因此,您需要在应用程序层上清理输出。我知道这是一种解决方法,不是真正的解决方案,但是,当我遇到同样的问题时,我发现至少在PHP中,您可以执行以下操作:
"userTags" : [
"foo",
"foo",
"foo",
"foo",
"moo",
"bar"
]
// 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。这……非常烦人。我想另一种方法是更改我的数据设计,以便
userTags
是一个带有{tag:(numberofinstances)的对象
只要在必要的时候增加/减少这个数字,但我真的不想这样做,因为它会干扰大量的其他东西!我同意你的看法。看起来这个功能非常有用,如果不阅读文档,你可能会误解$pull的概念。但是mongo的人在增加这个功能方面做得很好所以我相信有一天你会看到这个开箱即用的。另外,投票给JIRA中的功能。编辑得很好。你能推荐任何命令来检查标记是否存在,然后检查它是否大于1吗?或者我必须找到标记:{$exists:true}
在一次查找中,然后使用新的标签号进行更新?@Jascination答案在另一个答案中。我把它放在那里,因为它开始比原始答案大。