2016-08-08 63 views
4

Ich habe eine Sammlung mit Dokumenten gefüllt, die wie folgt aussehen:effizienteste Weg, ein String-Feld den Wert seiner Teilkette zu ändern

{ 
    data: 11, 
    version: "0.0.32" 
} 

und einige haben einen test Suffix version:

{ 
    data: 55, 
    version: "0.0.42-test" 
} 

Das Feld version hat unterschiedliche Werte, aber es entspricht immer dem Muster: 0.0.XXX. Ich möchte alle Dokumente aktualisieren, wie folgt aussehen:

{ 
    data: 11, 
    version: 32 
} 

und der nachgestellten Version (für Testdokumente - version sollte negativ sein):

{ 
    data: 55, 
    version: -42 
} 

Die Sammlung mit diesen Dokumenten verwendet wird, durch unser kritisches System, das bei der Aktualisierung der Daten ausgeschaltet werden muss - ich möchte, dass die Aktualisierung/Änderung so schnell wie möglich erfolgt. Es gibt ungefähr 66_000_000 Dokumente in dieser Sammlung, und es ist etwa 100 GB groß.

Welcher Typ von Mongodb-Betrieb wäre der effizienteste?

+0

in Bezug auf den Titel, '32' ist __not__ eine Teil von' "0.0. 32 ". In der Tat ist es überhaupt keine Schnur. JFYI. –

+0

Der Kern des Problems besteht darin, einen Teilstring zu nehmen. Könntest du mir helfen, einen besseren Titel zu finden? –

Antwort

4

Der effizienteste Weg, dies zu tun, ist in der bevorstehenden Veröffentlichung von MongoDB zum Zeitpunkt des Schreibens der $split Operator unsere String aufgeteilt als shown here dann auf eine Variable mit dem $let Variable Operator das letzte Element in dem Array zuweisen und die $arrayElemAt Betreiber.

Als nächstes verwenden wir den Operator $switch, um eine logische Bedingungsverarbeitung oder case-Anweisung für diese Variable auszuführen.

Die Bedingung ist hier $gt die true zurück, wenn der Wert "test" enthält, und in dem Fall, in der in Expression teilten wir die Zeichenfolge einfach und geben den $concat enated Wert des ersten Elements in der neu berechneten Array und die -. Wenn die Bedingung false ergibt, geben wir nur die Variable zurück.

Natürlich verwenden wir in unserem Fall die $indexOfCP, die -1 zurückgibt, wenn keine "test" aufgetreten ist.

let cursor = db.collection.aggregate(
    [ 
     { "$project": { 
      "data": 1, 
      "version": { 
       "$let": { 
        "vars": { 
         "v": { 
          "$arrayElemAt": [ 
           { "$split": [ "$version", "." ] }, 
           -1 
          ] 
         } 
        }, 
        "in": { 
         "$switch": { 
          "branches": [ 
           { 
            "case": { 
             "$gt": [ 
              { "$indexOfCP": [ "$$v", "test" ] }, 
              -1 
             ] 
            }, 
            "then": { 
             "$concat": [ 
              "-", 
              "", 
              { "$arrayElemAt": [ 
               { "$split": [ "$$v", "-" ] }, 
               0 
              ]} 
             ] 
            } 
           } 
          ], 
          "default": "$$v" 
         } 
        } 
       } 
      } 
     }} 
    ] 
) 

Die Aggregation Abfrage ergibt dies so etwas wie:

{ "_id" : ObjectId("57a98773cbbd42a2156260d8"), "data" : 11, "version" : "32" } 
{ "_id" : ObjectId("57a98773cbbd42a2156260d9"), "data" : 55, "version" : "-42" } 

Wie Sie sehen können, die "Version" Felddatenfolge sind. Wenn der Datentyp für dieses Feld dann keine Rolle spielt, können Sie einfach den Aggregationspipelinestufenoperatorverwenden, um das Ergebnis in eine neue Sammlung zu schreiben oder Ihre Sammlung zu ersetzen.

{ "out": "collection" } 

Wenn Sie dann Ihre Daten in Fließkommazahl konvertieren, ist der einzige Weg, dies zu tun, einfach weil MongoDB nicht keine Möglichkeit bietet Typumwandlung aus der Box mit Ausnahme integer zu bespannen zu tun , iterieren Sie das Aggregations-Cursor-Objekt und konvertieren Sie Ihren Wert mit parseFloat oder Number und aktualisieren Sie dann Ihre Dokumente mit dem Operator $set und der bulkWrite()-Methode für maximale Effizienz.

let requests = []; 
cursor.forEach(doc => { 
    requests.push({ 
     "updateOne": { 
      "filter": { "_id": doc._id }, 
      "update": { 
       "$set": { 
        "data": doc.data, 
        "version": parseFloat(doc.version) 
       }, 
       "$unset": { "person": " " } 
      } 
     } 
    }); 
    if (requests.length === 1000) { 
     // Execute per 1000 ops and re-init 
     db.collection.bulkWrite(requests); 
     requests = []; 
    }} 
); 

// Clean up queues 
if(requests.length > 0) { 
    db.coll.bulkWrite(requests); 
} 

Während die Aggregationsanfrage in MongoDB perfekt funktionieren 3.4 oder neuer unsere beste Wette von MongoDB 3.2 rückwärts ist mapReduce mit dem bulkWrite() Verfahren.

var results = db.collection.mapReduce(
    function() { 
     var v = this.version.split(".")[2]; 
     emit(this._id, v.indexOf("-") > -1 ? "-"+v.replace(/\D+/g, '') : v) 
    }, 
    function(key, value) {}, 
    { "out": { "inline": 1 } } 
)["results"]; 

results sieht wie folgt aus:

[ 
    { 
     "_id" : ObjectId("57a98773cbbd42a2156260d8"), 
     "value" : "32" 
    }, 
    { 
     "_id" : ObjectId("57a98773cbbd42a2156260d9"), 
     "value" : "-42" 
    } 
] 

Von hier können Sie die vorherige .forEach Schleife benutzen, um Ihre Dokumente zu aktualisieren.


Von MongoDB 2,6 bis 3,0 Sie müssen das jetzt veraltet Bulk() API verwenden und das zugehörige Verfahren als Show in meinem answer here.