Get Mongo _id as string and not ObjectId in find query without aggregation [duplicate] - mongodb

Here I have created a collection with a single document
db.getCollection('example').insert({"example":1});
I have tried to use Projection, and I get back the _id.
db.getCollection('example').find({"example":1},{"_id":1});
{
"_id" : ObjectId("562a6300bbc948a4315f3abc")
}
However, I need the below output as shown below.
id and not _id
ObjectId("562a6300bbc948a4315f3abc") vs "562a6300bbc948a4315f3abc"
{
"id" : "562a6300bbc948a4315f3abc"
}
Although I can process #1 and #2 on my app server(PHP based) to get the desired ouput, I am looking if there is a way to get the expected result on querying from mongo itself

MongoDB 4.0 adds the $convert aggregation operator and the $toString alias which allows you to do exactly that:
db.getCollection('example').aggregate([
{ "$match": { "example":1 } },
{ "$project": { "_id": { "$toString": "$_id" } } }
])
A main usage would most likely be though to use the _id value as a "key" in a document.
db.getCollection('example').insertOne({ "a": 1, "b": 2 })
db.getCollection('example').aggregate([
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": [
[{
"k": { "$toString": "$_id" },
"v": {
"$arrayToObject": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$ne": ["$$this.k", "_id"] }
}
}
}
}]
]
}
}}
])
Which would return:
{
"5b06973e7f859c325db150fd" : { "a" : 1, "b" : 2 }
}
Which clearly shows the string, as does the other example.
Generally though there is usually a way to do "transforms" on the cursor as documents are returned from the server. This is usually a good thing since an ObjectId is a 12-byte binary representation as opposed to a 24 character hex "string" which takes a lot more space.
The shell has a .map() method
db.getCollection('example').find().map(d => Object.assign(d, { _id: d._id.valueOf() }) )
And NodeJS has a Cursor.map() which can do much the same thing:
let cursor = db.collection('example').find()
.map(( _id, ...d }) => ({ _id: _id.toString(), ...d }));
while ( await cursor.hasNext() ) {
let doc = cursor.next();
// do something
})
And the same method exists in other drivers as well ( just not PHP ), or you can just iterate the cursor and transform the content as is more likely the best thing to do.
In fact, whole cursor results can be reduced into a single object with great ease by simply adding to any cursor returning statement, when working in the shell
.toArray().reduce((o,e) => {
var _id = e._id;
delete e._id;
return Object.assign(o, { [_id]: e })
},{ })
Or for full ES6 JavaScript supporting environments like nodejs:
.toArray().reduce((o,({ _id, ...e })) => ({ ...o, [_id]: e }),{ })
Really simple stuff without the complexity of what needs to process in the aggregation framework. And very possible in any language by much the same means.

You need to use the .aggregate() method.
db.getCollection('example').aggregate([ { "$project": { "_id": 0, "id": "$_id" } } ]);
Which yields:
{ "id" : ObjectId("562a67745488a8d831ce2e35") }
or using the .str property.
db.getCollection('example').find({"example":1},{"_id":1}).map(function(doc) {
return {'id': doc._id.str }
})
Which returns:
[ { "id" : "562a67745488a8d831ce2e35" } ]
Well if you are using the PHP driver you can do something like this:
$connection = new MongoClient();
$db = $connection->test;
$col = $db->example;
$cursor = $col->find([], ["_id" => 1]);
foreach($cursor as $doc) { print_r(array("id" => $doc["_id"])); }
Which yields:
Array
(
[id] => MongoId Object
(
[$id] => 562a6c60f850734c0c8b4567
)
)
Or using again the MongoCollection::aggregate method.
$result = $col->aggregate(array(["$project" => ["id" => "$_id", "_id" => 0]]))
Then using the foreach loop:
Array
(
[_id] => MongoId Object
(
[$id] => 562a6c60f850734c0c8b4567
)
)

One simple solution for traversing MongoCursor on PHP side is to use Generators as well as foreach or array_map($function, iterator_to_array($cursor)).
Example:
function map_traversable(callable $mapper, \Traversable $iterator) {
foreach($iterator as $val) {
yield $mapper($val);
}
}
You can meet more at PHP documentation about generators syntax.
So, now you can use/reuse it (or similar implementation) for any propose of "projecting" your data on PHP side with any amount of mapping (just like pipeline does in aggregate) but with fewer iterations amount. And this solution is pretty convenient for OOP in a case of reusing your map functions.
UPD:
Just for your case example below:
$cursor = $db->getCollection('example')->find(["example":1],["_id":1]);
$mapper = function($record) {
return array('id' => (string) $record['_id']); //see \MongoId::__toString()
}
$traversableWithIdAsStringApplied = map_traversable($mapper, $cursor);
//...
now you can proceed with more mappings applied to $traversableWithIdAsStringApplied or use just iterator_to_array for simple array retrieving.

Related

Convert ObjectID to String in mongo Aggregation

I'm in this scenario right now:
I have a collection X:
{
_id:ObjectId('56edbb4d5f084a51131dd4c6'),
userRef:ObjectId('56edbb4d5f084a51131dd4c6'),
serialNumber:'A123123',
...
}
I need to aggregate all documents, grouping them by the userRef + serialNumber, so I'm trying to use concat like this:
$group: {
_id: {
'$concat': ['$userRef','-','$serialNumber']
},
...
So basically in my aggregation in MongoDB, I need to group documents by the concatenation of a ObjectId and a string. However, It seems that $concat only accepts strings as parameters:
uncaught exception: aggregate failed: {
"errmsg" : "exception: $concat only supports strings, not OID",
"code" : 16702,
"ok" : 0
}
Is there a way to convert an ObjectId to a String within an aggregation expression?
EDIT:
This question is related, but I the solution doesn't fit my problem. (Specially because I can't use ObjectId.toString() during the aggregation)
Indeed I couldn't find any ObjectId().toString() operation in Mongo's documentation, but I wonder if there's any tricky thing that can be done in this case.
Now you can try with $toString aggregation which simply
converts ObjectId to string
db.collection.aggregate([
{ "$addFields": {
"userRef": { "$toString": "$userRef" }
}},
{ "$group": {
"_id": { "$concat": ["$userRef", "-", "$serialNumber"] }
}}
])
You can check the output here
I couldn't find a way to do what I wanted, so instead, I created a MapReduce function that, in the end, generated the keys the way I wanted to (concatenating other keys).
At the end, it looked something like this:
db.collection('myCollection').mapReduce(
function() {
emit(
this.userRef.str + '-' + this.serialNumber , {
count: 1,
whateverValue1:this.value1,
whateverValue2:this.value2,
...
}
)
},
function(key, values) {
var reduce = {}
.... my reduce function....
return reduce
}, {
query: {
...filters_here....
},
out: 'name_of_output_collection'
}
);
You can simply use $toString to apply $concat in aggregation on ObjectIDs in the following way -
$group: {
'_id': {
'$concat': [
{ '$toString' : '$userRef' },
'-',
{ '$toString' : '$serialNumber'}
]
},
}
I think you may try to resolve it by using an Array which contains both fields:
{$project:{newkey:['$userRef','$serialNumber']},{$match:{newkey:{$in:filterArray}}}}
this may match the data with both fields to the filter. Please notice that the data in the newkey array should have the same data type with the filterArray elements.
You can use $substr https://docs.mongodb.com/manual/reference/operator/aggregation/substr/#exp._S_substr to cast any object to string before $concat.
This is a sample of code that's working for me.
group_id_i['_id'] = {
'$concat' => [
{ '$substr' => [ {'$year' => '$t'}, 0, -1] }, '-',
{ '$substr' => [ {'$month' => '$t'}, 0, -1] }, '-',
{ '$substr' => [ {'$dayOfMonth' => '$t'}, 0, -1] }
]
}
Where t is DateTime field, this aggregation returns data like so.
{
"_id" => "28-9-2016",
"i" => 2
}

Replace a word from a string

I have mongodb documents with a field like this:
Image : http://static14.com/p/Inc.5-Black-Sandals-5131-2713231-7-zoom.jpg
How can I replace the zoom part in the string value with some other text in order to get:
Image : http://static14.com/p/Inc.5-Black-Sandals-5131-2713231-7-product2.jpg
You could use mongo's forEach() cursor method to do an atomic update with the $set operator :
db.collection.find({}).snapshot().forEach(function(doc) {
var updated_url = doc.Image.replace('zoom', 'product2');
db.collection.update(
{"_id": doc._id},
{ "$set": { "Image": updated_url } }
);
});
Given a very large collection to update, you could speed up things a little bit with bulkWrite and restructure your update operations to be sent in bulk as:
var ops = [];
db.collection.find({}).snapshot().forEach(function(doc) {
ops.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": { "$set": { "Image": doc.Image.replace('zoom', 'product2') } }
}
});
if ( ops.length === 500 ) {
db.collection.bulkWrite(ops);
ops = [];
}
})
if ( ops.length > 0 )
db.collection.bulkWrite(ops);
db.myCollection.update({image: 'http://static14.com/p/Inc.5-Black-Sandals-5131-2713231-7-zoom.jpg'}, {$set: {image : 'http://static14.com/p/Inc.5-Black-Sandals-5131-2713231-7-product2.jpg'}})
If you need to do this multiple times to multiple documents, you need to iterate them with a function. See here: MongoDB: Updating documents using data from the same document
Nowadays,
starting Mongo 4.2, db.collection.updateMany (alias of db.collection.update) can accept an aggregation pipeline, finally allowing the update of a field based on its own value.
starting Mongo 4.4, the new aggregation operator $replaceOne makes it very easy to replace part of a string.
// { "Image" : "http://static14.com/p/Inc.5-Black-Sandals-5131-2713231-7-zoom.jpg" }
// { "Image" : "http://static14.com/p/Inc.5-Black-Sandals-5131-2713231-7-boom.jpg" }
db.collection.updateMany(
{ "Image": { $regex: /zoom/ } },
[{
$set: { "Image": {
$replaceOne: { input: "$Image", find: "zoom", replacement: "product2" }
}}
}]
)
// { "Image" : "http://static14.com/p/Inc.5-Black-Sandals-5131-2713231-7-product2.jpg" }
// { "Image" : "http://static14.com/p/Inc.5-Black-Sandals-5131-2713231-7-boom.jpg" }
The first part ({ "Image": { $regex: /zoom/ } }) is just there to make the query faster by filtering which documents to update (the ones containing "zoom")
The second part ($set: { "Image": {...) is the update aggregation pipeline (note the squared brackets signifying the use of an aggregation pipeline):
$set is a new aggregation operator (Mongo 4.2) which in this case replaces the value of a field.
The new value is computed with the new $replaceOne operator. Note how Image is modified directly based on the its own value ($Image).

MongoDb MapReduce on child array

I've searched the internet long and hard but can't find a solution to this problem. Whilst there are lots of Map reduce examples, i'm getting confused because my document has a property which is an array of objects.
I'm pretty sure this should be easy for someone with experience but i'm a noob at the minute.
I have a document which looks roughly like this
{
_id:guid,
clientId:guid,
reference:'abc123'
items:
[
{ _id:guid, category:'A', length:100, active:true },
{ _id:guid, category:'B', length:150, active:true },
{ _id:guid, category:'A', length:10, active:false },
{ _id:guid, category:'A', length:111, active:true },
]
}
and I want to produce this output
dateFromIdGuid(day) category countOfItems countOfActive sumOfLength
I'd like to keep the data in this format to reduce the number of write operations (there are already over 1000 writes to this collection per second and rising)
This is driving me insane so any help would be very much appreciated.
Thanks.
If you are talking about extracting a timestamp and reducing that to a discrete day from a GUID, then MongoDB is not going to be of much help to you there. You would need an external language implementation that would support such a function and implement an external mapReduce process such as with Hadoop.
It makes me wonder though if we are in fact talking about a GUID or whether you actually mean an ObjectID which would be the default value for the _id field of your document unless this has been specifically overridden to have a GUID in there.
Even if that is not true, you would be helped by adding a "timestamp" field of some sort to your document and using the correct BSON Date object type as shown below:
{
_id:guid,
"timestamp": ISODate("2014-05-27T00:00:00Z")
"clientId":guid,
"reference":'abc123'
"items":
[
{ _id:guid, category:'A', length:100, active:true },
{ _id:guid, category:'B', length:150, active:true },
{ _id:guid, category:'A', length:10, active:false },
{ _id:guid, category:'A', length:111, active:true },
]
}
This allows you to use the MongoDB aggregation framework as it can operate on Date objects of this type in order to break down the results to discrete days:
db.collection.aggregate([
{ "$unwind": "$items" },
{ "$group": {
"_id": {
"day": { "$dayOfYear": "$timestamp" },
"category": "$items.category"
},
"countOfItems": { "$sum": 1 },
"countOfActive": {
"$sum": {
"$cond": [
"$items.active",
1,
0
]
}
},
"sumOfLength": { "$sum": "$items.length" }
}}
])
That not only gives you the results in the fastest way MongoDB can do it but that "timestamp" value is also useful for filtering queries within date ranges which is something you cannot easily do from other values.
Also there is a way in the JavaScript available to MongoDB mapReduce that allows you to get the date from an ObejctId. This runs slower than the aggregation framework though:
db.collection.mapReduce(
function() {
var date = this._id.getTimestamp();
items.forEach(function(item) {
var day =
"" + date.getFullyear() +
"" + ( date.getMonth() + 1 ) +
"" + date.getDate();
emit(
{
day: day,
category: item.category
},
{
countOfItems: 1,
countOfActive: ( item.active ) ? 1 : 0,
sumOfLength: item.length
}
);
});
},
function( key, values ) {
var reduced = {
countOfItems: 0,
countOfActive: 0,
sumOfLength: 0
};
values.forEach(function(value) {
for ( var k in value ) {
reduced[k] += value[k];
}
});
return reduced;
},
{
"out": { "inline": 1 }
}
)
That basically does the same thing where the mapper breaks apart the array and provides grouping keys while the reducer just sums up the values from the mapper. So even if you had to extract from GUID's that gives you a basic layout for a mapper and reducer in a language such as Java when using Hadoop.
Take a look at the aggregate and mapReduce manual pages for more information on options you can apply.

How to count document elements inside a mongo collection with php?

I have the following structure of a mongo document:
{
"_id": ObjectId("4fba2558a0787e53320027eb"),
"replies": {
"0": {
"email": ObjectId("4fb89a181b3129fe2d000000"),
"sentDate": "2012-05-21T11: 22: 01.418Z"
}
"1": {
"email": ObjectId("4fb89a181b3129fe2d000000"),
"sentDate": "2012-05-21T11: 22: 01.418Z"
}
"2" ....
}
}
How do I count all the replies from all the documents in the collection?
Thank you!
In the following answer, I'm working with a simple data set with five replies across the collection:
> db.foo.find()
{ "_id" : ObjectId("4fba6b0c7c32e336fc6fd7d2"), "replies" : [ 1, 2, 3 ] }
{ "_id" : ObjectId("4fba6b157c32e336fc6fd7d3"), "replies" : [ 1, 2 ] }
Since we're not simply counting documents, db.collection.count() won't help us here. We'll need to resort to MapReduce to scan each document and aggregate the reply array lengths. Consider the following:
db.foo.mapReduce(
function() { emit('totalReplies', { count: this.replies.length }); },
function(key, values) {
var result = { count: 0 };
values.forEach(function(value) {
result.count += value.count;
});
return result;
},
{ out: { inline: 1 }}
);
The map function (first argument) runs across the entire collection and emits the number of replies in each document under a constant key. Mongo will then consider all emitted values and run the reduce function (second argument) a number of times to consolidate (literally reduce) the result. Hopefully the code here is straightforward. If you're new to map/reduce, one caveat is that the reduce method must be capable of processing its own output. This is explained in detail in the MapReduce docs linked above.
Note: if your collection is quite large, you may have to use another output mode (e.g. collection output); however, inline works well for small data sets.
Lastly, if you're using MongoDB 2.1+, we can take advantage of the Aggregation Framework to avoid writing JS functions and make this even easier:
db.foo.aggregate(
{ $project: { replies: 1 }},
{ $unwind: "$replies" },
{ $group: {
_id: "result",
totalReplies: { $sum: 1 }
}}
);
Three things are happening here. First, we tell Mongo that we're interested in the replies field. Secondly, we want to unwind the array so that we can iterate over all elements across the fields in our projection. Lastly, we'll tally up results under a "result" bucket (any constant will do), adding 1 to the totalReplies result for each iteration. Executing this query will yield the following result:
{
"result" : [{
"_id" : "result",
"totalReplies" : 5
}],
"ok" : 1
}
Although I wrote the above answers with respect to the Mongo client, you should have no trouble translating them to PHP. You'll need to use MongoDB::command() to run either MapReduce or aggregation queries, as the PHP driver currently has no helper methods for either. There's currently a MapReduce example in the PHP docs, and you can reference this Google group post for executing an aggregation query through the same method.
I haven't checked your code, might work as well. I've did the following and it just works:
$replies = $db->command(
array(
"distinct" => "foo",
"key" => "replies"
)
);
$all = count($replies['values']);
I've did it again using the group command of the PHP Mongo Driver. It's similar to a MapReduce command.
$keys = array("replies.type" => 1); //keys for group by
$initial = array("count" => 0); //initial value of the counter
$reduce = "function (obj, prev) { prev.count += obj.replies.length; }";
$condition = array('replies' => array('$exists' => true), 'replies.type' => 'follow');
$g = $db->foo->group($keys, $initial, $reduce, $condition);
echo $g['count'];
Thanks jmikola for giving links to Mongo.
JSON should be
{
"_id": ObjectId("4fba2558a0787e53320027eb"),
"replies":[
{
0: {
"email": ObjectId("4fb89a181b3129fe2d000000"),
"sentDate": "2012-05-21T11: 22: 01.418Z"
},
1: {
"email": ObjectId("4fb89a181b3129fe2d000000"),
"sentDate": "2012-05-21T11: 22: 01.418Z"
},
2: {....}
]
}

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