sql >> Base de Datos >  >> NoSQL >> MongoDB

¿El orden de garantía de la cláusula $in de MongoDB?

Como se indicó, el orden de los argumentos en la matriz de una cláusula $in no refleja el orden en que se recuperan los documentos. Eso, por supuesto, será el orden natural o por el orden de índice seleccionado como se muestra.

Si necesita conservar este orden, básicamente tiene dos opciones.

Así que digamos que estabas haciendo coincidir los valores de _id en sus documentos con una matriz que se pasará a $in como [ 4, 2, 8 ] .

Enfoque usando 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 } }

])

Así que esa sería la forma expandida. Lo que básicamente sucede aquí es que al igual que la matriz de valores se pasa a $in también construyes un $cond "anidado" declaración para probar los valores y asignar un peso apropiado. Como ese valor de "peso" refleja el orden de los elementos en la matriz, puede pasar ese valor a una etapa de clasificación para obtener los resultados en el orden requerido.

Por supuesto, en realidad "construye" la declaración de canalización en código, de forma muy parecida a esto:

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 );

Enfoque usando mapReduce

Por supuesto, si todo parece demasiado pesado para su sensibilidad, entonces puede hacer lo mismo usando mapReduce, que parece más simple pero probablemente funcionará un poco más lento.

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;
        }
    }
)

Y eso básicamente depende de que los valores "clave" emitidos estén en el "orden de índice" de cómo ocurren en la matriz de entrada.

Entonces, esas son esencialmente sus formas de mantener el orden de una lista de entrada en un $in condición donde ya tienes esa lista en un orden determinado.