sql >> Base de Datos >  >> NoSQL >> MongoDB

Cómo fusionar un campo de matriz en un documento en la agregación de Mongo

TLDR;

Las versiones modernas deben usar $reduce con $setUnion después del $group inicial como se muestra:

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

Tenías razón al encontrar el $addToSet operador, pero cuando trabaja con contenido en una matriz, generalmente necesita procesar con $unwind primero. Esto "desnormaliza" las entradas de la matriz y esencialmente hace una "copia" del documento principal con cada entrada de la matriz como un valor singular en el campo. Eso es lo que necesita para evitar el comportamiento que está viendo sin usar eso.

Sin embargo, su "recuento" plantea un problema interesante, pero se resuelve fácilmente mediante el uso de un "desenrollamiento doble" después de un $group inicial operación:

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

Esa parte final con $project también está ahí porque usé nombres "temporales" para cada uno de los campos en otras etapas de la canalización de agregación. Esto se debe a que hay una optimización en $project que "copia" los campos de una etapa existente en el orden en que ya aparecían "antes" de agregar cualquier campo "nuevo" al documento.

De lo contrario, la salida se vería así:

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

Donde los campos no están en el mismo orden que podrías pensar. Trivial realmente, pero es importante para algunas personas, por lo que vale la pena explicar por qué y cómo manejarlo.

Así que $unwind hace el trabajo de mantener los elementos separados y no en matrices, y hace el $group primero le permite obtener el "recuento" de las apariciones de la clave de "agrupación".

El $first El operador utilizado más tarde "mantiene" ese valor de "recuento", ya que simplemente se "duplicó" para cada valor presente en la matriz de "etiquetas". Es todo el mismo valor de todos modos, así que no importa. Solo elige uno.