MongoDB在PHP中返回一个匹配的数组元素


MongoDB returning a matching array element in PHP

在这个

例子中可以返回数组的特定项吗?我在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]
                 ]
             }
         }
     }
}};