可以返回数组的特定项吗?我在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中数组中的一个匹配项吗?
谢谢!
答案 0 :(得分:0)
我想,我找到了解决方案:
$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
)
)
)
)
)
答案 1 :(得分:0)
为此,您需要使用聚合框架。
首先,您需要使用$match
运算符过滤掉所有与您的查询条件不匹配的文档。这减少了下一阶段要处理的文档数量。
从那里,您需要$project
您的文档并使用MongoDB 3.2中的$filter
运算符new来过滤掉那些与您的条件不符的子文档。当然$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);
正如您所看到的,这不是预期的输出格式,但足够接近。你只需停在这里并处理结果客户端。原因是为了获得确切的输出格式,你需要 denormalizate " items"使用$unwind
运算符进行两次数组运算,但如果处理大型集合,则此操作可能非常昂贵,从而导致性能下降。
$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)
))
)))
);
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);
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]
]
}
}
}
}};