ChatGPT解决这个技术问题 Extra ChatGPT

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!

If anyone is in the same situation I was, having to toString some document's field, here's the little program I've made/used.

u
user6039980

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);
});

Any chance of an example - changing a field type from int to string (or vice versa), from the shell?
in case Int32->String, new String(x.bad) creates collection of Strings with 0-index-item x.bad value. Variant ""+x.bad, described by Simone works as desired - creates String value instead of Int32
The above code is converting the field data from double to array instead of double to string. My actual data was in this format :3.1 whereas simone code is working fine for me
Had a situation where I needed to convert the _id field as well as not conflict with other indexes: db.questions.find({_id:{$type:16}}).forEach( function (x) { db.questions.remove({_id:x._id},true); x._id = ""+x._id; db.questions.save(x); });
.save is not a function facing this error
Y
Yaroslav Admin

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);
});

This is great - do you know how you'd convert a string (think currency like '1.23') to the integer 123? I assume you'd have to parse it as a float or decimal, multiply it by 100, then save it as an integer, but I can't find the right docs to do this. Thanks!
Actually this working is good. But I have an application running with mongoid 2.4.0-stable which has fields such as field: customer_count, type: Integer & a validation as validates_numericality_of :customer_count which was working fine. Now when I am upgrading to mongoid to 3.0.16, when I assign a string value it automatically converts it to 0. without an error. I want to throw an error on wrong data assignment, this behavior turning out strange for me.
I ran this and got the error: Error: could not convert string to integer (shell):1
And if you need to convert string (or "normal" 32-bit integer) to 64-bit integer, use NumberLong as here: db.db-name.find({field-name : {$exists : true}}).forEach( function(obj) { obj.field-name = new NumberLong(obj.field-name); db.db-name.save(obj); } );
This worked for me not only for numbers but for ObjectId data types too.
X
Xavier Guihot

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.update(
  { a : { $type: 1 } },
  [{ $set: { a: { $toString: "$a" } } }],
  { multi: true }
)
// { 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 codes representing the different possible types.

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 codes 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.

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.

Don't forget { multi: true }, otherwise only the first matching document will be updated.

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.update(
  { a : { $type: 1 } },
  [{ $set: { a: { $convert: { input: "$a", to: 2 } } } }],
  { multi: true }
)

db.collection.updateMany( { a : { $type: 1 } }, [{ $set: { a: { $toString: "$a" } } }] ) - the multi : true can be avoided using updateMany
As of 2020, using $convert should be the correct method for this as it should be a lot more efficient (and easier to use to boot).
As of Mongo 4.0 there are shorthand operators: "...In addition to $convert, MongoDB provides the following aggregation operators as shorthand when the default "onError" and "onNull" behavior is acceptable". mongodb.com/docs/manual/reference/operator/aggregation/convert/…
D
David Dehghan

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);

I would recommend also specifying the radix - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Watch out, I tested this with Robomongo, and this resulted in type 1: double. Had to use new NumberInt()
Daniel, ur solution is not okay... david's solution is ok
R
Russell
db.coll.find().forEach(function(data) {
    db.coll.update({_id:data._id},{$set:{myfield:parseInt(data.myfield)}});
})

u
user3616725

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',
         },
      ]);

I don't know why this isn't upvoted more. Row by row operations on large data sets are murder on performance
c
chridam

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 });

Great answer, the bulk method runs about 100x quicker for me even though it still appears to be a synchronous call.
G
Giulio Roggero

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);
});

F
Felipe

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).


A
Amarendra Kumar
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);
});

A
Aakash

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); 
});

P
Pang

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.


u
user3469031

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)
    );


}