2017-01-05 3 views
2

Ich muss ein Array mit eingebetteten Feldern über die Mongo-Konsole aktualisieren. Ich muss ein neues Element als "Test13" mit Standardwert als "1" in allen Filialdokumenten Fields hinzufügen. HierNeues Element im verschachtelten Array hinzufügen

ist ein Beispiel für meine Sammlung mit dem Namen testmarket:

{ 
    "_id" : ObjectId("573c9801056848fef667bfde"), 
    "MST" : "RR", 
    "GST" : null, 
    "Fields" : [{ 
     "Test1" : "boolean", 
     "Test2" : "TestLot", 
     "Test3" : "TestLot", 
     "Test4" : null, 
     "Test5" : true, 
     "Test6" : true, 
     "Test7" : NumberInt(1), 
     "Test8" : false, 
     "Test9" : false, 
     "Test10" : false, 
     "Test11" : false, 
     "Test12" : null 
    }, { 
     "Test1" : "String", 
     "Test2" : "TestSerial", 
     "Test3" : "TestSerial", 
     "Test4" : null, 
     "Test5" : true, 
     "Test6" : true, 
     "Test7" : NumberInt(1), 
     "Test8" : false, 
     "Test9" : false, 
     "Test10" : false, 
     "Test11" : false, 
     "Test12" : null 
    }] 
} 

Sammlung ist sehr groß und ich brauche ein neues Element in Feldern Sammlung hinzuzufügen. .

Ich habe versucht, die Abfrage

db.testmarket.Fields.update(
    {}, 
    {$set : {"Test13":"1"}}, 
    {upsert:true, multi:true} 
) 
+0

Was haben Sie ausprobiert? – Yogesh

+0

~ db.testmarket.Fields.update ({}, {$ gesetzt: { "Test13": "1"}}, { Upsert: true, multi: true}) ~ – Sam

Antwort

1

ist die Antwort:

db.testmarket.find().toArray().forEach(
    function(obj){ 
    for(var i = 0; i < obj.Fields.length; ++i) { 
     obj.Fields[i]['Test13'] = '1'; 
    } 
    db.testmarket.update({_id: obj._id}, obj); 
    } 
); 

Dank

+0

Die Lösung könnte durch Verwendung der Bulk-API verbessert werden, da das OP erwähnte, dass die "testmarket" -Auflistung sehr groß ist und somit die Leistung gefährdet ist, wenn Sie mit jedem Dokument in der Sammlung eine Schreibanforderung senden. Stellen Sie sich vor, wenn die Sammlung eine Million Dokumente enthält, was bedeutet, dass "db.testmarket.update ({_ id: obj._id}, obj)" eine Million Mal aufgerufen wird -> schlechte Leistung. – chridam

+0

Große Lösung, half mir wie ein Charme! –

0

db.getCollection läuft ('testmarket') Update ({ "_ id": ObjectId (573c9801056848fef667bfde)}, { '$ addToSet': { 'Fields': {"Test13": 1}}}); Aber ich denke, dass Sie Ihrem jsons innerhalb des Arrays wie dem oben genannten Code einen Namen geben sollten, indem Sie einfach ein json {Test13: 1} außerhalb aller jsons einfügen. Wenn Sie Ihre Struktur wie folgt ändern: Felder: [{"firstTest": {"Test1": "boolean", ......}, {"secondTest": {"Test1": "boolean", ... ...}}}] dann kann der obige Code umgeschrieben werden als->

db.getCollection ('testmarket'). update ({"_ id": Objekt-ID (573c9801056848fef667bfde)}, {'$ addToSet': { 'Fields.firstTest': {"Test13": 1}}});

+0

Hallo Riya, erste Abfrage ist Test13 nicht in die Testmarket-Sammlung einfügen. – Sam

+0

db.getCollection ('testmarket'). Update ({"_ id": Objekt-ID (573c9801056848fef667bfde)}, {'$ addToSet': {'Felder': {"Test13": NumberInt (1)}}}); Hast du das nur versucht? –

0
db.testmarket.find({}).forEach(function(x){for(var i in x.Fields){x.Fields[i]['Test13']=1} db.testmarket.save(x);}); 

Das glaube ich nicht, dass Sie es mit dem Update tun, Update Anruf bestenfalls wird aus dem Dokument ein einzelnes Element aktualisieren, obwohl sie es für die ganze Dokument mit Multi tun: true, aber nicht alle Elemente pro Dokument im gesamten Dokument.

Falls Ihre Sammlung zu groß ist, können Sie die obige Methode im Batch mit der Verwendung von Limit aufrufen und überspringen. Hier

1

Sie müssen im Wesentlichen eine Menge aller Fields Arrays in Ihrer Sammlung packen, die Sammlung durchlaufen und für jedes Dokument die Felder Teilmenge iterieren. Für jedes Element Fields aktualisieren Sie die Sammlung mit der $ positional operator.

zum Beispiel innerhalb der inneren Schleife (da Sie 2 verschachtelte Schleifen haben), das Update für das erste Element Array Fields wäre

db.testmarket.updateOne(
    { 
     "_id" : ObjectId("573c9801056848fef667bfde"), 
     "Fields.Test1" : "boolean", 
     "Fields.Test2" : "TestLot", 
     "Fields.Test3" : "TestLot", 
     "Fields.Test4" : null, 
     "Fields.Test5" : true, 
     "Fields.Test6" : true, 
     "Fields.Test7" : 1, 
     "Fields.Test8" : false, 
     "Fields.Test9" : false, 
     "Fields.Test10" : false, 
     "Fields.Test11" : false, 
     "Fields.Test12" : null 
    }, 
    { "$set": { "Fields.$.Test13": "1" } } 
); 

und das nächste Update in der Iteration wäre:

db.testmarket.updateOne(
    { 
     "_id" : ObjectId("573c9801056848fef667bfde"), 
     "Fields.Test1" : "String", 
     "Fields.Test2" : "TestSerial", 
     "Fields.Test3" : "TestSerial", 
     "Fields.Test4" : null, 
     "Fields.Test5" : true, 
     "Fields.Test6" : true, 
     "Fields.Test7" : 1, 
     "Fields.Test8" : false, 
     "Fields.Test9" : false, 
     "Fields.Test10" : false, 
     "Fields.Test11" : false, 
     "Fields.Test12" : null 
    }, 
    { "$set": { "Fields.$.Test13": "1" } } 
); 

und so weiter.

Dieser Algorithmus kann wie folgt realisiert werden:

db.testmarket.find().snapshot().forEach(function(doc) { 
    doc.Fields.forEach(function(field) { 
     var query = {}, 
      obj = { Fields: field }; 

     /* function to convert an object into dot notation */ 
     (function recurse(obj, current) { 
      for(var key in obj) { 
       var value = obj[key]; 
       var newKey = (current ? current + "." + key : key); /* joined key with dot */ 
       if(value && typeof value === "object") { 
        recurse(value, newKey); // it's a nested object, so do it again 
       } else { 
        query[newKey] = value; // it's not an object, so set the property 
       } 
      } 
     })(obj); 

     query["_id"] = doc._id; 

     /* do the update */ 
     db.testmarket.updateOne(
      query, 
      { "$set": { "Fields.$.Test13": "1" } } 
     ); 
    }); 
}); 

Wie Sie aus dem oben sehen können, beeinträchtigt werden wird Leistung gebunden, da Sie für Schleife doppelt verschachtelten haben. Die äußere Schleife für die erste Iteration, d. H. Das Iterieren der gesamten Sammlung, führt n-mal aus, wobei n die Gesamtanzahl der Dokumente in der Sammlung ist.

Für jede Iteration der äußeren Schleife, wird die innere Schleife i mal ausgeführt, wo i die Länge des Fields Array ist, so dass die Gesamtkomplexität kann wie folgt berechnet werden: ein für die zweite Iteration der ersten Iteration plus zwei plus drei für die dritte Iteration und so weiter plus n für die n-te Iteration.

1+2+3+4+5+...+n = (n*(n-1))/2 --> O(n^2) 

Doing das Updates in einer verschachtelten Schleife mit O(n^2) Komplexität für sehr große Sammlungen ist nicht sehr effizient. In dieser Hinsicht können Sie Ihren Code optimieren, indem Sie die Bulk API nutzen, die es Ihnen ermöglicht, die Updates als optimierte Chargen zu senden, dh anstatt jede Aktualisierungsanforderung mit jeder Iteration an den Server zu senden, können Sie die Aktualisierungsoperationen in einer Einzelanfrage, die schneller und effizienter ist. Im Folgenden wird gezeigt, wie Sie die Aktualisierungen mithilfe der Methode bulkWrite() nutzen können.

Für MongoDB Version 3.0 und unten:

var bulk = db.testmarket.initializeUnOrderedBulkOp(), 
    counter = 0; 

db.testmarket.find().snapshot().forEach(function(doc) { 
    doc.Fields.forEach(function(field) { 
     var query = {}, 
      obj = { Fields: field }; 

     /* function to convert an object into dot notation */ 
     (function recurse(obj, current) { 
      for(var key in obj) { 
       var value = obj[key]; 
       var newKey = (current ? current + "." + key : key); /* joined key with dot */ 
       if(value && typeof value === "object") { 
        recurse(value, newKey); // it's a nested object, so do it again 
       } else { 
        query[newKey] = value; // it's not an object, so set the property 
       } 
      } 
     })(obj); 

     query["_id"] = doc._id; 

     /* load up update operations */ 
     bulk.find(query).updateOne({ "$set": { "Fields.$.Test13": "1" } }); 

     counter++; 

     /* execute the update operations at once in bulk */ 
     if (counter % 500 === 0) { 
      bulk.execute(); 
      bulk = db.testmarket.initializeUnOrderedBulkOp(); 
     } 
    }); 
}); 

/* clean up the remaining update operations left in the queue */ 
if (counter % 500 !== 0) 
    bulk.execute(); 

MongoDB 3.2 oder neuer:

var ops = []; 

db.testmarket.find().snapshot().forEach(function(doc) { 
    doc.Fields.forEach(function(field) { 
     var query = {}, 
      obj = { Fields: field }; 

     /* function to convert an object into dot notation */ 
     (function recurse(obj, current) { 
      for(var key in obj) { 
       var value = obj[key]; 
       var newKey = (current ? current + "." + key : key); /* joined key with dot */ 
       if(value && typeof value === "object") { 
        recurse(value, newKey); // it's a nested object, so do it again 
       } else { 
        query[newKey] = value; // it's not an object, so set the property 
       } 
      } 
     })(obj); 

     query["_id"] = doc._id; 

     /* load up update operations */ 
     ops.push({ 
      "updateOne": { 
       "filter": query, 
       "update": { "$set": { "Fields.$.Test13": "1" } } 
      } 
     }); 
     counter++;  
    }); 

    if (counter % 500 === 0) { 
     db.testmarket.bulkWrite(ops); 
     ops = []; 
    } 
}); 


if (counter % 500 !== 0) 
    db.testmarket.bulkWrite(ops); 

Die Zählvariable oben ist es Ihre Bulk-Updates effektiv zu verwalten, wenn Sie Ihre Sammlung groß. Sie können die Aktualisierungsvorgänge stapelweise durchführen und die Schreibvorgänge in Stapeln von 500 an den Server senden, wodurch Sie eine bessere Leistung erzielen, da Sie nicht jede Anforderung an den Server senden, sondern nur einmal alle 500 Anfragen.

Für Massenoperationen legt MongoDB ein internes Standardlimit von 1000 Operationen pro Stapel fest. Daher ist die Auswahl von 500 Dokumenten in dem Sinne gut, dass Sie etwas Kontrolle über die Stapelgröße haben, statt MongoDB den Standard auferlegen zu lassen Operationen in der Größenordnung von> 1000 Dokumenten.

Verwandte Themen