如前所述,$in子句数组中参数的顺序并不反映检索文档的顺序.当然,这将是自然顺序或所选索引顺序,如图所示.
如果你需要保持这种秩序,那么你基本上有两个 Select .
假设您将文档中的值_id
与一个数组进行匹配,该数组将作为[ 4, 2, 8 ]
传递给$in
.
var list = [ 4, 2, 8 ];
db.collection.aggregate([
// Match the selected documents by "_id"
{ "$match": {
"_id": { "$in": [ 4, 2, 8 ] },
},
// Project a "weight" to each document
{ "$project": {
"weight": { "$cond": [
{ "$eq": [ "$_id", 4 ] },
1,
{ "$cond": [
{ "$eq": [ "$_id", 2 ] },
2,
3
]}
]}
}},
// Sort the results
{ "$sort": { "weight": 1 } }
])
这就是扩展形式.这里基本上发生的事情是,正如将值数组传递给$in
一样,您还可以构造一个"嵌套"$cond
语句来测试值并分配适当的权重.由于该"权重"值反映了数组中元素的顺序,因此可以将该值传递给排序阶段,以获得所需顺序的结果.
当然,您实际上是在代码中"构建"管道语句,就像这样:
var list = [ 4, 2, 8 ];
var stack = [];
for (var i = list.length - 1; i > 0; i--) {
var rec = {
"$cond": [
{ "$eq": [ "$_id", list[i-1] ] },
i
]
};
if ( stack.length == 0 ) {
rec["$cond"].push( i+1 );
} else {
var lval = stack.pop();
rec["$cond"].push( lval );
}
stack.push( rec );
}
var pipeline = [
{ "$match": { "_id": { "$in": list } }},
{ "$project": { "weight": stack[0] }},
{ "$sort": { "weight": 1 } }
];
db.collection.aggregate( pipeline );
当然,如果这一切似乎对你的敏感度有很大影响,那么你可以使用mapReduce做同样的事情,它看起来更简单,但运行速度可能会慢一些.
var list = [ 4, 2, 8 ];
db.collection.mapReduce(
function () {
var order = inputs.indexOf(this._id);
emit( order, { doc: this } );
},
function() {},
{
"out": { "inline": 1 },
"query": { "_id": { "$in": list } },
"scope": { "inputs": list } ,
"finalize": function (key, value) {
return value.doc;
}
}
)
这基本上取决于发出的"键"值在输入数组中的"索引顺序".
因此,这些基本上就是将输入列表的顺序保持为$in
的方法,在这种情况下,您已经以确定的顺序拥有了该列表.