sql >> Databasteknik >  >> NoSQL >> MongoDB

Hur man slår samman arrayfält i dokument i Mongo-aggregation

TLDR;

Moderna utgåvor bör använda $reduce med $setUnion efter den initiala $group som visas:

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 hade rätt när du hittade $addToSet operator, men när du arbetar med innehåll i en array behöver du vanligtvis bearbeta med $unwind först. Detta "avnormaliserar" matrisposterna och gör i huvudsak en "kopia" av det överordnade dokumentet med varje matrispost som ett singulär värde i fältet. Det är vad du behöver för att undvika beteendet du ser utan att använda det.

Din "räkning" utgör dock ett intressant problem, men det löses enkelt genom att använda en "dubbel avveckling" efter en första $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 sista biten med $project finns också där eftersom jag använde "tillfälliga" namn för vart och ett av fälten i andra stadier av aggregeringspipelinen. Detta beror på att det finns en optimering i $project som "kopierar" fälten från ett befintligt stadium i den ordning de redan dök upp "innan" några "nya" fält läggs till i dokumentet.

Annars skulle utdata se ut så här:

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

Där fälten inte är i samma ordning som man kan tro. Trivialt egentligen, men det är viktigt för vissa människor, så värt att förklara varför och hur man hanterar det.

$unwind gör jobbet för att hålla objekten åtskilda och inte i arrayer, och gör $group låter dig först få "räkningen" av förekomsterna av "gruppering"-nyckeln.

$first Operatören som senare används "behåller" det "räknevärde" eftersom det precis har "duplicerats" för varje värde som finns i "taggar"-matrisen. Allt har samma värde ändå så det spelar ingen roll. Välj bara en.




  1. Redis kö med anspråk löper ut

  2. Hur man lagrar och hämtar en ordbok med redis

  3. MongoDB grupp per timme

  4. Uppgradering till ClusterControl Enterprise Edition