Както беше отбелязано, редът на аргументите в масива на клауза $in не отразява реда на това как се извличат документите. Това, разбира се, ще бъде естественият ред или по избрания индексен ред, както е показано.
Ако трябва да запазите този ред, тогава имате две възможности.
Така че да приемем, че сте съвпадали по стойностите на _id
във вашите документи с масив, който ще бъде предаден в $in
като [ 4, 2, 8 ]
.
Подход с помощта на Aggregate
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
Разбира се, ако всичко това изглежда доста тежко за вашата чувствителност, тогава можете да направите същото, като използвате 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
състояние, при което вече имате този списък в определен ред.