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

So führen Sie Array-Felder in einem Dokument in der Mongo-Aggregation zusammen

TLDR;

Moderne Versionen sollten $reduce verwenden mit $setUnion nach dem anfänglichen $group wie gezeigt:

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

Sie hatten Recht, als Sie $addToSet gefunden haben -Operator, aber wenn Sie mit Inhalten in einem Array arbeiten, müssen Sie im Allgemeinen mit $unwind verarbeiten Erste. Dadurch werden die Array-Einträge "denormalisiert" und im Wesentlichen eine "Kopie" des übergeordneten Dokuments mit jedem Array-Eintrag als Einzelwert im Feld erstellt. Das ist es, was Sie brauchen, um das Verhalten zu vermeiden, das Sie sehen, ohne es zu verwenden.

Ihre "Zählung" wirft jedoch ein interessantes Problem auf, das jedoch leicht durch die Verwendung einer "doppelten Abwicklung" nach einer anfänglichen $group gelöst werden kann Betrieb:

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

Das letzte Stückchen mit $project ist auch da, weil ich "vorübergehende" Namen für jedes der Felder in anderen Phasen der Aggregationspipeline verwendet habe. Dies liegt daran, dass es eine Optimierung in $project gibt das die Felder aus einer bestehenden Stufe in der Reihenfolge "kopiert", in der sie bereits erschienen sind, "bevor" irgendwelche "neuen" Felder zum Dokument hinzugefügt werden.

Andernfalls würde die Ausgabe so aussehen:

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

Wo die Felder nicht in der gleichen Reihenfolge sind, wie Sie vielleicht denken. Eigentlich trivial, aber für manche Leute ist es wichtig, also lohnt es sich zu erklären, warum und wie man damit umgeht.

Also $unwind erledigt die Arbeit, um die Elemente getrennt und nicht in Arrays zu halten, und erledigt die $group ermöglicht Ihnen zunächst, die "Anzahl" der Vorkommen des "Gruppierungs"-Schlüssels zu erhalten.

Der $first Der später verwendete Operator "behält" diesen "count"-Wert, da er einfach für jeden im "tags"-Array vorhandenen Wert "dupliziert" wurde. Es ist sowieso alles derselbe Wert, also spielt es keine Rolle. Wählen Sie einfach eine aus.