使用 MongoDB 的 $in
时子句,返回文档的顺序是否总是与数组参数的顺序相对应?
最佳答案
如前所述,$in 子句数组中参数的顺序并不反射(reflect)文档检索的顺序。这当然是自然顺序或如图所示的所选索引顺序。
如果你需要保留这个顺序,那么你基本上有两种选择。
假设您在文档中将 _id
的值与将作为 传递给
.$in
的数组进行匹配[ 4, 2, 8 ]
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
语句来测试值并分配适当的权重.由于该“权重”值反射(reflect)了数组中元素的顺序,因此您可以将该值传递给排序阶段,以便按所需顺序获得结果。
当然,您实际上是在代码中“构建”管道语句,就像这样:
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
条件的方法,在该条件下,您已经以确定的顺序拥有该列表。
https://stackoverflow.com/questions/22797768/