mongodb printjson will output ObjectId to result, which can not be used for JSON.parse

lutaoact picture lutaoact · Oct 20, 2014 · Viewed 7.2k times · Source

I run my mongo shell script like this:

mongo --quiet myscript.js > /tmp/my.json

I use printjson in myscript.js. mongodb printjson will output ObjectId to my.json, like this:

"_id" : ObjectId("5444a932ca62bbcba14a1082")

I read some source code from mongo shell. printjson will run this code for the ObjectId object.

> x._id.tojson
function (){
    return this.toString();
}

after mongo version 2.2, ObjectId("507c7f79bcf86cd7994f6c0e").toString() will return the following string:

ObjectId("507c7f79bcf86cd7994f6c0e")

It's not I want. I use ObjectId("507c7f79bcf86cd7994f6c0e").valueOf().

This will return the following string:

507c7f79bcf86cd7994f6c0e

finally, I add one line in myscript.js:

ObjectId.prototype.toString = function() { return '"' + this.valueOf() + '"'; }

I solved my problem. but I don't like change the original behavior of the toString(). Is there any better solutions?

Answer

famousgarkin picture famousgarkin · Oct 20, 2014

Agreed, modifying the framework functions like this is a dangerous idea. This changes the ObjectID.toString behavior for all the other code as well, not just printjson.

Since the MongoDB aggregation framework doesn't allow for arbitrary JavaScript to be used, we cannot just do something like db.test.aggregate({$project: {_id: '$_id.valueOf()'}}) or give it a custom transformation function to use.

The MongoDB map-reduce framework can use custom JavaScript functions and may be able to achieve this, but it's rather elaborate, slow and it's use seems to be generally discouraged.

Your best option is to include this ID transformation as part of your script in some form. Either just transform the document before printing ad-hoc as needed:

var cursor = db.test.find();
while (cursor.hasNext()) {
    var doc = cursor.next();
    doc._id = doc._id.valueOf();
    printjson(doc);
}

Or get more sophisticated and wrap it up in your own print function, or replace or decorate the original printjson function, e.g. modify the document just for printing and roll back the change:

var theirPrintjson = printjson;
var printjson = function(doc) {
    var id = doc._id;
    doc._id = doc._id.valueOf();
    theirPrintjson(doc);
    doc._id = id;
};