Najlepszym sposobem na to jest w wersji 4.2 lub nowszej wersji, która pozwala za pomocą rurociągu agregacji w dokumencie Update oraz updateOne
, updateMany
czy update
metody zbierania. Należy pamiętać, że ten drugi jest przestarzały w większości, jeśli nie we wszystkich językach sterowników.
MongoDB 4.2+
W wersji 4.2 wprowadzono również $set
operator etapu potoku, który jest aliasem $addFields
. Wykorzystam $set
tutaj, ponieważ mapuje to, co próbujemy osiągnąć.
db.collection.<update method>(
{},
[
{"$set": {"name": { "$concat": ["$firstName", " ", "$lastName"]}}}
]
)
MongoDB 3.4+
W 3.4+ można używać $addFields
i $out
operatorów rurociągów agregacji.
db.collection.aggregate(
[
{ "$addFields": {
"name": { "$concat": [ "$firstName", " ", "$lastName" ] }
}},
{ "$out": "collection" }
]
)
Pamiętaj, że to nie aktualizuje Twojej kolekcji, ale zastępuje istniejącą kolekcję lub tworzy nową. Również w przypadku operacji aktualizacji wymagających „rzutowania typu” konieczne będzie przetwarzanie po stronie klienta, aw zależności od operacji może być konieczne użycie find()
metody zamiast .aggreate()
metody.
MongoDB 3.2 i 3.0
Sposób, w jaki to robimy, polega na przesłaniu $project
dokumentów i użyciu $concat
operatora agregacji ciągów w celu zwrócenia połączonego łańcucha. my Stamtąd iterujemy kursor i używamy $set
operatora aktualizacji, aby dodać nowe pole do dokumentów za pomocą operacji zbiorczych w celu uzyskania maksymalnej wydajności.
Zapytanie agregacyjne:
var cursor = db.collection.aggregate([
{ "$project": {
"name": { "$concat": [ "$firstName", " ", "$lastName" ] }
}}
])
MongoDB 3.2 lub nowszy
z tego musisz użyć bulkWrite
metody.
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 i 3.0
Od tej wersji musisz używać przestarzałego Bulk
interfejsu API i powiązanych z nim metod .
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 } }
);
})
Powinieneś iterować. W konkretnym przypadku:
źródło
save()
całkowicie zastępuje dokument. Powinien użyćupdate()
zamiast.db.person.update( { _id: elem._id }, { $set: { name: elem.firstname + ' ' + elem.lastname } } );
create_guid
która generowała tylko unikalny identyfikator dla każdego dokumentu podczas iteracjiforEach
w ten sposób (tj. Po prostu użyciecreate_guid
wupdate
instrukcji zmutli=true
spowodowało wygenerowanie tego samego identyfikatora dla wszystkich dokumentów). Ta odpowiedź działała dla mnie idealnie. +1Najwyraźniej istnieje sposób, aby to zrobić skutecznie od MongoDB 3.4, patrz odpowiedź styvane .
Przestarzała odpowiedź poniżej
Nie można odwoływać się do samego dokumentu w aktualizacji (jeszcze). Musisz iterować dokumenty i aktualizować każdy dokument za pomocą funkcji. Zobacz tę odpowiedź na przykład lub tę po stronie serwera
eval()
.źródło
update
operacji. To powiązane żądanie funkcji jest nadal nierozwiązane.OPEN
.W przypadku bazy danych o wysokiej aktywności możesz napotkać problemy, w których twoje aktualizacje wpływają na aktywnie zmieniające się rekordy iz tego powodu zalecam użycie snapshot ()
http://docs.mongodb.org/manual/reference/method/cursor.snapshot/
źródło
snapshot()
:Deprecated in the mongo Shell since v3.2. Starting in v3.2, the $snapshot operator is deprecated in the mongo shell. In the mongo shell, use cursor.snapshot() instead.
linkJeśli chodzi o tę odpowiedź , funkcja migawki jest przestarzała w wersji 3.6, zgodnie z tą aktualizacją . Tak więc w wersji 3.6 i nowszej można wykonać tę operację w następujący sposób:
źródło
Zaczynając
Mongo 4.2
,db.collection.update()
akceptuje potok agregacji, wreszcie pozwalając na aktualizację / utworzenie pola na podstawie innego pola:Pierwsza część
{}
to zapytanie pasujące, które filtruje dokumenty do aktualizacji (w naszym przypadku wszystkie dokumenty).Druga część
[{ $set: { name: { ... } }]
to potok agregacji aktualizacji (zwróć uwagę na kwadratowe nawiasy kwadratowe oznaczające użycie potoku agregacji).$set
to nowy operator agregacji i alias$addFields
.Nie zapomnij
{ multi: true }
, w przeciwnym razie tylko pierwszy pasujący dokument zostanie zaktualizowany.źródło
Wypróbowałem powyższe rozwiązanie, ale okazało się, że nie nadaje się do dużych ilości danych. Następnie odkryłem funkcję strumienia:
źródło
Oto, co wymyśliliśmy, aby skopiować jedno pole do drugiego dla ~ 150_000 rekordów. Zajęło to około 6 minut, ale nadal jest znacznie mniej zasobochłonne niż w przypadku tworzenia i iteracji tej samej liczby obiektów rubinowych.
źródło
Z MongoDB wersji wersji 4.2 lub nowszej , aktualizacje są bardziej elastyczne, gdyż umożliwia korzystanie z rurociągu agregacji w swojej
update
,updateOne
iupdateMany
. Możesz teraz przekształcić swoje dokumenty za pomocą operatorów agregujących, a następnie zaktualizować bez potrzeby jawnego określania$set
polecenia (zamiast tego używamy$replaceRoot: {newRoot: "$$ROOT"}
)W tym przypadku wykorzystujemy zapytanie zagregowane, aby wyodrębnić znacznik czasu z pola ObjectID „_id” MongoDB i zaktualizować dokumenty (nie jestem ekspertem w dziedzinie SQL, ale myślę, że SQL nie zapewnia żadnego automatycznie wygenerowanego ObjectID, który ma znacznik czasu, musiałbyś automatycznie utwórz tę datę)
źródło
{ $replaceRoot: { newRoot: "$$ROOT" } }
; oznacza to zastąpienie samego dokumentu, co nie ma sensu. Jeśli zamienisz$addFields
na jego alias$set
iupdateMany
który jest jednym z aliasówupdate
, uzyskasz dokładnie taką samą odpowiedź jak ta powyżej.