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

Sådan flettes matrixfelt i dokument i Mongo-aggregation

TLDR;

Moderne udgivelser bør bruge $reduce med $setUnion efter den indledende $group som vist:

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

Du havde ret i at finde $addToSet operatør, men når du arbejder med indhold i et array, skal du generelt behandle med $unwind først. Dette "denormaliserer" array-indgangene og laver i det væsentlige en "kopi" af det overordnede dokument med hver array-indgang som en enkelt værdi i feltet. Det er det, du skal bruge for at undgå den adfærd, du ser uden at bruge det.

Din "optælling" udgør dog et interessant problem, men det løses nemt ved at bruge en "dobbelt afvikling" efter en indledende $group operation:

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

Den sidste bit med $project er der også, fordi jeg brugte "midlertidige" navne for hvert af felterne i andre stadier af aggregeringspipelinen. Dette skyldes, at der er en optimering i $project at "kopierer" felterne fra et eksisterende trin i den rækkefølge, de allerede opstod "før" nogen "nye" felter tilføjes til dokumentet.

Ellers ville output se sådan ud:

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

Hvor felterne ikke er i samme rækkefølge, som du måske tror. Trivielt virkelig, men det betyder noget for nogle mennesker, så det er værd at forklare hvorfor, og hvordan man håndterer det.

$unwind gør arbejdet for at holde elementerne adskilt og ikke i arrays, og udfører $group giver dig først mulighed for at få "tællingen" af forekomsterne af "grupperings"-tasten.

$first operatør, der senere blev brugt, "beholder" denne "tælle"-værdi, da den lige er blevet "duplikeret" for hver værdi, der findes i "tags"-arrayet. Det er alligevel den samme værdi, så det er ligegyldigt. Bare vælg en.




  1. MongoCollection versus DBCollection java

  2. Sorteret sæt af fast størrelse i Redis?

  3. Hvordan aktiverer man godkendelse på MongoDB gennem Docker?

  4. Mongoose opdatering/upsert?