sql >> Database >  >> NoSQL >> MongoDB

Garandeert de $in-clausule van MongoDB?

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.



  1. MongoDB-aggregatie met $ lookup omvat (of projecteert) alleen enkele velden die moeten worden geretourneerd uit de query

  2. Meteor:onverwachte mongo-exitcode 100

  3. StackExchange.Redis ConnectionMultiplexer-pool voor synchrone methoden

  4. Moet `StackExchange.Redis.ConnectionMultiplexer` `AddSingleton` of `AddScope` zijn in .NET Core-afhankelijkheidsinjectie?