sql >> Database >  >> NoSQL >> MongoDB

sorteren op ingesloten objectwaarde in Mongodb

Als je zoiets tijdens runtime moet berekenen, waarbij "gefilterde" inhoud uit de array de sorteervolgorde bepaalt, dan kun je het beste iets doen met .aggregate() om een ​​sorteerwaarde als volgt te hervormen en te bepalen:

db.collection.aggregate([
    // Pre-filter the array elements
    { "$project": {
        "tags": 1,
        "score": {
            "$setDifference": [
                { "$map": {
                    "input": "$tags",
                    "as": "tag",
                    "in": {
                        "$cond": [
                            { "$eq": [ "$$el.id", "t1" ] },
                            "$$el.score",
                            false
                        ]
                    }
                }},
                [false]
            ]
        }
    }},
    // Unwind to denormalize
    { "$unwind": "$score" },
    // Group back the "max" score
    { "$group": {
        "_id": "$_id",
        "tags": { "$first": "$tags" },
        "score": { "$max": "$score" }
    }},
    // Sort descending by score
    { "$sort": { "score": -1 } }
])

Waarbij het eerste deel van de pijplijn wordt gebruikt om de array-inhoud "voor te filteren" (en ook om het originele veld te behouden) tot alleen die waarden van "score" waarbij de id gelijk is aan "t1". Dit wordt gedaan door $map te verwerken die een voorwaarde toepast op elk element via $cond om te bepalen of de "score" voor dat element moet worden geretourneerd of false .

De $setDifference operatie maakt een vergelijking met een array met één element [false] die effectief elke false . verwijdert waarden geretourneerd uit de $map . Als een "set" verwijdert dit ook dubbele vermeldingen, maar voor het sorteerdoel hier is dit een goede zaak.

Met de array verkleind en omgevormd tot waarden die u verwerkt $unwind klaar voor de volgende fase om de waarden als afzonderlijke elementen te behandelen. De $group stage is in wezen van toepassing $max op de "score" om de hoogste waarde in de gefilterde resultaten te retourneren.

Dan is het gewoon een kwestie van het toepassen van de $sort op de vastgestelde waarde de documenten te bestellen. Natuurlijk, als je dit andersom wilde, gebruik dan $min en sorteer in plaats daarvan in oplopende volgorde.

Voeg natuurlijk een $match toe ga naar het begin als u alleen maar documenten wilt die daadwerkelijk "t1"-waarden bevatten voor id binnen de tags. Maar dat deel is het minst relevant voor de sortering op gefilterde resultaten die u wilt bereiken.

Het alternatief voor berekenen is om het allemaal te doen terwijl u items naar de array in de documenten schrijft. Een beetje rommelig, maar het gaat ongeveer zo:

db.collection.update(
    { "_id": docId },
    {
        "$push": { "tags": { "id": "t1", "score": 60 } },
        "$max": { "maxt1score": 60 },
        "$min": { "mint1score": 60 }
    }
)

Hier de $max update-operator stelt de waarde voor het opgegeven veld alleen in als de nieuwe waarde groter is dan de bestaande waarde of als er anders nog geen eigenschap bestaat. Het omgekeerde geldt voor $min , waarbij alleen indien minder dan deze wordt vervangen door de nieuwe waarde.

Dit zou natuurlijk tot gevolg hebben dat er verschillende extra eigenschappen aan de documenten worden toegevoegd, maar het eindresultaat is dat het sorteren sterk vereenvoudigd wordt:

db.collection.find().sort({ "maxt1score": -1 })

En het gaat een stuk sneller dan rekenen met een aggregatiepijplijn.

Denk dus aan de ontwerpprincipes. Gestructureerde gegevens in arrays waarin u gefilterde en gekoppelde resultaten wilt sorteren, betekent tijdens runtime berekenen op welke waarde moet worden gesorteerd. Extra eigenschappen toevoegen aan het document op .update() betekent dat u eenvoudig naar die eigenschappen kunt verwijzen om de resultaten direct te sorteren.



  1. mongod shell start niet, data/db bestaat niet

  2. MongoDB .Net driver 2.0 Pull (element verwijderen)

  3. Dagen toevoegen/aftrekken aan ISODate in MongoDB Shell

  4. MongoDB-voorvoegsel-jokerteken:fulltext-search ($ tekst) deel vinden met zoekstring