How can I rename a field for all documents in MongoDB? - mongodb

Assuming I have a collection in MongoDB with 5000 records, each containing something similar to:
{
"occupation":"Doctor",
"name": {
"first":"Jimmy",
"additional":"Smith"
}
Is there an easy way to rename the field "additional" to "last" in all documents? I saw the $rename operator in the documentation but I'm not really clear on how to specify a subfield.

You can use:
db.foo.update({}, {
$rename: {
"name.additional": "name.last"
}
}, false, true);
Or to just update the docs which contain the property:
db.foo.update({
"name.additional": {
$exists: true
}
}, {
$rename: {
"name.additional": "name.last"
}
}, false, true);
The false, true in the method above are: { upsert:false, multi:true }. You need the multi:true to update all your records.
Or you can use the former way:
remap = function (x) {
if (x.additional) {
db.foo.update({
_id: x._id
}, {
$set: {
"name.last": x.name.additional
}, $unset: {
"name.additional": 1
}
});
}
}
db.foo.find().forEach(remap);
In MongoDB 3.2 you can also use
db.students.updateMany({}, {
$rename: {
"oldname": "newname"
}
})
The general syntax of this is
db.collection.updateMany(filter, update, options)
https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/

You can use the $rename field update operator:
db.collection.update(
{},
{ $rename: { 'name.additional': 'name.last' } },
{ multi: true }
)

If ever you need to do the same thing with mongoid:
Model.all.rename(:old_field, :new_field)
UPDATE
There is change in the syntax in monogoid 4.0.0:
Model.all.rename(old_field: :new_field)

Anyone could potentially use this command to rename a field from the collection (By not using any _id):
dbName.collectionName.update({}, {$rename:{"oldFieldName":"newFieldName"}}, false, true);
see FYI

I am using ,Mongo 3.4.0
The $rename operator updates the name of a field and has the following form:
{$rename: { <field1>: <newName1>, <field2>: <newName2>, ... } }
for e.g
db.getCollection('user').update( { _id: 1 }, { $rename: { 'fname': 'FirstName', 'lname': 'LastName' } } )
The new field name must differ from the existing field name. To specify a in an embedded document, use dot notation.
This operation renames the field nmae to name for all documents in the collection:
db.getCollection('user').updateMany( {}, { $rename: { "add": "Address" } } )
db.getCollection('user').update({}, {$rename:{"name.first":"name.FirstName"}}, false, true);
In the method above false, true are: { upsert:false, multi:true }.To update all your records, You need the multi:true.
Rename a Field in an Embedded Document
db.getCollection('user').update( { _id: 1 }, { $rename: { "name.first": "name.fname" } } )
use link : https://docs.mongodb.com/manual/reference/operator/update/rename/

This nodejs code just do that , as #Felix Yan mentioned former way seems to work just fine , i had some issues with other snipets hope this helps.
This will rename column "oldColumnName" to be "newColumnName" of table "documents"
var MongoClient = require('mongodb').MongoClient
, assert = require('assert');
// Connection URL
//var url = 'mongodb://localhost:27017/myproject';
var url = 'mongodb://myuser:mypwd#myserver.cloud.com:portNumber/databasename';
// Use connect method to connect to the server
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
console.log("Connected successfully to server");
renameDBColumn(db, function() {
db.close();
});
});
//
// This function should be used for renaming a field for all documents
//
var renameDBColumn = function(db, callback) {
// Get the documents collection
console.log("renaming database column of table documents");
//use the former way:
remap = function (x) {
if (x.oldColumnName){
db.collection('documents').update({_id:x._id}, {$set:{"newColumnName":x.oldColumnName}, $unset:{"oldColumnName":1}});
}
}
db.collection('documents').find().forEach(remap);
console.log("db table documents remap successfully!");
}

If you are using MongoMapper, this works:
Access.collection.update( {}, { '$rename' => { 'location' => 'location_info' } }, :multi => true )

Related

Update MongoDb collection and not rewrite it

I have a collection with the following objects:
{
...
"address": {
"addressLine1": "some address"
"city": "some city"
}
}
I need to update all objects to add address.fullName = address.addressLine1 (don't ask why :D) in cases when address exist.
I try to run this script in Robo 3T:
db.getCollection('suppliers').updateMany(
// query
{},
// update
{ $set: { address: { fullName: $address.address1 } } },
// options
{
// update only one document
"multi" : true,
// insert a new document, if no existing document match the query
"upsert" : false
}
);
I have an error
"$address isn't defined"
How should I write a script
To update address.fullName = address.addressLine1
Don't rewrite an entire object and subObject address (important)
Following this example from the MongoDB documentation, your update should be wrapped in an array like this.
db.getCollection('suppliers').updateMany(
{},
[
{
$set: {
'address.fullName': '$address.addressLine1',
},
},
],
{
multi: false,
upsert: false,
}
);
See a working example on MongoDB Playground

MongoDB Shell Script Update all field Names where there is space in field name

Using MongoDB shell script 3.2, how can I update all fields where field names have a space replace those with underscore?
{
"Some Field": "value",
"OtherField" :"Value",
"Another Field" : "Value"
}
update the above document as below
{
"Some_Field": "value",
"OtherField" :"Value",
"Another_Field" : "Value"
}
rename field can be done with something like this
db.CollectionName.update( { _id: 1 }, { $rename: { 'nickname': 'alias', 'cell': 'mobile' } } )
Challenging part here is filter, how to come up with a filter where there is a space in field name
This needs a two-step approach. First, you need a mechanism to get a list of all the keys with a space in your collection. Once you get the list, construct an object that maps those keys to their renamed values. You can then use that object as your $rename operator document. Consider using mapReduce to get the list of keys with spaces.
The following mapReduce operation will populate a separate collection with all the filtered keys as the _id values:
mr = db.runCommand({
"mapreduce": "CollectionName",
"map": function() {
var regxp = /\s/;
for (var key in this) {
if (key.match(regxp)) {
emit(key, null);
}
}
},
"reduce": function() {},
"out": "filtered_keys"
})
To get a list of all the spaced keys, run distinct on the resulting collection:
db[mr.result].distinct("_id")
["Some Field", "Another Field"]
Now given the list above, you can assemble your update document by creating an object that will have its properties set within a loop. Normally your update document will have this structure:
var update = {
"$rename": {
"Some Field": "Some_Field",
"Another Field": "Another_Field"
}
}
Thus
var update = { "$rename": {} };
db[mr.result].distinct("_id").forEach(function (key){
update["$rename"][key] = key.replace(/ /g,"_");
});
which you can then use in your update as
db.CollectionName.update({ }, update, false, true );
Thanks to #chridam that was a excellent query.
Had to make small changes to run query, Full working query.
mr = db.runCommand({
"mapreduce": "MyCollectionName",
"map": function() {
var regxp = /\s/;
for (var key in this) {
if (key.match(regxp)) {
emit(key, null);
}
}
},
"reduce": function() {},
"out": "filtered_keys"
})
db[mr.result].distinct("_id")
var update = { "$rename": {} };
db[mr.result].distinct("_id").forEach(function (key){
update["$rename"][key] = key.replace(/\s+/g, "_");
});
//print(update)
db.MyCollectionName.update({ }, update, false, true );

MongoDb $pull by array index

I was looking to pull(remove) values from my data collection based on array index.
this is how my collection looks.
"experience" : [
"neeee",
"avvvvvvv",
],
I'm looking to remove experience[1]
var update = {
$pull: {
'profile.experience': delet
}
};
this.findByIdAndUpdate(id,update,{ 'new': true},function(err,doc) {
if (err) { console.log(err);
callback(err);
} else if(doc){
callback(null,doc);
}
my delet looks like this: [1]
i was not able to delete (pull) what may be the reason.

Remove multiple documents from array in MongoDB

My document contains an array like:
{
"differentialDiagnosis" : "IART/Flutter",
"explanation" : "The rhythm.",
"fileName" : "A115a JPEG.jpg",
"history" : "1 year old with fussiness",
"interpretationList" : [
{
"interpretations" : [
ObjectId("54efe7c8d6d5ca3d5c580a22"),
ObjectId("54efe80bd6d5ca3d5c580a26")
]
},
{
"interpretations" : [
ObjectId("54efe80bd6d5ca3d5c580a26"),
ObjectId("54efe82ad6d5ca3d5c580a28")
]
}
],
}
and I want to remove all occurrences of ObjectId("54efe80bd6d5ca3d5c580a26"),
but I write a query:
db.ekgs.update({'interpretationList.interpretations':ObjectId("54c09fb3581c4c8c218d1a40")}, {$pull:{ 'interpretationList.$.interpretations':{ ObjectId("54c09fb3581c4c8c218d1a40")}})
This removes only first occurrence of ObjectId("54efe80bd6d5ca3d5c580a26").
The reason your query is only removing the first occurrence is because, as explained in this page in the documentation, "the positional $ operator acts as a placeholder for the first element that matches the query document".
The problem is that it is really tricky to deal with these types of updates with schema having embedded arrays in embedded objects in embedded arrays. In order to get around this problem, if you are able to flatten the schema, then your update becomes much easier. So if instead, your document looked like this:
{
"differentialDiagnosis" : "IART/Flutter",
"explanation" : "The rhythm.",
"fileName" : "A115a JPEG.jpg",
"history" : "1 year old with fussiness",
"interpretations" : [
ObjectId("54efe7c8d6d5ca3d5c580a22"),
ObjectId("54efe80bd6d5ca3d5c580a26"),
ObjectId("54efe82ad6d5ca3d5c580a28")
]
}
Then your query would be as simple as the one below. (Remember to add { "multi": true } as an option if you want to update multiple documents).
db.ekgs.update(
{ "interpretations": ObjectId("54efe80bd6d5ca3d5c580a26")},
{ "$pull": { "interpretations": ObjectId("54efe80bd6d5ca3d5c580a26") }}
);
But I understand that you might not be able to change the schema. In that case, you can try a solution that requires a small script. In the mongo shell, you can use the following bit of JavaScript to do the operation.
// Get cursor with documents requiring updating.
var oid = ObjectId("54efe80bd6d5ca3d5c580a26");
var c = db.ekgs.find({ "interpretationList.interpretations": oid });
// Iterate through cursor, removing oid from each subdocument in interpretationList.
while (c.hasNext()) {
var isModified = false;
var doc = c.next();
var il = doc.interpretationList;
for (var i in il) {
var j = il[i].interpretations.length;
while (j--) {
// If oid to remove is present, remove it from array
// and set flag that the document has been modified.
if (il[i].interpretations[j].str === oid.str) {
il[i].interpretations.splice(j, 1);
isModified = true;
}
}
}
// If modified, update interpretationList for document.
if (isModified) {
db.ekgs.update({ "_id": doc._id }, { "$set": { "interpretationList": il }});
}
}
UPDATE: Example of how it might work using the Node.js driver.
// Get cursor with documents requiring updating.
var oid = new ObjectID("54efe80bd6d5ca3d5c580a26");
var ekgs = db.collection("ekgs");
ekgs.find({ "interpretationList.interpretations": oid },
function(err, c) {
if(err) throw err;
// Iterate through cursor, removing oid from each subdocument in interpretationList.
c.each(function(err, doc) {
if (err) throw err;
// If doc is null then the cursor is exhausted/empty and closed.
if (doc != null) {
var isModified = false;
var il = doc.interpretationList;
for (var i in il) {
var j = il[i].interpretations.length;
while (j--) {
// If oid to remove is present, remove it from array
// and set flag that the document has been modified.
if (il[i].interpretations[j].equals(oid)) {
il[i].interpretations.splice(j, 1);
isModified = true;
}
}
}
// If modified, update interpretationList for document.
if (isModified) {
ekgs.update({ "_id": doc._id },
{ "$set": { "interpretationList": il }},
function(err, res) {
if (err) throw err;
// Callback.
console.log(res);
});
}
}
});
});

How to change the type of a field?

I am trying to change the type of a field from within the mongo shell.
I am doing this...
db.meta.update(
{'fields.properties.default': { $type : 1 }},
{'fields.properties.default': { $type : 2 }}
)
But it's not working!
The only way to change the $type of the data is to perform an update on the data where the data has the correct type.
In this case, it looks like you're trying to change the $type from 1 (double) to 2 (string).
So simply load the document from the DB, perform the cast (new String(x)) and then save the document again.
If you need to do this programmatically and entirely from the shell, you can use the find(...).forEach(function(x) {}) syntax.
In response to the second comment below. Change the field bad from a number to a string in collection foo.
db.foo.find( { 'bad' : { $type : 1 } } ).forEach( function (x) {
x.bad = new String(x.bad); // convert field to string
db.foo.save(x);
});
Convert String field to Integer:
db.db-name.find({field-name: {$exists: true}}).forEach(function(obj) {
obj.field-name = new NumberInt(obj.field-name);
db.db-name.save(obj);
});
Convert Integer field to String:
db.db-name.find({field-name: {$exists: true}}).forEach(function(obj) {
obj.field-name = "" + obj.field-name;
db.db-name.save(obj);
});
Starting Mongo 4.2, db.collection.update() can accept an aggregation pipeline, finally allowing the update of a field based on its own value:
// { a: "45", b: "x" }
// { a: 53, b: "y" }
db.collection.updateMany(
{ a : { $type: 1 } },
[{ $set: { a: { $toString: "$a" } } }]
)
// { a: "45", b: "x" }
// { a: "53", b: "y" }
The first part { a : { $type: 1 } } is the match query:
It filters which documents to update.
In this case, since we want to convert "a" to string when its value is a double, this matches elements for which "a" is of type 1 (double)).
This table provides the code representing the different possible types.
The second part [{ $set: { a: { $toString: "$a" } } }] is the update aggregation pipeline:
Note the squared brackets signifying that this update query uses an aggregation pipeline.
$set is a new aggregation operator (Mongo 4.2) which in this case modifies a field.
This can be simply read as "$set" the value of "a" to "$a" converted "$toString".
What's really new here, is being able in Mongo 4.2 to reference the document itself when updating it: the new value for "a" is based on the existing value of "$a".
Also note "$toString" which is a new aggregation operator introduced in Mongo 4.0.
In case your cast isn't from double to string, you have the choice between different conversion operators introduced in Mongo 4.0 such as $toBool, $toInt, ...
And if there isn't a dedicated converter for your targeted type, you can replace { $toString: "$a" } with a $convert operation: { $convert: { input: "$a", to: 2 } } where the value for to can be found in this table:
db.collection.updateMany(
{ a : { $type: 1 } },
[{ $set: { a: { $convert: { input: "$a", to: 2 } } } }]
)
For string to int conversion.
db.my_collection.find().forEach( function(obj) {
obj.my_value= new NumberInt(obj.my_value);
db.my_collection.save(obj);
});
For string to double conversion.
obj.my_value= parseInt(obj.my_value, 10);
For float:
obj.my_value= parseFloat(obj.my_value);
db.coll.find().forEach(function(data) {
db.coll.update({_id:data._id},{$set:{myfield:parseInt(data.myfield)}});
})
all answers so far use some version of forEach, iterating over all collection elements client-side.
However, you could use MongoDB's server-side processing by using aggregate pipeline and $out stage as :
the $out stage atomically replaces the existing collection with the
new results collection.
example:
db.documents.aggregate([
{
$project: {
_id: 1,
numberField: { $substr: ['$numberField', 0, -1] },
otherField: 1,
differentField: 1,
anotherfield: 1,
needolistAllFieldsHere: 1
},
},
{
$out: 'documents',
},
]);
To convert a field of string type to date field, you would need to iterate the cursor returned by the find() method using the forEach() method, within the loop convert the field to a Date object and then update the field using the $set operator.
Take advantage of using the Bulk API for bulk updates which offer better performance as you will be sending the operations to the server in batches of say 1000 which gives you a better performance as you are not sending every request to the server, just once in every 1000 requests.
The following demonstrates this approach, the first example uses the Bulk API available in MongoDB versions >= 2.6 and < 3.2. It updates all
the documents in the collection by changing all the created_at fields to date fields:
var bulk = db.collection.initializeUnorderedBulkOp(),
counter = 0;
db.collection.find({"created_at": {"$exists": true, "$type": 2 }}).forEach(function (doc) {
var newDate = new Date(doc.created_at);
bulk.find({ "_id": doc._id }).updateOne({
"$set": { "created_at": newDate}
});
counter++;
if (counter % 1000 == 0) {
bulk.execute(); // Execute per 1000 operations and re-initialize every 1000 update statements
bulk = db.collection.initializeUnorderedBulkOp();
}
})
// Clean up remaining operations in queue
if (counter % 1000 != 0) { bulk.execute(); }
The next example applies to the new MongoDB version 3.2 which has since deprecated the Bulk API and provided a newer set of apis using bulkWrite():
var bulkOps = [];
db.collection.find({"created_at": {"$exists": true, "$type": 2 }}).forEach(function (doc) {
var newDate = new Date(doc.created_at);
bulkOps.push(
{
"updateOne": {
"filter": { "_id": doc._id } ,
"update": { "$set": { "created_at": newDate } }
}
}
);
})
db.collection.bulkWrite(bulkOps, { "ordered": true });
To convert int32 to string in mongo without creating an array just add "" to your number :-)
db.foo.find( { 'mynum' : { $type : 16 } } ).forEach( function (x) {
x.mynum = x.mynum + ""; // convert int32 to string
db.foo.save(x);
});
What really helped me to change the type of the object in MondoDB was just this simple line, perhaps mentioned before here...:
db.Users.find({age: {$exists: true}}).forEach(function(obj) {
obj.age = new NumberInt(obj.age);
db.Users.save(obj);
});
Users are my collection and age is the object which had a string instead of an integer (int32).
You can easily convert the string data type to numerical data type.
Don't forget to change collectionName & FieldName.
for ex : CollectionNmae : Users & FieldName : Contactno.
Try this query..
db.collectionName.find().forEach( function (x) {
x.FieldName = parseInt(x.FieldName);
db.collectionName.save(x);
});
I need to change datatype of multiple fields in the collection, so I used the following to make multiple data type changes in the collection of documents. Answer to an old question but may be helpful for others.
db.mycoll.find().forEach(function(obj) {
if (obj.hasOwnProperty('phone')) {
obj.phone = "" + obj.phone; // int or longint to string
}
if (obj.hasOwnProperty('field-name')) {
obj.field-name = new NumberInt(obj.field-name); //string to integer
}
if (obj.hasOwnProperty('cdate')) {
obj.cdate = new ISODate(obj.cdate); //string to Date
}
db.mycoll.save(obj);
});
demo change type of field mid from string to mongo objectId using mongoose
Post.find({}, {mid: 1,_id:1}).exec(function (err, doc) {
doc.map((item, key) => {
Post.findByIdAndUpdate({_id:item._id},{$set:{mid: mongoose.Types.ObjectId(item.mid)}}).exec((err,res)=>{
if(err) throw err;
reply(res);
});
});
});
Mongo ObjectId is just another example of such styles as
Number, string, boolean that hope the answer will help someone else.
I use this script in mongodb console for string to float conversions...
db.documents.find({ 'fwtweaeeba' : {$exists : true}}).forEach( function(obj) {
obj.fwtweaeeba = parseFloat( obj.fwtweaeeba );
db.documents.save(obj); } );
db.documents.find({ 'versions.0.content.fwtweaeeba' : {$exists : true}}).forEach( function(obj) {
obj.versions[0].content.fwtweaeeba = parseFloat( obj.versions[0].content.fwtweaeeba );
db.documents.save(obj); } );
db.documents.find({ 'versions.1.content.fwtweaeeba' : {$exists : true}}).forEach( function(obj) {
obj.versions[1].content.fwtweaeeba = parseFloat( obj.versions[1].content.fwtweaeeba );
db.documents.save(obj); } );
db.documents.find({ 'versions.2.content.fwtweaeeba' : {$exists : true}}).forEach( function(obj) {
obj.versions[2].content.fwtweaeeba = parseFloat( obj.versions[2].content.fwtweaeeba );
db.documents.save(obj); } );
And this one in php)))
foreach($db->documents->find(array("type" => "chair")) as $document){
$db->documents->update(
array('_id' => $document[_id]),
array(
'$set' => array(
'versions.0.content.axdducvoxb' => (float)$document['versions'][0]['content']['axdducvoxb'],
'versions.1.content.axdducvoxb' => (float)$document['versions'][1]['content']['axdducvoxb'],
'versions.2.content.axdducvoxb' => (float)$document['versions'][2]['content']['axdducvoxb'],
'axdducvoxb' => (float)$document['axdducvoxb']
)
),
array('$multi' => true)
);
}
The above answers almost worked but had a few challenges-
Problem 1: db.collection.save no longer works in MongoDB 5.x
For this, I used replaceOne().
Problem 2: new String(x.bad) was giving exponential number
I used "" + x.bad as suggested above.
My version:
let count = 0;
db.user
.find({
custID: {$type: 1},
})
.forEach(function (record) {
count++;
const actualValue = record.custID;
record.custID = "" + record.custID;
console.log(`${count}. Updating User(id:${record._id}) from old id [${actualValue}](${typeof actualValue}) to [${record.custID}](${typeof record.custID})`)
db.user.replaceOne({_id: record._id}, record);
});
And for millions of records, here are the output (for future investigation/reference)-