MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Comment fusionner un champ de tableau dans un document dans l'agrégation Mongo

TLDR ;

Les versions modernes doivent utiliser $reduce avec $setUnion après le $group initial comme indiqué :

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

Vous aviez raison de trouver le $addToSet , mais lorsque vous travaillez avec du contenu dans un tableau, vous devez généralement traiter avec $unwind première. Cela "dénormalise" les entrées du tableau et fait essentiellement une "copie" du document parent avec chaque entrée du tableau comme une valeur singulière dans le champ. C'est ce dont vous avez besoin pour éviter le comportement que vous voyez sans l'utiliser.

Votre "compte" pose cependant un problème intéressant, mais facilement résolu par l'utilisation d'un "double déroulement" après un premier $group opération :

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

Ce dernier morceau avec $project est également là parce que j'ai utilisé des noms "temporaires" pour chacun des champs dans d'autres étapes du pipeline d'agrégation. C'est parce qu'il y a une optimisation dans $project qui "copie" les champs d'une étape existante dans l'ordre dans lequel ils apparaissaient déjà "avant" que de "nouveaux" champs ne soient ajoutés au document.

Sinon, la sortie ressemblerait à :

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

Où les champs ne sont pas dans le même ordre qu'on pourrait le penser. Insignifiant vraiment, mais cela compte pour certaines personnes, donc cela vaut la peine d'expliquer pourquoi et comment gérer.

Alors $unwind fait le travail pour garder les éléments séparés et non dans des tableaux, et fait le $group vous permet d'abord d'obtenir le "compte" des occurrences de la clé de "groupement".

Le $first L'opérateur utilisé plus tard "conserve" cette valeur "count", car il vient d'être "dupliqué" pour chaque valeur présente dans le tableau "tags". C'est tout de même la même valeur de toute façon donc ce n'est pas grave. Choisissez-en un.