Zoals opgemerkt, komt de volgorde van de argumenten in de array van een $in-clausule niet overeen met de volgorde waarin de documenten worden opgehaald. Dat is natuurlijk de natuurlijke volgorde of de geselecteerde indexvolgorde zoals weergegeven.
Als u deze bestelling wilt behouden, heeft u in principe twee opties.
Dus laten we zeggen dat je overeenkwam op de waarden van _id
in uw documenten met een array die wordt doorgegeven aan de $in
als [ 4, 2, 8 ]
.
Aanpak met 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 } }
])
Dus dat zou de uitgebreide vorm zijn. Wat hier in feite gebeurt, is dat net zoals de reeks waarden wordt doorgegeven aan $in
je bouwt ook een "geneste" $cond
statement om de waarden te testen en een passend gewicht toe te kennen. Aangezien die "gewichtswaarde" de volgorde van de elementen in de array weerspiegelt, kunt u die waarde doorgeven aan een sorteerfase om uw resultaten in de vereiste volgorde te krijgen.
Natuurlijk "bouwt" u de pijplijninstructie in code, ongeveer als volgt:
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 );
Aanpak met mapReduce
Natuurlijk, als dat allemaal te zwaar lijkt voor uw gevoeligheden, dan kunt u hetzelfde doen met mapReduce, dat er eenvoudiger uitziet, maar waarschijnlijk wat langzamer zal werken.
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;
}
}
)
En dat is in feite afhankelijk van de uitgezonden "sleutel" -waarden in de "indexvolgorde" van hoe ze voorkomen in de invoerarray.
Dus dat zijn in wezen uw manieren om de volgorde van een invoerlijst naar een $in
. te behouden voorwaarde waar je die lijst al in een bepaalde volgorde hebt.