it-swarm.com.de

Aktualisieren Sie das MongoDB-Feld mit dem Wert eines anderen Felds

Ist es in MongoDB möglich, den Wert eines Feldes mit dem Wert eines anderen Feldes zu aktualisieren? Die äquivalente SQL würde ungefähr so ​​aussehen:

UPDATE Person SET Name = FirstName + ' ' + LastName

Und der MongoDB-Pseudocode wäre:

db.person.update( {}, { $set : { name : firstName + ' ' + lastName } );
310
Chris Fulstow

Der beste Weg, dies zu tun, ist in Version 4.2+, die die Verwendung der Aggregationspipeline im Aktualisierungsdokument und die Verwendung von updateOne , updateMany ermöglicht oder update Erfassungsmethode. Beachten Sie, dass letzteres in den meisten, wenn nicht allen Sprachtreibern veraltet ist.

MongoDB 4.2+

In Version 4.2 wurde auch der Pipeline-Stage-Operator $set eingeführt, der ein Alias ​​für $addFields ist. Ich werde $set Hier verwenden, da es mit dem übereinstimmt, was wir erreichen wollen.

db.collection.<update method>(
    {},
    [
        {"$set": {"name": { "$concat": ["$firstName", " ", "$lastName"]}}}
    ]
)

MongoDB 3.4+

In 3.4+ können Sie $addFields Und die Aggregations-Pipeline-Operatoren $out verwenden.

db.collection.aggregate(
    [
        { "$addFields": { 
            "name": { "$concat": [ "$firstName", " ", "$lastName" ] } 
        }},
        { "$out": "collection" }
    ]
)

Beachten Sie, dass dies Ihre Sammlung nicht aktualisiert, sondern stattdessen die vorhandene Sammlung ersetzt oder eine neue erstellt. Auch für Aktualisierungsvorgänge, die "Typumwandlung" Sie benötigen clientseitige Verarbeitung und abhängig von der Operation müssen Sie möglicherweise die find() -Methode anstelle der .aggreate() Methode.

MongoDB 3.2 und 3.0

Dazu verwenden wir $project unsere Dokumente und verwenden den Operator $concat Zeichenfolgenaggregation, um die verkettete Zeichenfolge zurückzugeben. Von dort aus iterieren Sie den Cursor und verwenden den Operator $set , um Ihren Dokumenten das neue Feld mit hinzuzufügen ) Bulk-Operationen für maximale Effizienz.

Aggregationsabfrage:

var cursor = db.collection.aggregate([ 
    { "$project":  { 
        "name": { "$concat": [ "$firstName", " ", "$lastName" ] } 
    }}
])

MongoDB 3.2 oder neuer

hierfür müssen Sie die bulkWrite -Methode verwenden.

var requests = [];
cursor.forEach(document => { 
    requests.Push( { 
        'updateOne': {
            'filter': { '_id': document._id },
            'update': { '$set': { 'name': document.name } }
        }
    });
    if (requests.length === 500) {
        //Execute per 500 operations and re-init
        db.collection.bulkWrite(requests);
        requests = [];
    }
});

if(requests.length > 0) {
     db.collection.bulkWrite(requests);
}

MongoDB 2.6 und 3.0

Ab dieser Version müssen Sie die jetzt veraltete Bulk API und deren zugehörige Methoden verwenden.

var bulk = db.collection.initializeUnorderedBulkOp();
var count = 0;

cursor.snapshot().forEach(function(document) { 
    bulk.find({ '_id': document._id }).updateOne( {
        '$set': { 'name': document.name }
    });
    count++;
    if(count%500 === 0) {
        // Excecute per 500 operations and re-init
        bulk.execute();
        bulk = db.collection.initializeUnorderedBulkOp();
    }
})

// clean up queues
if(count > 0) {
    bulk.execute();
}

MongoDB 2.4

cursor["result"].forEach(function(document) {
    db.collection.update(
        { "_id": document._id }, 
        { "$set": { "name": document.name } }
    );
})
164
styvane

Sie sollten durchlaufen. Für Ihren speziellen Fall:

db.person.find().snapshot().forEach(
    function (elem) {
        db.person.update(
            {
                _id: elem._id
            },
            {
                $set: {
                    name: elem.firstname + ' ' + elem.lastname
                }
            }
        );
    }
);
233

Anscheinend gibt es seit MongoDB 3.4 eine Möglichkeit, dies effizient zu tun, siehe styvanes Antwort .


Veraltete Antwort unten

Sie können in einem Update (noch) nicht auf das Dokument selbst verweisen. Sie müssen die Dokumente durchlaufen und jedes Dokument mithilfe einer Funktion aktualisieren. Siehe diese Antwort für ein Beispiel oder diese für serverseitiges eval().

103

Bei einer Datenbank mit hoher Aktivität treten möglicherweise Probleme auf, bei denen sich Ihre Aktualisierungen auf sich aktiv ändernde Datensätze auswirken. Aus diesem Grund empfehle ich die Verwendung von snapshot ()

db.person.find().snapshot().forEach( function (hombre) {
    hombre.name = hombre.firstName + ' ' + hombre.lastName; 
    db.person.save(hombre); 
});

http://docs.mongodb.org/manual/reference/method/cursor.snapshot/

42
Eric Kigathi

Ich habe die obige Lösung ausprobiert, fand sie jedoch für große Datenmengen ungeeignet. Ich habe dann die Stream-Funktion entdeckt:

MongoClient.connect("...", function(err, db){
    var c = db.collection('yourCollection');
    var s = c.find({/* your query */}).stream();
    s.on('data', function(doc){
        c.update({_id: doc._id}, {$set: {name : doc.firstName + ' ' + doc.lastName}}, function(err, result) { /* result == true? */} }
    });
    s.on('end', function(){
        // stream can end before all your updates do if you have a lot
    })
})
9
Chris Gibb

In Bezug auf answer ist die Snapshot-Funktion in Version 3.6 dementsprechend pdate veraltet. Ab Version 3.6 ist es also möglich, den Vorgang folgendermaßen auszuführen:

db.person.find().forEach(
    function (elem) {
        db.person.update(
            {
                _id: elem._id
            },
            {
                $set: {
                    name: elem.firstname + ' ' + elem.lastname
                }
            }
        );
    }
);
4
Aldo

Das haben wir uns ausgedacht, um ein Feld für ~ 150_000 Datensätze in ein anderes zu kopieren. Es dauerte ungefähr 6 Minuten, ist aber immer noch wesentlich weniger ressourcenintensiv als das Instanziieren und Iterieren derselben Anzahl von Ruby.

js_query = %({
  $or : [
    {
      'settings.mobile_notifications' : { $exists : false },
      'settings.mobile_admin_notifications' : { $exists : false }
    }
  ]
})

js_for_each = %(function(user) {
  if (!user.settings.hasOwnProperty('mobile_notifications')) {
    user.settings.mobile_notifications = user.settings.email_notifications;
  }
  if (!user.settings.hasOwnProperty('mobile_admin_notifications')) {
    user.settings.mobile_admin_notifications = user.settings.email_admin_notifications;
  }
  db.users.save(user);
})

js = "db.users.find(#{js_query}).forEach(#{js_for_each});"
Mongoid::Sessions.default.command('$eval' => js)
2
Chris Bloom

Ab Mongo 4.2 Kann db.collection.update() eine Aggregationspipeline akzeptieren und schließlich die Aktualisierung/Erstellung eines Felds basierend auf einem anderen Feld ermöglichen:

// { firstName: "Hello", lastName: "World" }
db.collection.update(
  {},
  [{ $set: { name: { $concat: [ "$firstName", " ", "$lastName" ] } } }],
  { multi: true }
)
// { "firstName" : "Hello", "lastName" : "World", "name" : "Hello World" }
  • Der erste Teil {} Ist die Übereinstimmungsabfrage, in der die zu aktualisierenden Dokumente (in unserem Fall alle Dokumente) gefiltert werden.

  • Der zweite Teil [{ $set: { name: { ... } }] Ist die Aktualisierungsaggregationspipeline (beachten Sie die eckigen Klammern, die die Verwendung einer Aggregationspipeline kennzeichnen). $set ist ein neuer Aggregationsoperator und ein Alias ​​von $addFields.

  • Vergessen Sie nicht { multi: true }, Sonst wird nur das erste passende Dokument aktualisiert.

0
Xavier Guihot