sql >> Databasteknik >  >> NoSQL >> MongoDB

Gör MongoDB:s $in-klausul beställning

Som nämnts återspeglar inte ordningen på argumenten i arrayen av en $in-sats ordningen för hur dokumenten hämtas. Det kommer naturligtvis att vara den naturliga ordningen eller efter den valda indexordningen som visas.

Om du behöver behålla den här ordningen har du i princip två alternativ.

Så låt oss säga att du matchade värdena för _id i dina dokument med en array som kommer att skickas in till $in som [ 4, 2, 8 ] .

Tillvägagångssätt med 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 } }

])

Så det skulle vara den utökade formen. Vad som i princip händer här är att precis som arrayen av värden skickas till $in du konstruerar också en "kapslad" $cond uttalande för att testa värdena och tilldela en lämplig vikt. Eftersom det "vikt"-värdet återspeglar ordningen på elementen i arrayen, kan du sedan skicka det värdet till ett sorteringssteg för att få dina resultat i önskad ordning.

Naturligtvis "bygger" du faktiskt pipeline-satsen i kod, ungefär så här:

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

Tillvägagångssätt med mapReduce

Naturligtvis om allt verkar tungt för dina känslor kan du göra samma sak med mapReduce, som ser enklare ut men kommer troligen att gå något långsammare.

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

Och det förlitar sig i princip på att de utsända "nyckel"-värdena är i "indexordningen" av hur de uppträder i inmatningsmatrisen.

Så det är i huvudsak dina sätt att bibehålla ordningen för en inmatningslista till en $in skick där du redan har den listan i en bestämd ordning.



  1. Faye eller Redis Pubsub

  2. Node - Mongoose 3.6 - Sortera fråga med ifyllt fält

  3. Hur man fångar redis.serializer.SerializationException

  4. Mongoid / Mongodb och frågar efter inbäddade dokument