lutaoact
lutaoact

Reputation: 4429

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

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?

Upvotes: 4

Views: 8609

Answers (2)

jbyler
jbyler

Reputation: 7847

Depending on what's in your script, you might be able to solve your problem by using mongoexport instead of mongo. mongoexport JSON output uses the "strict mode" of extended json, which is valid JSON and can be parsed by other, non-mongo JSON tools.

To use this technique, you will need to convert your query from "shell mode" to "strict mode", pass the database name and collection name as arguments, as well as quote properly for your shell. For example, take this query as written for the mongo shell:

db.myItemsCollection.find({creationDate: {$gte: ISODate("2016-09-29")}})

and convert it into the equivalent shell expression with mongoexport:

mongoexport --jsonArray -d myDbName -c myItemsCollection -q '{"creationDate": {"$gte": {"$date": "2016-09-29T00:00Z"}}}'

This output can be piped to json_pp or jq or another tool that expects valid JSON as input.

Upvotes: 3

famousgarkin
famousgarkin

Reputation: 14116

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

Upvotes: 2

Related Questions