sql >> Databasteknik >  >> NoSQL >> MongoDB

Sammanfoga strängvärden i array i ett enda fält i MongoDB

I Modern MongoDB-utgåvor kan du. Du kan fortfarande inte "direkt" tillämpa en array på $concat , men du kan använda $reduce att arbeta med arrayelementen och producera detta:

db.collection.aggregate([
  { "$addFields": {
    "values": { 
      "$reduce": {
        "input": "$values",
        "initialValue": "",
        "in": {
          "$cond": {
            "if": { "$eq": [ { "$indexOfArray": [ "$values", "$$this" ] }, 0 ] },
            "then": { "$concat": [ "$$value", "$$this" ] },
            "else": { "$concat": [ "$$value", "_", "$$this" ] }
          }    
        }
      }        
    }
  }}
])

I kombination med $indexOfArray för att inte "sammanfoga" med "_" understryka när det är det "första" indexet i arrayen.

Även min ytterligare "önskelista" har besvarats med $sum :

db.collection.aggregate([
  { "$addFields": {
    "total": { "$sum": "$items.value" }
  }}
])

Den här typen av höjs lite generellt med aggregeringsoperatorer som tar en mängd objekt. Skillnaden här är att det betyder en "matris" av "agument" som tillhandahålls i den kodade representationen i motsats till ett "matriselement" som finns i det aktuella dokumentet.

Det enda sättet du verkligen kan göra den typ av sammanlänkning av objekt inom en array som finns i dokumentet är att göra något slags JavaScript-alternativ, som med det här exemplet i mapReduce:

db.collection.mapReduce(
    function() {
        emit( this._id, { "values": this.values.join("_") } );
    },
    function() {},
    { "out": { "inline": 1 } }
)

Naturligtvis, om du faktiskt inte aggregerar någonting, så är kanske det bästa tillvägagångssättet att helt enkelt göra den "join"-operationen i din klientkod när du efterbearbetar dina frågeresultat. Men om det behöver användas i något syfte över dokument kommer mapReduce att vara det enda stället där du kan använda det.

Jag skulle kunna tillägga att "till exempel" skulle jag älska att något sådant här skulle fungera:

{
    "items": [
        { "product": "A", "value": 1 },
        { "product": "B", "value": 2 },
        { "product": "C", "value": 3 }
    ]
}

Och sammantaget:

db.collection.aggregate([
    { "$project": {
        "total": { "$add": [
            { "$map": {
                "input": "$items",
                "as": "i",
                "in": "$$i.value"
            }}
        ]}
    }}
])

Men det fungerar inte på det sättet eftersom $add förväntar sig argument i motsats till en array från dokumentet. Suck! :(. En del av "by design"-resonemangen för detta skulle kunna hävdas att "bara för att" det är en array eller "lista" av singularvärden som skickas in från resultatet av transformationen är det inte "garanterat" att de är faktiskt "giltiga" singulära numeriska typvärden som operatören förväntar sig. Åtminstone inte vid de nuvarande implementerade metoderna för "typkontroll".

Det betyder att vi fortfarande måste göra detta:

db.collection.aggregate([
   { "$unwind": "$items" },
   { "$group": {
       "_id": "$_id",
        "total": { "$sum": "$items.value" }
   }}
])

Och tyvärr skulle det inte heller finnas något sätt att använda en sådan grupperingsoperator för att sammanfoga strängar.

Så du kan hoppas på någon form av förändring på detta, eller hoppas på någon förändring som gör att en externt omfångsvariabel kan ändras inom ramen för en $map operation på något sätt. Ännu bättre en ny $join operation skulle också vara välkommen. Men dessa finns inte i skrivande stund, och kommer förmodligen inte att göra det på en tid framöver.



  1. Uppdatera dokument i MongoDB

  2. Skapa en samling i MongoDB

  3. MongoDB - Filtrera innehållet i en intern Array i en resultatuppsättning

  4. Är redis-operationer på datastrukturer trådsäkra