/* * * Mongo-Hacker * MongoDB Shell Enhancements for Hackers * * Tyler J. Brock - 2013 * * http://tylerbrock.github.com/mongo-hacker * */ if (_isWindows()) { print("\nSorry! MongoDB Shell Enhancements for Hackers isn't compatible with Windows.\n"); } if (typeof db !== 'undefined') { var current_version = parseFloat(db.serverBuildInfo().version).toFixed(2) if (current_version < 2.2) { print("Sorry! MongoDB Shell Enhancements for Hackers is only compatible with Mongo 2.2+\n"); } } mongo_hacker_config = { verbose_shell: true, // additional verbosity index_paranoia: true, // querytime explain enhance_api: true, // additonal api extensions indent: 2, // number of spaces for indent uuid_type: 'default', // 'java', 'c#', 'python' or 'default' banner_message: 'Shell Enhanced by MongoHacker Ver. ', //banner message to show on mongo shell load version: '0.0.3', // current mongo-hacker version show_banner: true, // show mongo-hacker version banner on startup // Shell Color Settings // [, , ] colors: { 'number': [ 'blue', false, false ], 'null': [ 'red', false, false ], 'undefined': [ 'magenta', false, false ], 'objectid': [ 'green', false, false ], 'string': [ 'green', false, false ], 'function': [ 'magenta', false, false ], 'date': [ 'blue', false, false ] } } if (mongo_hacker_config['show_banner']) { print(mongo_hacker_config['banner_message'] + mongo_hacker_config['version']); } function hasDollar(fields){ for (k in fields){ if(k.indexOf('$') !== -1){ return true; }; }; return false; } // Aggregate extension to support alternate API DBCollection.prototype.aggregate = function( ops ){ if(arguments.length >= 1 && (hasDollar(ops) || hasDollar(ops[0]))){ var arr = ops; if (!ops.length) { arr = []; for (var i=0; i 0 ? '-' : '+'; // This is correct ofs += (ofsmin/60).zeroPad(2); ofs += (ofsmin%60).zeroPad(2); } } var isodate = colorize('"' + [year, month, date].join('-') + 'T' + hour +':' + minute + ':' + sec + ofs + '"', "cyan"); return 'ISODate(' + isodate + ')'; }; Array.tojson = function( a , indent , nolint ){ var lineEnding = nolint ? " " : "\n"; if (!indent) indent = ""; if ( nolint ) indent = ""; if (a.length === 0) { return "[ ]"; } var s = "[" + lineEnding; indent += __indent; for ( var i=0; i slowms) { fetched += colorize(time + "ms", "red", true); } else { fetched += colorize(time + "ms", "green", true); } output.push(fetched); } var paranoia = mongo_hacker_config.index_paranoia; if (typeof paranoia !== 'undefined' && paranoia) { var explain = this.clone(); explain._ensureSpecial(); explain._query.$explain = true; explain._limit = Math.abs(n._limit) * -1; var result = explain.next(); var type = result.cursor; if (type !== undefined) { var index_use = "Index["; if (type == "BasicCursor") { index_use += colorize( "none", "red", true); } else { index_use += colorize( result.cursor.substring(12), "green", true ); } index_use += "]"; output.push(index_use); } } if ( this.hasNext() ) { ___it___ = this; output.push("More[" + colorize("true", "green", true) + "]"); } print(output.join(" -- ")); } catch ( e ){ print( e ); } }; tojsonObject = function( x, indent, nolint ) { var lineEnding = nolint ? " " : "\n"; var tabSpace = nolint ? "" : __indent; assert.eq( ( typeof x ) , "object" , "tojsonObject needs object, not [" + ( typeof x ) + "]" ); if (!indent) indent = ""; if ( typeof( x.tojson ) == "function" && x.tojson != tojson ) { return x.tojson(indent,nolint); } if ( x.constructor && typeof( x.constructor.tojson ) == "function" && x.constructor.tojson != tojson ) { return x.constructor.tojson( x, indent , nolint ); } if ( x.toString() == "[object MaxKey]" ) return "{ $maxKey : 1 }"; if ( x.toString() == "[object MinKey]" ) return "{ $minKey : 1 }"; var s = "{" + lineEnding; // push one level of indent indent += tabSpace; var total = 0; for ( var k in x ) total++; if ( total === 0 ) { s += indent + lineEnding; } var keys = x; if ( typeof( x._simpleKeys ) == "function" ) keys = x._simpleKeys(); var num = 1; for ( var key in keys ){ var val = x[key]; if ( val == DB.prototype || val == DBCollection.prototype ) continue; s += indent + colorize("\"" + key + "\"", "yellow") + ": " + tojson( val, indent , nolint ); if (num != total) { s += ","; num++; } s += lineEnding; } // pop one level of indent indent = indent.substring(__indent.length); return s + indent + "}"; }; tojson = function( x, indent , nolint ) { if ( x === null ) return colorize("null", "red", true); if ( x === undefined ) return colorize("undefined", "magenta", true); if ( x.isObjectId ) { return 'ObjectId(' + colorize('"' + x.str + '"', "green", false, true) + ')'; } if (!indent) indent = ""; var s; switch ( typeof x ) { case "string": { s = "\""; for ( var i=0; i= 0 ) { throw "can't have . in field names [" + k + "]" ; } if ( k.indexOf( "$" ) === 0 && ! DBCollection._allowedFields[k] ) { throw "field names cannot start with $ [" + k + "]"; } if ( o[k] !== null && typeof( o[k] ) === "object" ) { this._validateForStorage( o[k] ); } } }; DBQuery.prototype._checkMulti = function(){ if(this._limit > 0 || this._skip > 0){ var ids = this.clone().select({_id: 1}).map(function(o){return o._id;}); this._query['_id'] = {'$in': ids}; return true; } else { return false; } }; DBQuery.prototype.ugly = function(){ this._prettyShell = false; return this; } function runMatch(cmd, args, regexp) { clearRawMongoProgramOutput(); if (args) { run(cmd, args); } else { run(cmd); } var output = rawMongoProgramOutput(); return output.match(regexp); }; function getEnv(env_var) { var env_regex = new RegExp(env_var + '=(.*)'); return runMatch('env', '', env_regex)[1]; }; function getVersion() { var regexp = /version: (\d).(\d).(\d)/; return runMatch('mongo', '--version', regexp).slice(1, 4); }; function isMongos() { return db.isMaster().msg == 'isdbgrid'; }; function getSlowms(){ if(!isMongos()){ return db.getProfilingStatus().slowms; } else { return 100; } }; // Override group because map/reduce style is deprecated DBCollection.prototype.agg_group = function( name, group_field, operation, op_value, filter ) { var ops = []; var group_op = { $group: { _id: '$' + group_field } }; if (filter !== undefined) { ops.push({ '$match': filter }); } group_op['$group'][name] = { }; group_op['$group'][name]['$' + operation] = op_value; ops.push(group_op); return this.aggregate(ops); }; // Function that groups and counts by group after applying filter DBCollection.prototype.gcount = function( group_field, filter ) { return this.agg_group('count', group_field, 'sum', 1, filter); }; // Function that groups and sums sum_field after applying filter DBCollection.prototype.gsum = function( group_field, sum_field, filter ) { return this.agg_group('sum', group_field, 'sum', '$' + sum_field, filter); }; // Function that groups and averages avg_feld after applying filter DBCollection.prototype.gavg = function( group_field, avg_field, filter ) { return this.agg_group('avg', group_field, 'avg', '$' + avg_field, filter); }; // Improve the default prompt with hostname, process type, and version prompt = function() { var serverstatus = db.serverStatus(); var host = serverstatus.host.split('.')[0]; var process = serverstatus.process; var version = db.serverBuildInfo().version; var repl_set = db._adminCommand({"replSetGetStatus": 1}).ok !== 0; var rs_state = ''; if(repl_set) { members = rs.status().members; for(var i = 0; i '; }; // Better show dbs shellHelper.show = function (what) { assert(typeof what == "string"); var args = what.split( /\s+/ ); what = args[0] args = args.splice(1) if (what == "profile") { if (db.system.profile.count() == 0) { print("db.system.profile is empty"); print("Use db.setProfilingLevel(2) will enable profiling"); print("Use db.system.profile.find() to show raw profile entries"); } else { print(); db.system.profile.find({ millis: { $gt: 0} }).sort({ $natural: -1 }).limit(5).forEach( function (x) { print("" + x.op + "\t" + x.ns + " " + x.millis + "ms " + String(x.ts).substring(0, 24)); var l = ""; for ( var z in x ){ if ( z == "op" || z == "ns" || z == "millis" || z == "ts" ) continue; var val = x[z]; var mytype = typeof(val); if ( mytype == "string" || mytype == "number" ) l += z + ":" + val + " "; else if ( mytype == "object" ) l += z + ":" + tojson(val ) + " "; else if ( mytype == "boolean" ) l += z + " "; else l += z + ":" + val + " "; } print( l ); print("\n"); } ) } return ""; } if (what == "users") { db.system.users.find().forEach(printjson); return ""; } if (what == "collections" || what == "tables") { db.getCollectionNames().forEach(function (x) { print(x) }); return ""; } if (what == "dbs" || what == "databases") { var dbs = db.getMongo().getDBs(); var dbinfo = []; var maxNameLength = 0; var maxGbDigits = 0; dbs.databases.forEach(function (x){ var sizeStr = (x.sizeOnDisk / 1024 / 1024 / 1024).toFixed(3); var nameLength = x.name.length; var gbDigits = sizeStr.indexOf("."); if( nameLength > maxNameLength) maxNameLength = nameLength; if( gbDigits > maxGbDigits ) maxGbDigits = gbDigits; dbinfo.push({ name: x.name, size: x.sizeOnDisk, size_str: sizeStr, name_size: nameLength, gb_digits: gbDigits }); }); dbinfo.sort(function (a,b) { a.name - b.name }); dbinfo.forEach(function (db) { var namePadding = maxNameLength - db.name_size; var sizePadding = maxGbDigits - db.gb_digits; var padding = Array(namePadding + sizePadding + 3).join(" "); if (db.size > 1) { print(colorize(db.name, "green", true) + padding + db.size_str + "GB"); } else { print(colorize(db.name, "green", true) + padding + "(empty)"); } }); return ""; } if (what == "log" ) { var n = "global"; if ( args.length > 0 ) n = args[0] var res = db.adminCommand( { getLog : n } ); if ( ! res.ok ) { print("Error while trying to show " + n + " log: " + res.errmsg); return ""; } for ( var i=0; i " + tojson( chunk.max, 0, true ) + " on: " + colorize(chunk.shard, "cyan") + " " + ( chunk.jumbo ? "jumbo " : "" ) ); } ); } else { output( "\t\t\ttoo many chunks to print, use verbose if you want to force print" ); } configDB.tags.find( { ns : coll._id } ).sort( { min : 1 } ).forEach( function( tag ) { output( " tag: " + tag.tag + " " + tojson( tag.min ) + " -> " + tojson( tag.max ) ); } ) } } ) } } ); print( raw ); } function listDbs(){ return db.adminCommand("listDatabases").databases.map(function(d){return d.name}); } this.__proto__.constructor.autocomplete = listDbs;function base64ToHex(base64) { var base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var hexDigits = "0123456789abcdef"; var hex = ""; for (var i = 0; i < 24; ) { var e1 = base64Digits.indexOf(base64[i++]); var e2 = base64Digits.indexOf(base64[i++]); var e3 = base64Digits.indexOf(base64[i++]); var e4 = base64Digits.indexOf(base64[i++]); var c1 = (e1 << 2) | (e2 >> 4); var c2 = ((e2 & 15) << 4) | (e3 >> 2); var c3 = ((e3 & 3) << 6) | e4; hex += hexDigits[c1 >> 4]; hex += hexDigits[c1 & 15]; if (e3 != 64) { hex += hexDigits[c2 >> 4]; hex += hexDigits[c2 & 15]; } if (e4 != 64) { hex += hexDigits[c3 >> 4]; hex += hexDigits[c3 & 15]; } } return hex; } function hexToBase64(hex) { var base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var base64 = ""; var group; for (var i = 0; i < 30; i += 6) { group = parseInt(hex.substr(i, 6), 16); base64 += base64Digits[(group >> 18) & 0x3f]; base64 += base64Digits[(group >> 12) & 0x3f]; base64 += base64Digits[(group >> 6) & 0x3f]; base64 += base64Digits[group & 0x3f]; } group = parseInt(hex.substr(30, 2), 16); base64 += base64Digits[(group >> 2) & 0x3f]; base64 += base64Digits[(group << 4) & 0x3f]; base64 += "=="; return base64; } var platformSpecificUuidModifications = { "java": function (hex) { var msb = hex.substr(0, 16); var lsb = hex.substr(16, 16); msb = msb.substr(14, 2) + msb.substr(12, 2) + msb.substr(10, 2) + msb.substr(8, 2) + msb.substr(6, 2) + msb.substr(4, 2) + msb.substr(2, 2) + msb.substr(0, 2); lsb = lsb.substr(14, 2) + lsb.substr(12, 2) + lsb.substr(10, 2) + lsb.substr(8, 2) + lsb.substr(6, 2) + lsb.substr(4, 2) + lsb.substr(2, 2) + lsb.substr(0, 2); return msb + lsb; }, "c#": function (hex) { return hex.substr(6, 2) + hex.substr(4, 2) + hex.substr(2, 2) + hex.substr(0, 2) + hex.substr(10, 2) + hex.substr(8, 2) + hex.substr(14, 2) + hex.substr(12, 2) + hex.substr(16, 16); }, "python": function (hex) { return hex; }, "default": function (hex) { return hex; } }; function UUID(uuid, type) { var hex = uuid.replace(/[{}-]/g, ""); var typeNum = 4; if (type != undefined) { typeNum = 3; hex = platformSpecificUuidModifications[type](hex); } return new BinData(typeNum, hexToBase64(hex)); } function uuidToString(uuid, uuidType) { var uuidType = uuidType || mongo_hacker_config['uuid_type']; var hex = platformSpecificUuidModifications[uuidType](base64ToHex(uuid.base64())); return hex.substr(0, 8) + '-' + hex.substr(8, 4) + '-' + hex.substr(12, 4) + '-' + hex.substr(16, 4) + '-' + hex.substr(20, 12); } setVerboseShell(true); DBQuery.prototype._prettyShell = true DB.prototype._getExtraInfo = function(action) { if ( typeof _verboseShell === 'undefined' || !_verboseShell ) { __callLastError = true; return; } // explicit w:1 so that replset getLastErrorDefaults aren't used here which would be bad. var startTime = new Date().getTime(); var res = this.getLastErrorCmd(1); if (res) { if (res.err !== undefined && res.err !== null) { // error occurred, display it print(res.err); return; } var info = action + " "; // hack for inserted because res.n is 0 info += action != "Inserted" ? res.n : 1; if (res.n > 0 && res.updatedExisting !== undefined) info += " " + (res.updatedExisting ? "existing" : "new"); info += " record(s) in "; var time = new Date().getTime() - startTime; var slowms = getSlowms(); if (time > slowms) { info += colorize(time + "ms", "red", true); } else { info += colorize(time + "ms", "green", true); } print(info); } };