sql >> Databasteknik >  >> NoSQL >> MongoDB

Koncernsträng efter grupp

Du kan göra det med aggregeringsramverket som en "tvåstegs" operation. Vilket är att först samla objekten till en array via $push inom en $group pipeline och sedan för att använda $concat med $reduce på den producerade arrayen i slutprojektion:

db.collection.aggregate([
  { "$group": {
    "_id": "$tag_id",
    "client_id": { "$push": "$client_id" }
  }},
  { "$addFields": {
    "client_id": {
      "$reduce": {
        "input": "$client_id",
        "initialValue": "",
        "in": {
          "$cond": {
            "if": { "$eq": [ "$$value", "" ] },
            "then": "$$this",
            "else": {
              "$concat": ["$$value", ",", "$$this"]
            }
          }
        }
      }
    }
  }}
])

Vi tillämpar även $cond här för att undvika att sammanfoga en tom sträng med ett kommatecken i resultaten, så det ser mer ut som en avgränsad lista.

FYI Det finns ett JIRA-problem SERVER-29339 som kräver $reduce ska implementeras som ett ackumulatoruttryck för att tillåta användning direkt i en $group rörledningsstadiet. Det är inte troligt att det kommer att hända snart, men det skulle teoretiskt sett ersätta $push i ovanstående och göra operationen till ett enda steg i pipeline. Exempel på föreslagen syntax handlar om JIRA-frågan.

Om du inte har $reduce (kräver MongoDB 3.4) sedan är det bara att efterbehandla markören:

db.collection.aggregate([
  { "$group": {
    "_id": "$tag_id",
    "client_id": { "$push": "$client_id" }
  }},
]).map( doc =>
  Object.assign(
    doc,
   { "client_id": doc.client_id.join(",") }
  )
)

Vilket sedan leder till det andra alternativet att göra detta med mapReduce om du verkligen måste:

db.collection.mapReduce(
  function() {
    emit(this.tag_id,this.client_id);
  },
  function(key,values) {
    return [].concat.apply([],values.map(v => v.split(","))).join(",");
  },
  { "out": { "inline": 1 } }
)

Vilket naturligtvis utmatas i den specifika mapReduce form av _id och value som en uppsättning nycklar, men det är i princip utgången.

Vi använder [].concat.apply([],values.map(...)) eftersom utdata från "reducer" kan vara en "avgränsad sträng" eftersom mapReduce fungerar inkrementellt med stora resultat och därför kan utsignalen från reduceraren bli "ingång" på ett annat pass. Så vi måste förvänta oss att detta kan hända och behandla det därefter.



  1. Mongodb Healthcheck steg för steg

  2. MapReduce verkar begränsade till 100?

  3. Ogiltig operator '$size' i aggregering

  4. Sortera nycklar i Response Object från Mongoose i ExpressJS och NodeJS