sql >> Database >  >> NoSQL >> MongoDB

Hoe een matrixveld in document samen te voegen in Mongo-aggregatie

TLDR;

Moderne releases moeten $reduce . gebruiken met $setUnion na de eerste $group zoals wordt getoond:

db.collection.aggregate([
  { "$group": {
    "_id": { "Host": "$Host", "ArtId": "$ArtId" },
    "count": { "$sum": 1 },
    "tags": { "$addToSet": "$tags" }
  }},
  { "$addFields": {
    "tags": {
      "$reduce": {
        "input": "$tags",
        "initialValue": [],
        "in": { "$setUnion": [ "$$value", "$$this" ] }
      }
    }
  }}
])

Je had gelijk toen je de $addToSet . vond operator, maar wanneer u met inhoud in een array werkt, moet u over het algemeen verwerken met $unwind eerst. Dit "de-normaliseert" de array-ingangen en maakt in wezen een "kopie" van het bovenliggende document met elke array-ingang als een enkelvoudige waarde in het veld. Dat is wat je nodig hebt om het gedrag te vermijden dat je ziet zonder dat te gebruiken.

Uw "telling" vormt echter een interessant probleem, maar gemakkelijk op te lossen door het gebruik van een "dubbele afwikkeling" na een initiële $group bediening:

db.collection.aggregate([
    // Group on the compound key and get the occurrences first
    { "$group": {
        "_id": { "Host": "$Host", "ArtId": "$ArtId" },
        "tcount": { "$sum": 1 },
        "ttags": { "$push": "$tags" }
    }},

    // Unwind twice because "ttags" is now an array of arrays
    { "$unwind": "$ttags" },
    { "$unwind": "$ttags" },

    // Now use $addToSet to get the distinct values        
    { "$group": {
        "_id": "$_id",
        "tcount": { "$first": "$tcount" },
        "tags": { "$addToSet": "$ttags" }
    }},

    // Optionally $project to get the fields out of the _id key
    { "$project": {
        "_id": 0,
        "Host": "$_id.Host",
        "ArtId": "$_id.ArtId",
        "count": "$tcount",
        "tags": "$ttags"
    }}
])

Dat laatste stukje met $project is er ook omdat ik "tijdelijke" namen heb gebruikt voor elk van de velden in andere stadia van de aggregatiepijplijn. Dit komt omdat er een optimalisatie is in $project die de velden uit een bestaande fase "kopieert" in de volgorde waarin ze al verschenen "voordat" eventuele "nieuwe" velden aan het document werden toegevoegd.

Anders zou de uitvoer er als volgt uitzien:

{  "count":2 , "tags":[ "tag1", "tag2", "tag3" ], "Host": "abc.com", "ArtId": "123" }

Waar de velden niet in dezelfde volgorde staan ​​als je zou denken. Triviaal eigenlijk, maar voor sommige mensen is het belangrijk, dus het is de moeite waard om uit te leggen waarom en hoe ermee om te gaan.

Dus $unwind doet het werk om de items gescheiden te houden en niet in arrays, en doet de $group eerst kunt u het "aantal" van de voorkomens van de "groepering" -sleutel ophalen.

De $first operator die later werd gebruikt, "houdt" die "tel"-waarde, omdat deze zojuist is "gedupliceerd" voor elke waarde die aanwezig is in de "tags" -array. Het is toch allemaal dezelfde waarde, dus het maakt niet uit. Kies er gewoon een.




  1. Voorwaarden voor meerdere deelname met behulp van de $lookup-operator

  2. Verificatiefout tijdens het opslaan naar mongodb

  3. Toepassingen implementeren op CDP Operational Database (COD)

  4. Hoe alle hosts toe te staan ​​tot een replicaset in mongodb