MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Como mesclar o campo de matriz no documento na agregação do Mongo

TLDR;


As versões modernas devem usar $reduce com $setUnion após o $group inicial como é mostrado:
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" ] }
      }
    }
  }}
])

Você estava certo ao encontrar o $addToSet operador, mas ao trabalhar com conteúdo em uma matriz você geralmente precisa processar com $unwind primeiro. Isso "desnormaliza" as entradas da matriz e essencialmente faz uma "cópia" do documento pai com cada entrada da matriz como um valor singular no campo. Isso é o que você precisa para evitar o comportamento que você está vendo sem usar isso.

Sua "contagem" apresenta um problema interessante, mas facilmente resolvido através do uso de um "dupla desenrolamento" após um $group inicial Operação:
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"
    }}
])

Essa parte final com $project também está lá porque usei nomes "temporários" para cada um dos campos em outros estágios do pipeline de agregação. Isso ocorre porque há uma otimização em $project que "copia" os campos de um estágio existente na ordem em que já apareceram "antes" de quaisquer "novos" campos serem adicionados ao documento.

Caso contrário, a saída ficaria assim:
{  "count":2 , "tags":[ "tag1", "tag2", "tag3" ], "Host": "abc.com", "ArtId": "123" }

Onde os campos não estão na mesma ordem que você imagina. Trivial realmente, mas é importante para algumas pessoas, então vale a pena explicar o porquê e como lidar.

Então $unwind faz o trabalho de manter os itens separados e não em arrays, e fazendo o $group primeiro permite que você obtenha a "contagem" das ocorrências da chave "agrupamento".

O $first O operador usado posteriormente "mantém" esse valor de "contagem", pois acaba de ser "duplicado" para cada valor presente na matriz "tags". É tudo o mesmo valor de qualquer maneira, então não importa. Basta escolher um.