MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Verketten Sie Zeichenfolgenwerte in einem Array in einem einzelnen Feld in MongoDB

In Modern MongoDB-Releases ist dies möglich. Sie können ein Array immer noch nicht "direkt" auf $concat anwenden , Sie können jedoch $reduce verwenden um mit den Array-Elementen zu arbeiten und dies zu erzeugen:

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

Kombiniert natürlich mit $indexOfArray um nicht "verketten" mit dem "_" unterstreichen, wenn es der "erste" Index des Arrays ist.

Auch mein zusätzlicher "Wunsch" wurde mit $sum beantwortet :

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

Diese Art wird im Allgemeinen mit Aggregationsoperatoren, die ein Array von Elementen verwenden, etwas erhöht. Der Unterschied besteht hier darin, dass es sich um ein "Array" von "Agumenten" handelt, die in der codierten Darstellung bereitgestellt werden, im Gegensatz zu einem "Array-Element", das im aktuellen Dokument vorhanden ist.

Die einzige Art, wie Sie die Art der Verkettung von Elementen innerhalb eines im Dokument vorhandenen Arrays wirklich durchführen können, besteht darin, eine Art JavaScript-Option zu verwenden, wie in diesem Beispiel in mapReduce:

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

Wenn Sie tatsächlich nichts aggregieren, ist es möglicherweise der beste Ansatz, diese "Join"-Operation einfach in Ihrem Clientcode bei der Nachbearbeitung Ihrer Abfrageergebnisse durchzuführen. Aber wenn es für einen bestimmten Zweck dokumentenübergreifend verwendet werden muss, dann ist mapReduce der einzige Ort, an dem Sie es verwenden können.

Ich könnte hinzufügen, dass "zum Beispiel" ich es lieben würde, wenn so etwas funktioniert:

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

Und insgesamt:

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

Aber es funktioniert nicht so, weil $add erwartet Argumente im Gegensatz zu einem Array aus dem Dokument. Seufzen! :(. Ein Teil der "beabsichtigten" Argumentation dafür könnte argumentiert werden, dass "nur weil" es sich um ein Array oder eine "Liste" von singulären Werten handelt, die aus dem Ergebnis der Transformation übergeben werden, ist dies nicht "garantiert". tatsächlich "gültige" singuläre numerische Typwerte, die der Operator erwartet, zumindest nicht bei den derzeit implementierten Methoden der "Typprüfung".

Das bedeutet, dass wir vorerst noch Folgendes tun müssen:

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

Und leider gäbe es auch keine Möglichkeit, einen solchen Gruppierungsoperator auf die Verkettung von Zeichenfolgen anzuwenden.

Sie können also auf eine Änderung hoffen oder auf eine Änderung, die es ermöglicht, eine Variable mit externem Geltungsbereich innerhalb des Geltungsbereichs einer $map zu ändern Betrieb in gewisser Weise. Besser noch ein neues $join Betrieb wäre auch willkommen. Aber diese existieren zum jetzigen Zeitpunkt nicht und werden es wahrscheinlich noch einige Zeit nicht geben.