MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Fa l'ordine di garanzia della clausola $in di MongoDB

Come notato, l'ordine degli argomenti nell'array di una clausola $in non riflette l'ordine di recupero dei documenti. Quello ovviamente sarà l'ordine naturale o dall'ordine dell'indice selezionato come mostrato.

Se devi preservare questo ordine, hai sostanzialmente due opzioni.

Diciamo quindi che stavi abbinando i valori di _id nei tuoi documenti con un array che verrà passato a $in come [ 4, 2, 8 ] .

Approccio utilizzando 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 } }

])

Quindi quella sarebbe la forma espansa. Ciò che fondamentalmente accade qui è che proprio come l'array di valori viene passato a $in costruisci anche un $cond "annidato". dichiarazione per testare i valori e assegnare un peso appropriato. Poiché quel valore di "peso" riflette l'ordine degli elementi nell'array, puoi quindi passare quel valore a una fase di ordinamento per ottenere i risultati nell'ordine richiesto.

Ovviamente in realtà "costruisci" l'istruzione pipeline nel codice, proprio come questo:

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

Avvicinati usando mapReduce

Ovviamente se tutto ciò sembra pesante per la tua sensibilità, puoi fare la stessa cosa usando mapReduce, che sembra più semplice ma probabilmente funzionerà un po' più lentamente.

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 ciò si basa fondamentalmente sul fatto che i valori della "chiave" emessi siano nell'"ordine dell'indice" di come si verificano nell'array di input.

Quindi questi sono essenzialmente i tuoi modi per mantenere l'ordine di un elenco di input su un $in condizione in cui hai già quell'elenco in un determinato ordine.