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

sorter efter indlejret objektværdi i Mongodb

Hvis du har brug for at udregne noget som dette ved kørsel, med "filtreret" indhold fra arrayet, der bestemmer sorteringsrækkefølgen, så gør du bedst noget med .aggregate() for at omforme og bestemme en sorteringsværdi som denne:

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

Hvor den første del af pipelinen bruges til at "forfiltrere" array-indholdet (såvel som at beholde det originale felt) til kun de værdier af "score", hvor id'et er lig med "t1". Dette gøres ved at behandle $map som anvender en betingelse for hvert element via $cond for at bestemme, om "score" for det pågældende element skal returneres eller false .

$setDifference operation foretager en sammenligning med et enkelt element array [false] som effektivt fjerner enhver false værdier returneret fra $map . Som et "sæt" fjerner dette også duplikerede poster, men til sorteringsformålet her er dette en god ting.

Med arrayet reduceret og omformet til værdier behandler du $unwind klar til næste trin for at håndtere værdierne som individuelle elementer. $group fase gælder i det væsentlige $max på "score" for at returnere den højeste værdi indeholdt i de filtrerede resultater.

Så er det bare et spørgsmål om at anvende $sort på den fastsatte værdi for at bestille dokumenterne. Hvis du ville have det omvendt, så brug $min og sorter i stigende rækkefølge i stedet.

Tilføj selvfølgelig en $match trin til begyndelsen, hvis alt du virkelig ønsker er dokumenter, der faktisk indeholder "t1"-værdier for id inden for tags. Men den del er mindst relevant for sorteringen på filtrerede resultater, du ønsker at opnå.

Alternativet til at beregne er at gøre det hele, mens du skriver indgange til arrayet i dokumenterne. Lidt rodet, men det lyder sådan her:

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

Her er $max opdateringsoperatøren indstiller kun værdien for det angivne felt, hvis den nye værdi er større end den eksisterende værdi, eller hvis der på anden måde ikke findes nogen egenskab endnu. Det omvendte tilfælde gælder for $min , hvor kun hvis mindre end det vil blive erstattet med den nye værdi.

Dette ville naturligvis have den effekt, at der tilføjes forskellige yderligere egenskaber til dokumenterne, men slutresultatet er, at sorteringen er meget forenklet:

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

Og det kommer til at køre meget hurtigere end at beregne med en aggregeringspipeline.

Så overvej designprincipperne. Strukturerede data i arrays, hvor du ønsker filtrerede og parrede resultater til sortering, betyder, at du beregner ved kørsel for at bestemme, hvilken værdi der skal sorteres på. Tilføjelse af yderligere egenskaber til dokumentet på .update() betyder, at du blot kan referere til disse egenskaber for direkte at sortere resultater.



  1. Sådan foretrækker du læsninger på sekundære i MongoDb

  2. Har node.js server brug for internetforbindelse for at køre?

  3. Fejl ved kørsel af mongo-image - docker-entrypoint.sh:linje 381

  4. blpop stopper med at behandle køen efter et stykke tid