Conforme observado, a ordem dos argumentos na matriz de uma cláusula $in não reflete a ordem de como os documentos são recuperados. Isso, obviamente, será a ordem natural ou pela ordem do índice selecionado, conforme mostrado.
Se você precisar preservar essa ordem, terá basicamente duas opções.
Então, digamos que você estava combinando os valores de
_id
em seus documentos com um array que será passado para o $in
como [ 4, 2, 8 ]
. Abordagem usando o Agregado
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 } }
])
Então essa seria a forma expandida. O que basicamente acontece aqui é que assim como o array de valores é passado para
$in
você também constrói um $cond
"aninhado" para testar os valores e atribuir um peso apropriado. Como esse valor de "peso" reflete a ordem dos elementos na matriz, você pode passar esse valor para um estágio de classificação para obter os resultados na ordem necessária. É claro que você realmente "cria" a instrução do pipeline no código, mais ou menos assim:
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 );
Aborde usando mapReduce
É claro que se tudo isso parece pesado para sua sensibilidade, você pode fazer a mesma coisa usando mapReduce, que parece mais simples, mas provavelmente funcionará um pouco mais devagar.
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;
}
}
)
E isso basicamente depende dos valores de "chave" emitidos estarem na "ordem de índice" de como eles ocorrem na matriz de entrada.
Então, essas são essencialmente suas maneiras de manter a ordem de uma lista de entrada para um
$in
condição onde você já tem essa lista em uma determinada ordem.