根据经验,在文档中使用动态字段名称(例如objectives.37
)会降低索引和查询的灵活性。对于初学者,您将无法利用多键索引。
不过,我们可以使用map/reduce来聚合和计算现有架构中的不同 ID。为了使以下示例保持简洁,我将字段名称缩短为o
数据夹具中的不相关数据。
<?php
$m = new Mongo();
$db = $m->test;
$db->foo->drop();
$db->foo->insert(['o' => [36 => [], 63 => [], 64 => []]]);
$db->foo->insert(['o' => [12 => [], 36 => [], 97 => []]]);
$result = $db->command([
'mapreduce' => 'foo',
'map' => new MongoCode('
function() {
for (var key in this.o) emit(key, { count: 1 });
}
'),
'reduce' => new MongoCode('
function(key, values) {
var r = { count: 0 };
values.forEach(function(v) { r.count += v.count; });
return r;
}
'),
'out' => ['inline' => 1]
]);
echo json_encode($result, JSON_PRETTY_PRINT);
在这里,我们在整个集合中执行 map 函数,在o
我们处理的对象中发出每个唯一键。对于每次发射,我们发射的初始值为{count: 1}
。映射后,我们有一大堆由密钥和{count: 1}
对组成的排放量。然后调用我们的 reduce 方法来处理不同键的这些中间结果。参数中的每个对象都values
遵循相同的结构(即我们之前发出的相同值),并且我们期望返回相同结构的单个简化值。
该脚本将产生以下输出:
$ php mr.php
{
"results": [
{
"_id": "12",
"value": {
"count": 1
}
},
{
"_id": "36",
"value": {
"count": 2
}
},
{
"_id": "63",
"value": {
"count": 1
}
},
{
"_id": "64",
"value": {
"count": 1
}
},
{
"_id": "97",
"value": {
"count": 1
}
}
],
"timeMillis": 17,
"counts": {
"input": 2,
"emit": 6,
"reduce": 1,
"output": 5
},
"ok": 1
}
如果您改为重新设计架构,使其objectives
成为嵌套对象数组,我们可以利用 MongoDB 2.2+ 中的聚合框架来更轻松地计算相同的结果:
<?php
$m = new Mongo();
$db = $m->test;
$db->foo->drop();
$db->foo->insert(['o' => [['id' => 36], ['id' => 63], ['id' => 64]]]);
$db->foo->insert(['o' => [['id' => 12], ['id' => 36], ['id' => 97]]]);
$result = $db->command([
'aggregate' => 'foo',
'pipeline' => [
['$project' => ['o' => 1]],
['$unwind' => '$o'],
['$group' => ['_id' => '$o.id', 'count' => ['$sum' => 1]]],
],
]);
echo json_encode($result, JSON_PRETTY_PRINT);
在这里,我们使用聚合管道依次进行三个操作:
- 投影扫描文档的
o
字段,类似于在查找查询中选择特定的输出字段。
o
展开我们遇到的每个数组。将管道视为文档流,我们将有两个文档进入此步骤,每个文档在各自的o
字段中都有三个嵌套对象。展开将产生六个文档进行下一步,每个o
数组字段由数组的一个元素替换。
- 此时对流中的所有文档进行分组,使用
o.id
字段作为分组标识符,并使用总和作为每个元素的组值。对于每个分组的元素,count
值字段将增加一。
此脚本产生输出:
$ php af.php
{
"result": [
{
"_id": 12,
"count": 1
},
{
"_id": 63,
"count": 1
},
{
"_id": 97,
"count": 1
},
{
"_id": 64,
"count": 1
},
{
"_id": 36,
"count": 2
}
],
"ok": 1
}