Using variable field names when running modifier operations on an embedded object in mongodb
When running modifier operations on an embedded object you would often run into situations where in the field name needs to be generated/referenced dynamically when the query runs.
Lets take an example, assume we have a db testing and in that we have a collection testing, in that lets insert some documents like
db.testing.save({key:1,data:{0:1,1:2,2:3}})using the following commands
> use testing switched to db testing > db.testing.remove({}) > db.testing.save({key:1,data:{0:1,1:2,2:3}}) > db.testing.save({key:2,data:{0:1,1:2,2:3}}) > db.testing.save({key:3,data:{0:1,1:2,2:3}}) > db.testing.find() { "_id" : ObjectId("4d0259f72715000000004d70"), "key" : 1, "data" : { "0" : 1, "1" : 2, "2" : 3 } } { "_id" : ObjectId("4d0259f72715000000004d71"), "key" : 2, "data" : { "0" : 1, "1" : 2, "2" : 3 } } { "_id" : ObjectId("4d0259f72715000000004d72"), "key" : 3, "data" : { "0" : 1, "1" : 2, "2" : 3 } } >
Now if we would want to increment every value of the embedded data(testing.data), we would normally be running a command like
> db.testing.update({key:1},{$inc:{"data.0":1},$inc:{"data.1":2},$inc:{"data.2":3}},false) > db.testing.find() { "_id" : ObjectId("4d0259f72715000000004d70"), "key" : 1, "data" : { "0" : 1, "1" : 2, "2" : 6 } } { "_id" : ObjectId("4d0259f72715000000004d71"), "key" : 2, "data" : { "0" : 1, "1" : 2, "2" : 3 } } { "_id" : ObjectId("4d0259f72715000000004d72"), "key" : 3, "data" : { "0" : 1, "1" : 2, "2" : 3 } } >
Now if you noticed we had to build individual increment commands for each field, this may not work out
- when there are large such fields to update, or
- when the field names to update are known at runtime for example in a map-reduce
You cannot use a variable substituion there since the field name in the increment operation maps to a string, if you subtitute it with a variable it would be inserted with a fieldname as the variable.
> var aField = "data.0" > db.testing.update({key:1},{$inc:{aField:1}},false) > db.testing.find() { "_id" : ObjectId("4d0259f72715000000004d70"), "aField" : 1, "data" : { "0" : 1, "1" : 2, "2" : 6 }, "key" : 1 } { "_id" : ObjectId("4d0259f72715000000004d71"), "key" : 2, "data" : { "0" : 1, "1" : 2, "2" : 3 } } { "_id" : ObjectId("4d0259f72715000000004d72"), "key" : 3, "data" : { "0" : 1, "1" : 2, "2" : 3 } } >
Here a new field with the name aField got inserted into the first document
So how do we handle this ? The solution is simple; since the update query takes in a json object, all we need to do is to build the whole thing separately as a json object.
First in-order to bring in the dynamic update use-case, lets use an array that contains the field name as the key and value as the value to be incremented
> var aIncr = []; > aIncr[0] = 1; 1 > aIncr[1] = 2; 2 > aIncr[2] = 3; 3 > for(pKey in aIncr) {print(pKey);print(aIncr[pKey]);} 0 1 1 2 2 3
This can be built dynamically at runtime. Now we use this to build the update query json object
> for (pKey in aIncr) { ... incKey = "data." + pKey ... incData = {} ... incData[incKey] = aIncr[pKey] ... incQuery = [] ... incQuery['$inc'] = incData ... db.testing.update({key:1},incQuery,false) ... } > db.testing.find() { "_id" : ObjectId("4d0259f72715000000004d70"), "aField" : 1, "data" : { "0" : 2, "1" : 4, "2" : 9 }, "key" : 1 } { "_id" : ObjectId("4d0259f72715000000004d71"), "key" : 2, "data" : { "0" : 1, "1" : 2, "2" : 3 } } { "_id" : ObjectId("4d0259f72715000000004d72"), "key" : 3, "data" : { "0" : 1, "1" : 2, "2" : 3 } } >
Here we build the increment part as separate json object that contains an array of key-value pairs(the actual fields to increment). Note you would not be able to use a dynamic object instead of an array for the increment json object due to the fact that increment modifier takes an object with a single field and value.
There you go, you can use the same approach when building any such dynamic query.
The complete working script is given below
use testing db.testing.remove({}) db.testing.save({key:1,data:{0:1,1:2,2:3}}) db.testing.save({key:2,data:{0:1,1:2,2:3}}) db.testing.save({key:3,data:{0:1,1:2,2:3}}) db.testing.find() var aIncr = []; aIncr[0] = 1; aIncr[1] = 2; aIncr[2] = 3; for(pKey in aIncr) {print(pKey);print(aIncr[pKey]);} for (pKey in aIncr) { incKey = "data." + pKey incData = {} incData[incKey] = aIncr[pKey] incQuery = [] incQuery['$inc'] = incData db.testing.update({key:1},incQuery,false) } db.testing.find()
Who's online
Who's new
- Saravana Peruma...
Recent comments
17 weeks 1 day ago
20 weeks 3 days ago
35 weeks 11 hours ago
37 weeks 2 days ago
45 weeks 6 days ago
1 year 24 weeks ago
2 years 4 days ago
2 years 1 week ago