好的,那么情况是我有一个产品类别的集合,每个文档类似于:
Category {
_id => ...,
name => ...,
products => array(
0 => new MongoID(PID...1),
1 => new MongoID(PID...2),
2 => new MongoID(PID...3),
....
)
}
和product集合:
Product {
_id => ...,
name => ...,
active => true,
status => 'published',
categories => array(
0 => new MongoID(CATID...1),
1 => new MongoID(CATID...2),
2 => new MongoID(CATID...3)
)
}
我正在重构我的代码,因为在此刻,因为当我做任何限制/抵消,我必须返回一个类别中的所有产品作为对象,然后取消设置,如果他们不是活动的(有几个更多的内容匹配,但我留下他们简短)。显然,我们在这个阶段还远没有达到最佳状态。
我已经建立了一个条件查询,看起来像这样:array(
'$and' => array(
array(
'$or' => array(
0 => array(
'_id' => new MongoID(PID...1)
),
1 => array(
'_id' => new MongoID(PID...2)
),
2 => array(
'_id' => new MongoID(PID...3)
),
...
)
),
array(
'$and' => array(
array(
'active' => true
),
array(
'status' => 'published'
)
)
)
)
)
我需要做的是尊重查询的第一部分中_id的顺序,并根据哪些文档匹配_id的序列中的第二部分来偏移/限制。
查询最终以find()命令结束,但设置排序顺序显然不起作用,因为我们没有排序。
缺少从Product中拆分一些字段并添加到Category->products[],我正在努力寻找一种方法来做到这一点。现在是简单地使用MapReduce/aggregation来处理它,还是有更简单的替代方案?
谢谢!
如果您需要以这种方式维持"顺序",那么您最好的选择是为您所选择的项目分配一个"权重"。这给了你排序的东西。
您可以使用.aggregate()
来做到这一点,您也可以使用mapReuce来做同样的事情,但是"聚合"方式应该运行得更快。而且你这里的语法似乎有点不对劲。而不是在同一字段中使用$or
,您可能希望使用$in
。
一般JavaScript/JSON格式:
var idArray = [ 5, 2, 8 ];
db.collection.aggregate([
// Match the selected documents by "_id"
{ "$match": {
"_id": { "$in": [ 5, 2, 8 ] },
"active": true,
"status": "published"
},
// Project a "weight" to each document
{ "$project": {
"name": 1,
"active": 1,
"status": 1,
"weight": { "$cond": [
{ "$eq": [ "_id", 5 ] },
1,
{ "$cond": [
{ "$eq": [ "_id", 2 ] },
2,
3
]}
]}
}},
// Sort the results
{ "$sort": { "weight": 1 } }
])
因此,我确实"展开"了数组的目的是为了阅读这一点,但你的实际代码,你只是想引用数组的$in
子句。
$cond
的嵌套使用评估逻辑条件以匹配_id
字段的"值",并分配"权重",如增加数字。它维持输入数组中值的顺序。
实际上,您可以在代码中这样做,以"生成"管道部分,特别是嵌套条件,这些条件与这里所看到的内容一致。这个例子使用了"特定"权重,但大部分原则是相同的。
但是将允许您保持输入数组的顺序
PHP代码
例如,像这样生成所需的管道:
$list = array( 5, 2, 8 );
$stack = array();
for( $i = count($list)-1; $i > 0; $i-- ) {
$rec = array(
'$cond' => array(
array( '$eq' =>
array( '$_id', $list[$i-1] )
),
$i
)
);
if ( count($stack) == 0 ) {
$rec['$cond'][] = $i+1;
} else {
$last = array_pop($stack);
$rec['$cond'][] = $last;
}
$stack[] = $rec;
}
$pipeline = array(
array(
'$match' => array(
'_id' => array( '$in' => $list ),
'active' => true,
'status' => "published"
)
),
array(
'$project' => array(
'name' => 1,
'active' => 1,
'status' => 1,
'weight' => $stack[0]
)
),
array(
'$sort' => array( 'weight' => 1 )
)
);
echo json_encode( $pipeline, JSON_PRETTY_PRINT ) . "'n";
当然,在现实中您不会"编码"到JSON,这只是为了显示形成的结构。
地图减少
为了说明你也可以用mapReduce
做同样的事情。首先设置映射器
var mapper = function () {
var order = inputs.indexOf(this._id);
emit( order, { doc: this } );
}
也许还有一个finalize函数来清理,"a little":
var finalize = function (key, value) {
return value.doc;
}
运行mapReduce
。不需要减速器:
db.test.mapReduce(
mapper,
function(){},
{
"out": { "inline": 1 },
"query": { "_id": { "$in": idArray } },
"scope": { "inputs": idArray } ,
"finalize": finalize
}
)
看起来更干净,但是可能不会运行得那么快,并且对结果有一个非常"mapReduce"类型的输出。