MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Gruppieren von Dokumenten in MongoDB unter besonderen Bedingungen

Haftungsausschluss

Bevor Sie den Rest der Antwort lesen, lesen Sie bitte https://docs. mongodb.com/manual/core/aggregation-pipeline-limits/ Es wird erwartet, dass das resultierende Dokument in der Frage ein Array aller Dokumente enthält, die zu einer bestimmten Altersgruppe gehören.Die Größe dieses Arrays darf 16 MB nicht überschreiten , daher funktioniert der folgende Code nur für sehr kleine Sammlungen winziger Dokumente.

Der Code:

db.collection.aggregate([
    { $sort: { age: 1 } },
    { $group: {
            _id: null,
            ages: { $push: "$age" }
    } },
    { $addFields: {
        ranges: { $reduce: { 
            input: { $range: [ 1, { $size: "$ages" }, 1 ] }, 
            initialValue: [ [ { $arrayElemAt: [ "$ages", 0 ] } ] ], 
            in: { $cond: { 
                if:  { $gt: [
                    { $subtract: [ { $arrayElemAt: [ "$ages", "$$this" ] }, { $arrayElemAt: [ "$ages", { $subtract: [ "$$this", 1 ] } ] } ] },
                    2
                    ] }, 
                then: { $concatArrays: [ "$$value",  [ [ { $arrayElemAt: [ "$ages", "$$this" ] } ] ] ] }, 
                else: { $concatArrays: [ 
                    { $slice: [ "$$value" , { $subtract: [ { $size: "$$value" }, 1 ] } ] },
                    [ { $concatArrays: [ 
                        { $arrayElemAt: [ { $slice: [ "$$value" , -1 ] }, 0 ] }  ,  
                        [ { $arrayElemAt: [ "$ages", "$$this" ] } ]
                    ]  } ]
                ] }
            } }
        } } 
    } },
    { $unwind: "$ranges" }, 
    { $lookup: {
       from: "collection",
       localField: "ranges",
       foreignField: "age",
       as: "group"
     } },
     { $project: { _id: 0, group: 1 } }
])

Der Teil, der vielleicht etwas erklärt werden muss, ist die Berechnung der Altersgruppen.

Dafür erhalten wir alle Altersgruppen mit $group in ein einzelnes Array und dann $addFields "Bereiche" - ein 2D-Array von Altersgruppen mit Lücken zwischen der ältesten Person in einer jüngeren Gruppe und einer jüngsten Person in der älteren Gruppe von mehr als 2 Jahren.

Das Array wird mit $reduce berechnet eines $range Array von Indizes aller Altersgruppen außer dem ersten, der auf den Anfangswert geht.

Der Reduce-Ausdruck ist ein $cond was die Differenz zwischen aktuell und vorher berechnet ($subtract ) Element des Arrays aller Altersgruppen.

Wenn es größer als 2 ist, wird eine neue Altersgruppe mit $concatArrays . Andernfalls wird das Alter mithilfe von $slice um zur letzten Gruppe im Ranges-Array zu pushen und $setUnion um Duplikate zu eliminieren.

Wenn die Altersgruppen berechnet werden, $lookup dieselbe Sammlung nach Alter, um sie im "Gruppen"-Array zu gruppieren.