sql >> Database >  >> NoSQL >> MongoDB

Voeg tekenreekswaarden samen in een array in een enkel veld in MongoDB

In moderne MongoDB-releases kan dat. U kunt nog steeds niet "direct" een array toepassen op $concat , u kunt echter $reduce . gebruiken om met de array-elementen te werken en dit te produceren:

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

Natuurlijk combineren met $indexOfArray om niet "samenvoegen" met de "_" onderstrepingsteken als het de "eerste" index van de array is.

Ook is mijn aanvullende "wens" beantwoord met $sum :

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

Dit soort wordt in het algemeen een beetje verhoogd met aggregatie-operators die een reeks items nemen. Het onderscheid hier is dat het een "array" van "agumenten" in de gecodeerde weergave betekent, in tegenstelling tot een "array-element" dat aanwezig is in het huidige document.

De enige manier waarop u echt het soort aaneenschakeling van items binnen een array in het document kunt doen, is door een soort JavaScript-optie te gebruiken, zoals in dit voorbeeld in mapReduce:

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

Natuurlijk, als u niet echt iets aggregeert, dan is misschien de beste aanpak om gewoon die "join"-bewerking in uw klantcode uit te voeren bij het naverwerking van uw queryresultaten. Maar als het voor een bepaald doel in verschillende documenten moet worden gebruikt, dan is mapReduce de enige plaats waar u het kunt gebruiken.

Ik zou kunnen toevoegen dat "bijvoorbeeld" ik graag zou willen dat zoiets werkt:

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

En in totaal:

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

Maar zo werkt het niet omdat $add verwacht argumenten in tegenstelling tot een array van het document. Zucht! :(. Een deel van de "door ontwerp" redenering hiervoor zou kunnen worden gesteld dat "alleen omdat" het een array of "lijst" is van singuliere waarden die worden doorgegeven uit het resultaat van de transformatie, het niet "gegarandeerd" is dat die zijn eigenlijk "geldige" enkelvoudige numerieke typewaarden die de operator verwacht. Tenminste niet bij de huidige geïmplementeerde methoden van "typecontrole".

Dat betekent dat we dit voorlopig nog moeten doen:

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

En helaas zou er ook geen manier zijn om zo'n groeperingsoperator toe te passen om strings samen te voegen.

Dus je kunt hopen op een soort verandering hierin, of hopen op een verandering waardoor een extern bereikbare variabele kan worden gewijzigd binnen het bereik van een $map op de een of andere manier opereren. Beter nog een nieuwe $join operatie zou ook welkom zijn. Maar deze bestaan ​​op het moment van schrijven nog niet, en zullen waarschijnlijk nog een tijdje niet bestaan.



  1. Fix "indexnaam moet een tekenreeks zijn" bij het laten vallen van meerdere indexen in MongoDB

  2. Een weergavefunctie maken zonder een antwoord terug te sturen in Flask

  3. MongoDB $ronde

  4. Hoe om te gaan met het tijdzoneprobleem bij het opslaan van datums in utc met behulp van mongod?