Comme indiqué, l'ordre des arguments dans le tableau d'une clause $in ne reflète pas l'ordre dans lequel les documents sont récupérés. Ce sera bien sûr l'ordre naturel ou l'ordre de l'index sélectionné, comme indiqué.
Si vous devez conserver cette commande, vous avez essentiellement deux options.
Alors disons que vous correspondiez sur les valeurs de _id
dans vos documents avec un tableau qui va être transmis au $in
comme [ 4, 2, 8 ]
.
Approche utilisant l'agrégation
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 } }
])
Ce serait donc la forme développée. Ce qui se passe essentiellement ici, c'est que tout comme le tableau de valeurs est passé à $in
vous construisez également un $cond
"imbriqué" pour tester les valeurs et attribuer un poids approprié. Comme cette valeur de "poids" reflète l'ordre des éléments dans le tableau, vous pouvez ensuite passer cette valeur à une étape de tri afin d'obtenir vos résultats dans l'ordre requis.
Bien sûr, vous "construisez" réellement l'instruction de pipeline dans le code, un peu comme ceci :
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 );
Approche utilisant mapReduce
Bien sûr, si tout cela semble lourd pour votre sensibilité, vous pouvez faire la même chose en utilisant mapReduce, qui semble plus simple mais qui fonctionnera probablement un peu plus lentement.
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;
}
}
)
Et cela repose essentiellement sur le fait que les valeurs de "clé" émises sont dans "l'ordre d'index" de la façon dont elles se produisent dans le tableau d'entrée.
Donc, ce sont essentiellement vos façons de maintenir l'ordre d'une liste d'entrée dans un $in
condition où vous avez déjà cette liste dans un ordre déterminé.