sql >> Databasteknik >  >> NoSQL >> MongoDB

sortera efter inbäddat objektvärde i Mongodb

Om du behöver räkna ut något sånt här vid körning, med "filtrerat" innehåll från arrayen som bestämmer sorteringsordningen, gör du bäst med .aggregate() för att omforma och bestämma ett sorteringsvärde så här:

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

Där den första delen av pipelinen används för att "förfiltrera" arrayinnehållet (liksom att behålla det ursprungliga fältet) till bara de värden för "score" där id är lika med "t1". Detta görs genom att bearbeta $map som tillämpar ett villkor för varje element via $cond för att avgöra om "poängen" för det elementet ska returneras eller false .

$setDifference operation gör en jämförelse med en enstaka elementarray [false] som effektivt tar bort alla false värden som returneras från $map . Som en "uppsättning" tar detta också bort dubbletter av poster, men för sorteringsändamålet här är det bra.

Med arrayen reducerad och omformad till värden bearbetar du $unwind redo för nästa steg för att hantera värdena som individuella element. $group stadiet gäller i huvudsak $max på "poängen" för att returnera det högsta värdet som finns i de filtrerade resultaten.

Sedan är det bara att använda $sort på det fastställda värdet för att beställa dokumenten. Naturligtvis om du ville ha det tvärtom, använd $min och sortera i stigande ordning istället.

Naturligtvis lägga till en $match steg till början om allt du verkligen vill ha är dokument som faktiskt innehåller "t1"-värden för id inom taggarna. Men den delen är minst relevant för sorteringen på filtrerade resultat du vill uppnå.

Alternativet till att beräkna är att göra allt medan du skriver poster till arrayen i dokumenten. Lite rörigt, men det går ungefär så här:

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

Här är $max uppdateringsoperatören ställer bara in värdet för det angivna fältet om det nya värdet är större än det befintliga värdet eller annars ingen egenskap ännu existerar. Det omvända fallet är sant för $min , där endast om mindre än det kommer att ersättas med det nya värdet.

Detta skulle naturligtvis ha effekten av att lägga till olika ytterligare egenskaper till dokumenten, men slutresultatet är att sorteringen förenklas avsevärt:

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

Och det kommer att gå mycket snabbare än att beräkna med en aggregeringspipeline.

Så överväg designprinciperna. Strukturerad data i arrayer där du vill ha filtrerade och parade resultat för sortering innebär att man beräknar vid körning för att bestämma vilket värde som ska sorteras på. Lägga till ytterligare egenskaper till dokumentet på .update() innebär att du helt enkelt kan referera till dessa egenskaper för att direkt sortera resultat.



  1. Hur kan jag använda MongoDB i Kohana?

  2. Lagra nycklar med prefix som upphör att gälla på redis

  3. Styre:Åtkomst har nekats att lösa egendomen från eftersom det inte är en egen egendom för sin förälder

  4. Hämta n:te elementet i en array i MongoDB