self referencing update using MongoDB
Asked Answered
L

2

10

I wonder if there is a way to make a self referencing update in MongoDB, so you can use object's params on a $set query. Here is an example:

> db.labels.save({"name":"label1", "test":"hello"})
> db.labels.save({"name":"label2", "test":"hello"})
> db.labels.save({"name":"label3", "test":"hello"})
> db.labels.find()
{ "_id" : ObjectId("4f1200e2f8509434f1d28496"), "name" : "label1", "test" : "hello" }
{ "_id" : ObjectId("4f1200e6f8509434f1d28497"), "name" : "label2", "test" : "hello" }
{ "_id" : ObjectId("4f1200eaf8509434f1d28498"), "name" : "label3", "test" : "hello" }

I saw that you can use this syntax on $where queries: http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-JavascriptExpressionsand%7B%7B%24where%7D%7D

> db.myCollection.find( { a : { $gt: 3 } } ); 
> db.myCollection.find( { $where: "this.a > 3" } );
> db.myCollection.find("this.a > 3");
> f = function() { return this.a > 3; } db.myCollection.find(f);

So, I tried with:

db.labels.update({"test":"hola"}, {$set : {"test": this.name})

but it didn't work.

The expected result is:

{ "_id" : ObjectId("4f1200e2f8509434f1d28496"), "name" : "label1", "test" : "label1" }
{ "_id" : ObjectId("4f1200e6f8509434f1d28497"), "name" : "label2", "test" : "label2" }
{ "_id" : ObjectId("4f1200eaf8509434f1d28498"), "name" : "label3", "test" : "label3" }

Any thoughts? Thanks in advance

Leodora answered 15/1, 2012 at 8:58 Comment(0)
C
22

Update:

It can be done by now using

db.labels.updateMany(
   {"test":"hola"},
   [{ $set: { test: "$name" }}],
)

Old Answer

At present there is no straight way to do that. But you can workaround this by

    db.labels.find({"test":"hola"}).forEach(function (doc) {
           doc.test = doc.name;
           db.labels.save(doc); 
    })
Clayborne answered 15/1, 2012 at 9:25 Comment(6)
Thanks for the response RameshVel. I'm using Mongoid, so I will loop over the results and save it with the old value. I'm worried about the performance. Any recommendation?Leodora
@fertapric, sorry nope :( . I am afraid thats the only way.Clayborne
@Clayborne Is that still true for the current version of MongoDb?Spaceman
@Cartesius00, seems its still a open request per jira.mongodb.org/browse/…Clayborne
Sorry but, isn't the use of "snapshot()" method needed here to prevent updating the same doc twice ? or maybe I did not understand well what the snapshot option does.Phylactery
MongoDB 4.01 : "$name" is simply resolved into... "$name" ! not the value of document.name . Furthermore, the second argument, $set:, doesn't accept an array. It must be a document or an aggregation.Carrack
Q
4

new in MongoDB 4.2

[FYI] Below approach avoids row-by-row operations (which can cause performance issues), and shifts the processing load onto the DB itself.

"Starting in MongoDB 4.2, the db.collection.update() method can accept an aggregation pipeline that specifies the modifications to perform." docs

The pipeline has access to each documents' fields, thus allowing self-referencial updates.
Please see the documentation on this, which includes an example of this sort of update.

Following the example from the OP's question the update would be:

db.labels.update(
   {"test":"hello"},
   [{ $set: { test: "$name" }}],
   { multi: true }
);

Please note that the $set used in the pipeline refers to the aggregation stage $set, and not the update operator $set.

For those familiar with the aggregate pipeline in earlier MongoDB versions: the $set stage is an alias for $addFields.

Quinque answered 16/10, 2020 at 12:6 Comment(1)

© 2022 - 2024 — McMap. All rights reserved.