在这个
例子中可以返回数组的特定项吗?我在PHP中使用mongo版本3(3.0.4)和mongoDB驱动程序版本1.6.11。
我在 mongo 集合中有这个对象:
{
"_id": ObjectId('56e9247287f5204c0a000001'),
"name": "test name",
"folders": [
{
"name": "folder name",
"items": [
{
"name": "item 1",
"internalID": 1
},
{
"name": "item 2",
"internalID": 2
},
{
"name": "item 3",
"internalID": 3
},
{
"name": "item 4",
"internalID": 4
}
]
}
]
}
我需要查找并返回对象,其中有一项内部ID = 2。结果应该是这样的:
{
"_id": ObjectId('56e9247287f5204c0a000001'),
"items": [
{
"name": "item 2",
"internalID": 2
},
]
}
下面的代码不起作用,返回包含所有项目的完整对象。
$collection->find(
[
'folders.items.internalID' => 2,
],
[
'folders' => [
'$elemMatch' => [
'items.internalID' => 2
]
]
]
);
你能帮我吗? 是否可以从 Mongodb 中的数组中只返回一个匹配的项目?
谢谢!
我认为
,我找到了解决方案:
$collection->aggregate(
array(
# First match the "document" to reduce the pipeline
array(
'$match' => array(
'folders.items.internalID' => 3
)
),
# Then unwind the array
array('$unwind' => '$folders'),
array('$unwind' => '$folders.items'),
# Match again on the "unwound" elements to filter
array(
'$match' => array(
'folders.items.internalID' => 3
)
),
# Group back to original structure per document
array(
'$group' => array(
'_id' => '$_id',
'items' => array(
'$push' => '$folders.items'
)
)
),
array(
'$group' => array(
'_id' => '$_id',
'items' => array(
'$push' => '$items'
)
)
)
)
);
返回:
Array
(
[0] => Array
(
[_id] => MongoId Object
(
[$id] => 56e9494087f5204c0a000002
)
[items] => Array
(
[0] => Array
(
[0] => Array
(
[name] => item 3
[internalID] => 3
)
)
)
)
)
为此,您需要使用聚合框架。
首先,您需要使用 $match
运算符过滤掉所有与您的查询条件不匹配的文档。这减少了下一阶段要处理的文档数量。
从那里,您需要$project
文档并使用MongoDB 3.2中新增的$filter
运算符来过滤掉那些不符合您条件的子文档。当然,$map
运算符返回一个值数组;在这里,它只是返回 2D 数组。
$match = array('$match' => array('folders.items.internalID' => 2));
$project = array(
'$project' => array('item' => array('$map' => array(
'input' => '$folders',
'as' => 'f',
'in' => array('$filter' => array(
'input' => '$$f.items',
'as' => 'af',
'cond' => array('$eq' => array('$$af.internalID', 2))
)),
)))
);
这会产生这个:
{
"_id" : ObjectId("56e96b784839cea8e9a1d098"),
"item" : [
[
{
"name" : "item 2",
"internalID" : 2
}
]
]
}
{
"_id" : ObjectId("56e96bcb4839cea8e9a1d099"),
"item" : [
[
{
"name" : "item 2",
"internalID" : 2
}
],
[
{
"name" : "item 2",
"internalID" : 2
}
]
]
}
$pipeline = array($match, $project);
$out = $c->aggregate($pipeline);
如您所见,这不是预期的输出格式,但足够接近。您只需停在这里并在客户端处理您的结果。原因是因为要获得确切的输出格式,您需要使用 $unwind
运算符对"items"数组进行两次非规范化,但如果您正在处理大型集合,此操作可能会非常昂贵,从而导致性能下降。
$unwind = array('$unwind' => '$item');
$group = array('$group' => array(
'_id' => '$_id',
'item' => array('$push' => '$item')
));
$pipeline = array($match, $project, $unwind, $unwind, $group);
$out = $c->aggregate($pipeline);
<小时 />由于旧版本不支持$filter
运算符,因此需要使用$setDifference
运算符。
$project = array(
'$project' => array('item' => array('$map' => array(
'input' => '$folders',
'as' => 'f',
'in' => array('$setDifference' => array(
'$map' => array(
'input' => '$$f.items',
'as' => 'af',
'in' => array('$cond' => array('$eq' => array('$$af.internalID', 2)), "$$af", false)
),
array(false)
))
)))
);
版本 3.2 或更高版本的外壳解决方案。
var match = { "$match": { "folders.items.intenalID": 2 } };
var project = { "$project": {
"item": {
"$map": {
"input": "$folders",
"as": "f",
"in": {
"$filter": {
"input": "$$f.items",
"as": "af",
"cond": { "$eq": [ "$$af.internalID", 2 ] }
}
}
}
}
}};
// optional
var unwind = { "$unwind": "$item" };
var group = {
"$group": {
"_id": "$_id",
"item": { "$push": "$item" }
}
};
db.collection.aggregate(match, project, unwind, unwind, group);
<小时 />版本 <= 3.0 解决方案。
var project = { "$project": {
"item": {
"$map": {
"input": "$folders",
"as": "f",
"in": {
"$setDifference": [
{ "$map": {
"input": "$$f.items",
"as": "af",
"in": {
"$cond": [
{ "$eq": [ "$$af.internalID", 2 ] },
"$$af",
false
]
}
}},
[false]
]
}
}
}
}};