好的,所以案例场景是我有一个产品类别的集合,每个文档类似于:
Category {
_id => ...,
name => ...,
products => array(
0 => new MongoID(PID...1),
1 => new MongoID(PID...2),
2 => new MongoID(PID...3),
....
)
}
以及一系列产品:
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序列中的第二部分匹配。< / p>
查询最终以find()命令结束,但设置排序顺序显然不会起作用,因为我们没有排序。
如果没有从产品中拆分某些字段并添加到Category-&gt;产品[],我很难找到一种方法来做到这一点。它只是现在用MapReduce / aggregation处理它的情况还是有更简单的替代方案?
谢谢!
答案 0 :(得分:2)
如果您需要以这种方式维护“订单”,那么您最好的选择是为您选择的项目指定“权重”。这给你排序的东西。
您可以使用.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
字段的“值”,并在增加数字中指定“权重”。这样可以保持输入数组中值的顺序。
实际上,您可以在代码中执行此操作以“生成”管道部件,尤其是嵌套条件,其中包含与here一致的内容。该示例使用“特定”权重,但大部分原则都是相同的。
但将允许您维护输入数组的顺序
例如,像这样生成所需的管道:
$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 } );
}
也许是一个最终化的功能来清理,“一点点”:
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”类型的输出。