A melhor maneira de fazer isso é na versão 4.2+ que permite a utilização do gasoduto agregação no documento de atualização e o updateOne
, updateMany
ou update
método de coleta. Observe que o último foi preterido na maioria dos drivers, se não em todos os idiomas.
MongoDB 4.2+
A versão 4.2 também introduziu o $set
operador de estágio de pipeline, que é um alias para $addFields
. Vou usar $set
aqui como ele mapeia com o que estamos tentando alcançar.
db.collection.<update method>(
{},
[
{"$set": {"name": { "$concat": ["$firstName", " ", "$lastName"]}}}
]
)
MongoDB 3.4+
No 3.4+, você pode usar $addFields
e os $out
operadores de pipeline de agregação.
db.collection.aggregate(
[
{ "$addFields": {
"name": { "$concat": [ "$firstName", " ", "$lastName" ] }
}},
{ "$out": "collection" }
]
)
Observe que isso não atualiza sua coleção, mas substitui a coleção existente ou cria uma nova. Também para operações de atualização que requerem "conversão de tipo", você precisará de processamento no lado do cliente e , dependendo da operação, poderá precisar usar o find()
método em vez do .aggreate()
método.
MongoDB 3.2 e 3.0
A maneira como fazemos isso é usando $project
nossos documentos e usando o $concat
operador de agregação de string para retornar a string concatenada. A partir daí, você itera o cursor e usa o $set
operador de atualização para adicionar o novo campo aos seus documentos usando operações em massa para obter a máxima eficiência.
Consulta de agregação:
var cursor = db.collection.aggregate([
{ "$project": {
"name": { "$concat": [ "$firstName", " ", "$lastName" ] }
}}
])
MongoDB 3.2 ou mais recente
disso, você precisa usar o bulkWrite
método
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 e 3.0
Nesta versão, você precisa usar a Bulk
API agora obsoleta e seus métodos associados .
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 } }
);
})
Você deve percorrer. Para o seu caso específico:
fonte
save()
substitui completamente o documento. Deve usar em seuupdate()
lugar.db.person.update( { _id: elem._id }, { $set: { name: elem.firstname + ' ' + elem.lastname } } );
create_guid
que produzia apenas um guia único por documento ao iterarforEach
dessa maneira (ou seja, o simples usocreate_guid
de umaupdate
instrução commutli=true
causou o mesmo guia para todos os documentos). Essa resposta funcionou perfeitamente para mim. 1Aparentemente, existe uma maneira de fazer isso com eficiência desde o MongoDB 3.4, veja a resposta do styvane .
Resposta obsoleta abaixo
Você não pode se referir ao próprio documento em uma atualização (ainda). Você precisará percorrer os documentos e atualizar cada documento usando uma função. Veja esta resposta para um exemplo, ou esta para o servidor
eval()
.fonte
update
operação. Essa solicitação de recurso relacionada ainda não foi resolvida.OPEN
.Para um banco de dados com alta atividade, você pode encontrar problemas nos quais suas atualizações afetam a alteração ativa dos registros e, por esse motivo, recomendo o uso do snapshot ()
http://docs.mongodb.org/manual/reference/method/cursor.snapshot/
fonte
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.
linkEm relação a esta resposta , a função de captura instantânea foi preterida na versão 3.6, de acordo com esta atualização . Portanto, na versão 3.6 e superior, é possível executar a operação desta maneira:
fonte
Iniciando
Mongo 4.2
,db.collection.update()
pode aceitar um pipeline de agregação, finalmente permitindo a atualização / criação de um campo com base em outro campo:A primeira parte
{}
é a consulta de correspondência, filtrando quais documentos serão atualizados (no nosso caso, todos os documentos).A segunda parte
[{ $set: { name: { ... } }]
é o pipeline de agregação de atualização (observe os colchetes ao quadrado significando o uso de um pipeline de agregação).$set
é um novo operador de agregação e um alias de$addFields
.Não se esqueça
{ multi: true }
, caso contrário, apenas o primeiro documento correspondente será atualizado.fonte
Tentei a solução acima, mas achei inadequada para grandes quantidades de dados. Descobri o recurso de fluxo:
fonte
Aqui está o que criamos para copiar um campo para outro para ~ 150_000 registros. Demorou cerca de 6 minutos, mas ainda é significativamente menos intensivo em recursos do que seria para instanciar e iterar sobre o mesmo número de objetos ruby.
fonte
Com o MongoDB versão 4.2 ou posterior , as atualizações são mais flexíveis, pois permitem o uso do pipeline de agregação em seu
update
,updateOne
eupdateMany
. Agora você pode transformar seus documentos usando os operadores de agregação e atualizar sem a necessidade de explicitar o$set
comando (em vez disso, usamos$replaceRoot: {newRoot: "$$ROOT"}
)Aqui, usamos a consulta agregada para extrair o registro de data e hora do campo ObjectID "_id" do MongoDB e atualizar os documentos (eu não sou especialista em SQL, mas acho que o SQL não fornece nenhum ObjectID gerado automaticamente com registro de data e hora, você precisa criar automaticamente essa data)
fonte
{ $replaceRoot: { newRoot: "$$ROOT" } }
; significa substituir o documento por si só, o que é inútil. Se você substituir$addFields
por seu alias$set
eupdateMany
qual for um dos aliasesupdate
, obterá exatamente a mesma resposta que esta acima.