Created
November 2, 2017 16:06
-
-
Save terakilobyte/6a850c0b9d2dbb5bb65d864a48891f1b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* | |
* Mongo-Hacker | |
* MongoDB Shell Enhancements for Hackers | |
* | |
* Tyler J. Brock - 2013 - 2016 | |
* | |
* http://tylerbrock.github.com/mongo-hacker | |
* | |
* | |
* | |
* Edits made to make it play well with changes to the aggregation framework in MongoDB 3.6 | |
* Remove all aggregation additions. They're no longer needed and now get in the way. | |
* | |
*/ | |
mongo_hacker_config = { | |
verbose_shell: true, // additional verbosity | |
index_paranoia: false, // querytime explain | |
enhance_api: true, // additonal api extensions | |
indent: 2, // number of spaces for indent | |
sort_keys: false, // sort the keys in documents when displayed | |
uuid_type: 'default', // 'java', 'c#', 'python' or 'default' | |
banner_message: 'Mongo-Hacker ', // banner message | |
version: '0.0.14', // current mongo-hacker version | |
show_banner: true, // show mongo-hacker version banner on startup | |
windows_warning: true, // show warning banner for windows | |
force_color: false, // force color highlighting for Windows users | |
count_deltas: false, // "count documents" shows deltas with previous counts | |
column_separator: '→', // separator used when printing padded/aligned columns | |
value_separator: '/', // separator used when merging padded/aligned values | |
dbref: { | |
extended_info: true, // enable more informations on DBRef | |
plain: false, // print DBRef as plain JSON object | |
db_if_differs: false // include $db only if is different than current one | |
}, | |
// Shell Color Settings | |
// Colors available: red, green, yellow, blue, magenta, cyan | |
colors: { | |
'key': { color: 'gray' }, | |
'number': { color: 'red' }, | |
'boolean': { color: 'blue', bright: true }, | |
'null': { color: 'red', bright: true }, | |
'undefined': { color: 'magenta', bright: true }, | |
'objectid': { color: 'yellow', underline: true }, | |
'string': { color: 'green' }, | |
'binData': { color: 'green', bright: true }, | |
'function': { color: 'magenta' }, | |
'date': { color: 'blue' }, | |
'uuid': { color: 'cyan' }, | |
'databaseNames': { color: 'green', bright: true }, | |
'collectionNames': { color: 'blue', bright: true } | |
} | |
} | |
if (mongo_hacker_config['show_banner']) { | |
print(mongo_hacker_config['banner_message'] + mongo_hacker_config['version']); | |
} | |
if (_isWindows() && mongo_hacker_config['windows_warning']) { | |
print("\nMongoDB Shell Enhancements for Hackers does not support color highlighting in "); | |
print("the default Windows Command Prompt. If you are using an alternative console "); | |
print("such as ConEmu (https://github.com/Maximus5/ConEmu) you may wish to try enabling"); | |
print("highlighting in your mongo_hacker config by setting:"); | |
print("\n\tforce_color: true\n"); | |
print("You can hide this startup warning by setting:"); | |
print("\n\twindows_warning: false\n"); | |
} | |
if (typeof db !== 'undefined') { | |
var current_version = parseFloat(db.serverBuildInfo().version).toFixed(2) | |
if (current_version < 2.4) { | |
print("Sorry! MongoDB Shell Enhancements for Hackers is only compatible with Mongo 2.4+\n"); | |
} | |
} | |
// Helper method for determining if parameter has dollar signs | |
function hasDollar(fields){ | |
for (k in fields){ | |
if(k.indexOf('$') !== -1){ | |
return true; | |
}; | |
}; | |
return false; | |
} | |
//---------------------------------------------------------------------------- | |
// API Additions | |
//---------------------------------------------------------------------------- | |
DBQuery.prototype.fields = function( fields ) { | |
this._fields = fields; | |
return this; | |
}; | |
DBQuery.prototype.select = function( fields ){ | |
this._fields = fields; | |
return this; | |
}; | |
DBQuery.prototype.one = function(){ | |
return this.limit(1)[0]; | |
}; | |
DBQuery.prototype.first = function(field){ | |
var field = field || "$natural"; | |
var sortBy = {}; | |
sortBy[field] = 1; | |
return this.sort(sortBy).one(); | |
} | |
DBQuery.prototype.reverse = function( field ){ | |
var field = field || "$natural"; | |
var sortBy = {}; | |
sortBy[field] = -1; | |
return this.sort(sortBy); | |
} | |
DBQuery.prototype.last = function( field ){ | |
var field = field || "$natural"; | |
return this.reverse(field).one(); | |
} | |
DB.prototype.rename = function(newName) { | |
if(newName == this.getName() || newName.length === 0) | |
return; | |
this.copyDatabase(this.getName(), newName, "localhost"); | |
this.dropDatabase(); | |
db = this.getSiblingDB(newName); | |
}; | |
DB.prototype.indexStats = function(collectionFilter, details){ | |
details = details || false; | |
collectionNames = db.getCollectionNames().filter(function (collectionName) { | |
// exclude "system" collections from "count" operation | |
if (!collectionFilter) { | |
return !collectionName.startsWith('system.'); | |
} | |
if (collectionName == collectionFilter) { | |
return !collectionName.startsWith('system.'); | |
} | |
}); | |
documentIndexes = collectionNames.map(function (collectionName) { | |
var count = db.getCollection(collectionName).count(); | |
return (count.commify() + " document(s)"); | |
}); | |
columnSeparator = mongo_hacker_config['column_separator']; | |
assert(collectionNames.length == documentIndexes.length); | |
maxKeyLength = maxLength(collectionNames); | |
maxValueLength = maxLength(documentIndexes); | |
for (i = 0; i < collectionNames.length; i++) { | |
print( | |
colorize(collectionNames[i].pad(maxKeyLength, true), mongo_hacker_config['colors']['collectionNames']) | |
+ " " + columnSeparator + " " | |
+ documentIndexes[i].pad(maxValueLength) | |
); | |
var stats = db.getCollection(collectionNames[i]).stats(); | |
var totalIndexSize = (Math.round((stats.totalIndexSize / 1024 / 1024) * 10) / 10) + " MB"; | |
var indexNames = []; | |
var indexSizes = []; | |
for (indexName in stats.indexSizes) { | |
indexSizes.push((Math.round((stats.indexSizes[indexName] / 1024 / 1024) * 10) / 10) + " MB"); | |
indexNames.push(" " + indexName); | |
} | |
maxIndexKeyLength = maxLength(indexNames); | |
maxIndexValueLength = maxLength(indexSizes); | |
print( | |
colorize("totalIndexSize".pad(maxKeyLength, true), mongo_hacker_config['colors']['string']) | |
+ " " + columnSeparator + " " | |
+ colorize(totalIndexSize.pad(maxValueLength), mongo_hacker_config['colors']['number']) | |
); | |
if (details) { | |
for (var j = 0; j < indexSizes.length; j++) { | |
print( | |
colorize("" + indexNames[j].pad(maxIndexKeyLength, true), mongo_hacker_config['colors']['string']) | |
+ " " + columnSeparator + " " | |
+ colorize(indexSizes[j].pad(maxIndexValueLength), mongo_hacker_config['colors']['binData']) | |
); | |
}; | |
} | |
} | |
return ""; | |
} | |
Mongo.prototype.getDatabaseNames = function() { | |
// this API addition gives us the following convenience function: | |
// | |
// db.getMongo().getDatabaseNames() | |
// | |
// which is similar in use to: | |
// | |
// db.getCollectionNames() | |
// | |
// mongo-hacker FTW :-) | |
return this.getDBs().databases.reduce(function(names, db) { | |
return names.concat(db.name); | |
}, []); | |
} | |
//---------------------------------------------------------------------------- | |
// API Modifications (additions and changes) | |
//---------------------------------------------------------------------------- | |
// Add upsert method which has upsert set as true and multi as false | |
DBQuery.prototype.upsert = function( upsert ){ | |
assert( upsert , "need an upsert object" ); | |
this._validate(upsert); | |
this._db._initExtraInfo(); | |
this._mongo.update( this._ns , this._query , upsert , true , false ); | |
this._db._getExtraInfo("Upserted"); | |
}; | |
// Updates are always multi and never an upsert | |
DBQuery.prototype.update = function( update ){ | |
assert( update , "need an update object" ); | |
this._checkMulti(); | |
this._validate(update); | |
this._db._initExtraInfo(); | |
this._mongo.update( this._ns , this._query , update , false , true ); | |
this._db._getExtraInfo("Updated"); | |
}; | |
// Replace one document | |
DBQuery.prototype.replace = function( replacement ){ | |
assert( replacement , "need an update object" ); | |
this._validate(replacement); | |
this._db._initExtraInfo(); | |
this._mongo.update( this._ns , this._query , replacement , false , false ); | |
this._db._getExtraInfo("Replaced"); | |
}; | |
// Remove is always multi | |
DBQuery.prototype.remove = function(){ | |
for ( var k in this._query ){ | |
if ( k == "_id" && typeof( this._query[k] ) == "undefined" ){ | |
throw "can't have _id set to undefined in a remove expression"; | |
} | |
} | |
this._checkMulti(); | |
this._db._initExtraInfo(); | |
this._mongo.remove( this._ns , this._query , false ); | |
this._db._getExtraInfo("Removed"); | |
}; | |
//---------------------------------------------------------------------------- | |
// Full Text Search | |
//---------------------------------------------------------------------------- | |
DBQuery.prototype.textSearch = function( search ) { | |
var text = { | |
text: this._collection.getName(), | |
search: search, | |
filter: this._query, | |
project: this._fields, | |
limit: this._limit | |
} | |
var result = this._db.runCommand( text ); | |
return result.results; | |
}; | |
function listDbs(){ | |
return db.adminCommand("listDatabases").databases.map(function(d){return d.name}); | |
} | |
this.__proto__.constructor.autocomplete = listDbs;shellHelper.find = function (query) { | |
assert(typeof query == "string"); | |
var args = query.split( /\s+/ ); | |
query = args[0]; | |
args = args.splice(1); | |
if (query !== "") { | |
var regexp = new RegExp(query, "i"); | |
var result = db.runCommand("listCommands"); | |
for (var command in result.commands) { | |
var commandObj = result.commands[command]; | |
var help = commandObj.help; | |
if (commandObj.help.indexOf('\n') != -1 ) { | |
help = commandObj.help.substring(0, commandObj.help.lastIndexOf('\n')); | |
} | |
if (regexp.test(command) || regexp.test(help)) { | |
var numSpaces = 30 - command.length; | |
print(colorize(command, {color: 'green'}), Array(numSpaces).join(" "), "-", help); | |
} | |
} | |
} | |
}; | |
//---------------------------------------------------------------------------- | |
// Color Functions | |
//---------------------------------------------------------------------------- | |
__ansi = { | |
csi: String.fromCharCode(0x1B) + '[', | |
reset: '0', | |
text_prop: 'm', | |
foreground: '3', | |
bright: '1', | |
underline: '4', | |
colors: { | |
black: '0', | |
red: '1', | |
green: '2', | |
yellow: '3', | |
blue: '4', | |
magenta: '5', | |
cyan: '6', | |
gray: '7', | |
} | |
}; | |
function controlCode( parameters ) { | |
if ( parameters === undefined ) { | |
parameters = ""; | |
} | |
else if (typeof(parameters) == 'object' && (parameters instanceof Array)) { | |
parameters = parameters.join(';'); | |
} | |
return __ansi.csi + String(parameters) + String(__ansi.text_prop); | |
}; | |
function applyColorCode( string, properties, nocolor ) { | |
// Allow global __colorize default to be overriden | |
var applyColor = (null == nocolor) ? __colorize : !nocolor; | |
return applyColor ? controlCode(properties) + String(string) + controlCode() : String(string); | |
}; | |
function colorize( string, color, nocolor ) { | |
var params = []; | |
var code = __ansi.foreground + __ansi.colors[color.color]; | |
params.push(code); | |
if ( color.bright === true ) params.push(__ansi.bright); | |
if ( color.underline === true ) params.push(__ansi.underline); | |
return applyColorCode( string, params, nocolor ); | |
}; | |
function colorizeAll( strings, color, nocolor ) { | |
return strings.map(function(string) { | |
return colorize( string, color, nocolor ); | |
}); | |
}; | |
__indent = Array(mongo_hacker_config.indent + 1).join(' '); | |
__colorize = (_isWindows() && !mongo_hacker_config['force_color']) ? false : true; | |
ObjectId.prototype.toString = function() { | |
return this.str; | |
}; | |
ObjectId.prototype.tojson = function(indent, nolint) { | |
return tojson(this); | |
}; | |
var dateToJson = Date.prototype.tojson; | |
Date.prototype.tojson = function(indent, nolint, nocolor) { | |
var isoDateString = dateToJson.call(this); | |
var dateString = isoDateString.substring(8, isoDateString.length-1); | |
var isodate = colorize(dateString, mongo_hacker_config.colors.date, nocolor); | |
return 'ISODate(' + isodate + ')'; | |
}; | |
Array.tojson = function( a , indent , nolint, nocolor ){ | |
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<a.length; i++){ | |
s += indent + tojson( a[i], indent , nolint, nocolor ); | |
if ( i < a.length - 1 ){ | |
s += "," + lineEnding; | |
} | |
} | |
if ( a.length === 0 ) { | |
s += indent; | |
} | |
indent = indent.substring(__indent.length); | |
s += lineEnding+indent+"]"; | |
return s; | |
}; | |
function surround(name, inside) { | |
return [name, '(', inside, ')'].join(''); | |
} | |
Number.prototype.commify = function() { | |
// http://stackoverflow.com/questions/2901102 | |
return this.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); | |
}; | |
NumberLong.prototype.tojson = function(indent, nolint, nocolor) { | |
var color = mongo_hacker_config.colors.number; | |
var output = colorize('"' + this.toString().match(/-?\d+/)[0] + '"', color, nocolor); | |
return surround('NumberLong', output); | |
}; | |
NumberInt.prototype.tojson = function(indent, nolint, nocolor) { | |
var color = mongo_hacker_config.colors.number; | |
var output = colorize('"' + this.toString().match(/-?\d+/)[0] + '"', color, nocolor); | |
return surround('NumberInt', output); | |
}; | |
BinData.prototype.tojson = function(indent , nolint, nocolor) { | |
var uuidType = mongo_hacker_config.uuid_type; | |
var uuidColor = mongo_hacker_config.colors.uuid; | |
var binDataColor = mongo_hacker_config.colors.binData; | |
if (this.subtype() === 3) { | |
var output = colorize('"' + uuidToString(this) + '"', uuidColor, nocolor) + ', ' | |
output += colorize('"' + uuidType + '"', uuidColor) | |
return surround('UUID', output); | |
} else if (this.subtype() === 4) { | |
var output = colorize('"' + uuidToString(this, "default") + '"', uuidColor, nocolor) + ')' | |
return surround('UUID', output); | |
} else { | |
var output = colorize(this.subtype(), {color: 'red'}) + ', ' | |
output += colorize('"' + this.base64() + '"', binDataColor, nocolor) | |
return surround('BinData', output); | |
} | |
}; | |
function isInArray(array, value) { | |
return array.indexOf(value) > -1; | |
} | |
tojsonObject = function( x, indent, nolint, nocolor, sort_keys ) { | |
var lineEnding = nolint ? " " : "\n"; | |
var tabSpace = nolint ? "" : __indent; | |
var sortKeys = (null == sort_keys) ? mongo_hacker_config.sort_keys : sort_keys; | |
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, nocolor ); | |
} | |
if ( x.constructor && typeof( x.constructor.tojson ) == "function" && x.constructor.tojson != tojson ) { | |
return x.constructor.tojson( x, indent , nolint, nocolor ); | |
} | |
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; | |
var keylist=[]; | |
for(var key in keys) | |
keylist.push(key); | |
if ( sortKeys ) { | |
// Disable sorting if this object looks like an index spec | |
if ( (isInArray(keylist, "v") && isInArray(keylist, "key") && isInArray(keylist, "name") && isInArray(keylist, "ns")) ) { | |
sortKeys = false; | |
} else { | |
keylist.sort(); | |
} | |
} | |
for ( var i=0; i<keylist.length; i++) { | |
var key=keylist[i]; | |
var val = x[key]; | |
if ( val == DB.prototype || val == DBCollection.prototype ) | |
continue; | |
var color = mongo_hacker_config.colors.key; | |
s += indent + colorize("\"" + key + "\"", color, nocolor) + ": " + tojson( val, indent , nolint, nocolor, sortKeys ); | |
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, nocolor, sort_keys ) { | |
var sortKeys = (null == sort_keys) ? mongo_hacker_config.sort_keys : sort_keys; | |
if ( x === null ) | |
return colorize("null", mongo_hacker_config.colors['null'], nocolor); | |
if ( x === undefined ) | |
return colorize("undefined", mongo_hacker_config.colors['undefined'], nocolor); | |
if ( x.isObjectId ) { | |
var color = mongo_hacker_config.colors['objectid']; | |
return surround('ObjectId', colorize('"' + x.str + '"', color, nocolor)); | |
} | |
if (!indent) | |
indent = ""; | |
var s; | |
switch ( typeof x ) { | |
case "string": { | |
s = "\""; | |
for ( var i=0; i<x.length; i++ ){ | |
switch (x[i]){ | |
case '"': s += '\\"'; break; | |
case '\\': s += '\\\\'; break; | |
case '\b': s += '\\b'; break; | |
case '\f': s += '\\f'; break; | |
case '\n': s += '\\n'; break; | |
case '\r': s += '\\r'; break; | |
case '\t': s += '\\t'; break; | |
default: { | |
var code = x.charCodeAt(i); | |
if (code < 0x20){ | |
s += (code < 0x10 ? '\\u000' : '\\u00') + code.toString(16); | |
} else { | |
s += x[i]; | |
} | |
} | |
} | |
} | |
s += "\""; | |
return colorize(s, mongo_hacker_config.colors.string, nocolor); | |
} | |
case "number": | |
return colorize(x, mongo_hacker_config.colors.number, nocolor); | |
case "boolean": | |
return colorize("" + x, mongo_hacker_config.colors['boolean'], nocolor); | |
case "object": { | |
s = tojsonObject( x, indent , nolint, nocolor, sortKeys ); | |
if ( ( nolint === null || nolint === true ) && s.length < 80 && ( indent === null || indent.length === 0 ) ){ | |
s = s.replace( /[\s\r\n ]+/gm , " " ); | |
} | |
return s; | |
} | |
case "function": | |
return colorize(x.toString(), mongo_hacker_config.colors['function'], nocolor); | |
default: | |
throw "tojson can't handle type " + ( typeof x ); | |
} | |
}; | |
DBQuery.prototype._validate = function( o ){ | |
var firstKey = null; | |
for (var k in o) { firstKey = k; break; } | |
if (firstKey !== null && firstKey[0] == '$') { | |
// for mods we only validate partially, for example keys may have dots | |
this._validateObject( o ); | |
} else { | |
// we're basically inserting a brand new object, do full validation | |
this._validateForStorage( o ); | |
} | |
}; | |
DBQuery.prototype._validateObject = function( o ){ | |
if (typeof(o) != "object") | |
throw "attempted to save a " + typeof(o) + " value. document expected."; | |
if ( o._ensureSpecial && o._checkModify ) | |
throw "can't save a DBQuery object"; | |
}; | |
DBQuery.prototype._validateForStorage = function( o ){ | |
this._validateObject( o ); | |
for ( var k in o ){ | |
if ( k.indexOf( "." ) >= 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; | |
} | |
DB.prototype.shutdownServer = function(opts) { | |
if( "admin" != this._name ){ | |
return "shutdown command only works with the admin database; try 'use admin'"; | |
} | |
cmd = {"shutdown" : 1}; | |
opts = opts || {}; | |
for (var o in opts) { | |
cmd[o] = opts[o]; | |
} | |
try { | |
var res = this.runCommand(cmd); | |
if( res ) | |
throw "shutdownServer failed: " + res.errmsg; | |
throw "shutdownServer failed"; | |
} | |
catch ( e ){ | |
assert( e.message.indexOf( "error doing query: failed" ) >= 0 , "unexpected error: " + tojson( e ) ); | |
print( "server should be down..." ); | |
} | |
} | |
// helper function to format delta counts | |
function delta(currentCount, previousCount) { | |
var delta = Number(currentCount - previousCount); | |
var formatted_delta; | |
if (isNaN(delta)) { | |
formatted_delta = colorize("(first count)", { color: 'blue' }); | |
} else if (delta == 0) { | |
formatted_delta = colorize("(=)", { color: 'blue' }); | |
} else if (delta > 0) { | |
formatted_delta = colorize("(+" + delta.commify() + ")", { color: 'green' }); | |
} else if (delta < 0) { | |
formatted_delta = colorize("(" + delta.commify() + ")", { color: 'red' }); | |
} else { | |
formatted_delta = (delta + " not supported"); | |
} | |
return formatted_delta; | |
} | |
// global variable (to ensure "persistence" of document counts) | |
shellHelper.previousDocumentCount = {}; | |
// "count documents", a bit akin to "show collections" | |
shellHelper.count = function (what) { | |
assert(typeof what == "string"); | |
var args = what.split( /\s+/ ); | |
what = args[0] | |
args = args.splice(1) | |
if (what == "collections" || what == "tables") { | |
databaseNames = db.getMongo().getDatabaseNames(); | |
collectionCounts = databaseNames.map(function (databaseName) { | |
var count = db.getMongo().getDB(databaseName).getCollectionNames().length; | |
return (count.commify() + " collection(s)"); | |
}); | |
databaseNames = colorizeAll(databaseNames, mongo_hacker_config['colors']['databaseNames']); | |
printPaddedColumns(databaseNames, collectionCounts); | |
return ""; | |
} | |
if (what == "documents" || what == "docs") { | |
collectionNames = db.getCollectionNames().filter(function (collectionName) { | |
// exclude "system" collections from "count" operation | |
return !collectionName.startsWith('system.'); | |
}); | |
documentCounts = collectionNames.map(function (collectionName) { | |
var count = db.getCollection(collectionName).count(); | |
return (count.commify() + " document(s)"); | |
}); | |
deltaCounts = collectionNames.map(function (collectionName) { | |
// retrieve the previous document count for this collection | |
var previous = shellHelper.previousDocumentCount[collectionName]; | |
// determine the current document count for this collection | |
var current = db.getCollection(collectionName).count(); | |
// update the stored document count for this collection | |
shellHelper.previousDocumentCount[collectionName] = current; | |
// format the delta since last count | |
return delta(current, previous); | |
}); | |
collectionNames = colorizeAll(collectionNames, mongo_hacker_config['colors']['collectionNames']); | |
if (mongo_hacker_config['count_deltas']) { | |
printPaddedColumns(collectionNames, documentCounts, deltaCounts); | |
} else { | |
printPaddedColumns(collectionNames, documentCounts); | |
} | |
return ""; | |
} | |
if (what == "index" || what == "indexes") { | |
db.indexStats("", 1); | |
return "" | |
} | |
throw "don't know how to count [" + what + "]"; | |
} | |
DBRef.prototype.__toString = DBRef.prototype.toString; | |
DBRef.prototype.toString = function () { | |
var org = this.__toString(); | |
var config = mongo_hacker_config.dbref; | |
if (!config.extended_info) { | |
return org; | |
} | |
var additional = {}; | |
var o = this; | |
for (var p in o) { | |
if (typeof o[p] === 'function') { | |
continue; | |
} | |
if (!config.plain && (p === '$ref' || p === '$id')) { | |
continue; | |
} | |
if (config.db_if_differs && p === '$db' && o[p] === db.getName()) { | |
continue; | |
} | |
additional[p] = o[p]; | |
} | |
if (config.plain) { | |
return tojsonObject(additional, undefined, true); | |
} | |
return Object.keys(additional).length | |
? (org.slice(0, -1) + ", " + tojsonObject(additional, undefined, true) + ")") | |
: org; | |
}; | |
//---------------------------------------------------------------------------- | |
// findAndModify Helper | |
//---------------------------------------------------------------------------- | |
DBQuery.prototype._findAndModify = function( options ) { | |
var findAndModify = { | |
'findandmodify': this._collection.getName(), | |
'query': this._query, | |
'new': true, | |
'fields': this._fields, | |
'upsert': this._upsert || false, | |
'sort': this._query.orderby || {}, | |
}; | |
for (var key in options){ | |
findAndModify[key] = options[key]; | |
}; | |
var result = this._db.runCommand( findAndModify ); | |
if ( ! result.ok ){ | |
throw "findAndModifyFailed failed: " + tojson( result ); | |
}; | |
return result.value; | |
}; | |
//---------------------------------------------------------------------------- | |
// findAndModify Terminal Variants | |
//---------------------------------------------------------------------------- | |
DBQuery.prototype.updateAndGet = function( update ) { | |
return this._findAndModify({ 'update': update }); | |
}; | |
DBQuery.prototype.getAndUpdate = function( update ) { | |
return this._findAndModify({ 'update': update, 'new': false }); | |
}; | |
DBQuery.prototype.replaceAndGet = function( replacement ) { | |
return this._findAndModify({ 'update': replacement }); | |
}; | |
DBQuery.prototype.getAndReplace = function( replacement ) { | |
return this._findAndModify({ 'update': replacement, 'new': false }); | |
}; | |
DBQuery.prototype.getAndRemove = function() { | |
return this._findAndModify({ 'remove': true }) | |
};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; | |
} | |
}; | |
function maxLength(listOfNames) { | |
return listOfNames.reduce(function(maxLength, name) { | |
return (name.length > maxLength) ? name.length : maxLength ; | |
}, 0); | |
}; | |
function printPaddedColumns() { | |
var columnWidths = Array.prototype.map.call( | |
arguments, | |
function(column) { | |
return maxLength(column); | |
} | |
); | |
for (i = 0; i < arguments[0].length; i++) { | |
row = ""; | |
for (j = 0; j < arguments.length; j++) { | |
row += arguments[j][i].toString().pad(columnWidths[j], (j == 0)); | |
if (j < (arguments.length - 1)) { | |
separator = ((j == 0) ? | |
mongo_hacker_config['column_separator'] : | |
mongo_hacker_config['value_separator'] | |
); | |
row += " " + separator + " "; | |
} | |
} | |
print(row); | |
} | |
return null; | |
}; | |
function runOnDbs(regexp, callback) { | |
var originalDb = db.getName(); | |
db.getMongo().getDBs().databases.filter(function(db) { | |
return db.name.match(regexp); } | |
).forEach(function(dbEntry) { | |
db = db.getSiblingDB(dbEntry.name); | |
callback(db); | |
}); | |
db = db.getSiblingDB(originalDb); | |
} | |
// 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 enterpriseModulePresent = db.serverBuildInfo().modules.indexOf("enterprise"); | |
var enterprise = enterpriseModulePresent !== -1 ? "ENTERPRISE" : ""; | |
var repl_set = db._adminCommand({"replSetGetStatus": 1}).ok !== 0; | |
var rs_state = ''; | |
if(repl_set) { | |
var status = rs.status(); | |
var members = status.members; | |
var rs_name = status.set; | |
for(var i = 0; i<members.length; i++){ | |
if(members[i].self === true){ | |
rs_state = '[' + members[i].stateStr + ':' + rs_name + ']'; | |
} | |
}; | |
} | |
var state = isMongos() ? '[mongos]' : rs_state; | |
return host + '(' + process + '-' + version + ' ' + enterprise + ')' + state + ' ' + db + '> '; | |
}; | |
// Begin PS Hack | |
// Copyright (c) 2016 Andrew Young | |
// SPDX-License-Identifier: MIT | |
function printTableRow(row, columnWidths) { | |
var pad = function(x, i) { | |
return x.pad(i).slice(-i).toString(); | |
}; | |
var line = ""; | |
for(c in columnWidths) { | |
line = line + pad(row[c], columnWidths[c]) + " "; | |
} | |
print(line); | |
} | |
function printTable(headers, rows) { | |
// printTable(headers: Array[String], rows: Array[Array[String]]) | |
// Find column widths | |
var columnWidths = []; | |
for(c in headers) { | |
columnWidths[c] = headers[c].length; | |
} | |
for(r in rows) { | |
var row = rows[r]; | |
for (c in headers) { | |
row[c] = row[c].toString(); | |
columnWidths[c] = Math.max(columnWidths[c], row[c].length); | |
} | |
} | |
// Print Table | |
var hr = function(l) { | |
return new Array(l + 1).join("-"); | |
}; | |
var hrs = []; | |
for(c in columnWidths) { | |
hrs.push(hr(columnWidths[c])); | |
} | |
printTableRow(headers, columnWidths); | |
printTableRow(hrs, columnWidths); | |
rows.forEach(function(row) { printTableRow(row, columnWidths); }); | |
} | |
function getConnections() { | |
return db.currentOp(true).inprog.filter(function(x) { return x.connectionId; } ); | |
} | |
shellHelper.ps = function() { | |
var headers = [ | |
"Connection", | |
"ID", | |
"Client", | |
"S", | |
"Active", | |
"Time", | |
"WaitLock", | |
"Operation", | |
"Plan", | |
"Namespace" | |
]; | |
var rows = []; | |
var connections = getConnections(); | |
connections.forEach(function(op) { | |
var connectionId = op.connectionId; | |
var opId = op.opid || ""; | |
var client = op.client || op.client_s || ""; | |
var isMongos = op.client_s ? "S" : ""; | |
var active = op.active ? "Active" : "Idle"; | |
var time = op.secs_running || ""; | |
var waitingForLock = op.waitingForLock ? "Yes" : "No"; | |
var opName = op.op || ""; | |
var plan = op.planSummary || ""; | |
var ns = op.ns || ""; | |
rows.push([ | |
connectionId, | |
opId, | |
client, | |
isMongos, | |
active, | |
time, | |
waitingForLock, | |
opName, | |
plan, | |
ns | |
]); | |
}); | |
printTable(headers, rows); | |
}; | |
shellHelper.kill = function(opId) { | |
return db.killOp(opId); | |
} | |
// End PS Hack | |
//---------------------------------------------------------------------------- | |
// Randomise API | |
//---------------------------------------------------------------------------- | |
function randomWord(length, words, seed){ | |
/* Return a random word(s). | |
length: length of each word (default is 5 letters). | |
words: number of words (default is 1 word). | |
seed: a word to be planted randomly amongst the word(s), good for search. (optional) | |
*/ | |
words = typeof words !== 'undefined' ? words : 1; | |
length = typeof length !== 'undefined' ? length : 5; | |
var seedOn = typeof seed !== 'undefined'; | |
var text = ""; | |
var possible ="abcdefghijklmnopqrstuvwxyz"; | |
var firstword = true; | |
for (var j=0; j < words; j++){ | |
var word = ""; | |
for (var i=0; i < length; i++){ | |
word += possible.charAt(Random.randInt(possible.length)); | |
} | |
/* Plant a seeded word */ | |
if (seedOn == true){ | |
var randomBool = Random.rand() >= 0.8; | |
if (randomBool == true){ | |
if (firstword == true){ text = seed; firstword = false;} | |
else {text += " " + seed;} | |
seedOn = false; | |
} | |
} | |
if (firstword == true){ text = word; firstword = false;} | |
else {text += " " + word;} | |
} | |
return text; | |
}; | |
function randomNumber(max){ | |
/* Return a random number | |
max: highest random number (default is 100). | |
*/ | |
max = typeof max !== 'undefined' ? max : 100; | |
return Random.randInt(max); | |
}; | |
function randomDate(start, end){ | |
/* Return a random date between start and end values. | |
start: Date(), default 2 years ago. | |
end: Date(), default today. | |
*/ | |
end = typeof end !== 'undefined' ? end : new Date(); | |
if (typeof start === 'undefined') { | |
start = new Date(end.getTime()); | |
start.setYear(start.getFullYear() - 2); | |
} | |
return new Date(start.getTime() + Random.randInt(end.getTime() - start.getTime())); | |
}; | |
sh.getRecentMigrations = function () { | |
var configDB = db.getSiblingDB("config"); | |
var yesterday = new Date( new Date() - 24 * 60 * 60 * 1000 ); | |
var result = []; | |
result = result.concat(configDB.changelog.aggregate( [ | |
{ $match : { time : { $gt : yesterday }, what : "moveChunk.from", "details.errmsg" : { | |
"$exists" : false } } }, | |
{ $group : { _id: { msg: "$details.errmsg" }, count : { "$sum":1 } } }, | |
{ $project : { _id : { $ifNull: [ "$_id.msg", "Success" ] }, count : "$count" } } | |
] ).result); | |
result = result.concat(configDB.changelog.aggregate( [ | |
{ $match : { time : { $gt : yesterday }, what : "moveChunk.from", "details.errmsg" : { | |
"$exists" : true } } }, | |
{ $group : { _id: { msg: "$details.errmsg", from : "$details.from", to: "$details.to" }, | |
count : { "$sum":1 } } }, | |
{ $project : { _id : "$_id.msg" , from : "$_id.from", to : "$_id.to" , count : "$count" } } | |
] ).result); | |
return result; | |
}; | |
printShardingStatus = function( configDB , verbose ){ | |
if (configDB === undefined) | |
configDB = db.getSisterDB('config') | |
var version = configDB.getCollection( "version" ).findOne(); | |
if ( version == null ){ | |
print( "printShardingStatus: this db does not have sharding enabled. be sure you are", | |
"connecting to a mongos from the shell and not to a mongod." ); | |
return; | |
} | |
var raw = ""; | |
var output = function(s){ | |
raw += s + "\n"; | |
} | |
output( "--- Sharding Status --- " ); | |
output( " sharding version: " + tojson( configDB.getCollection( "version" ).findOne(), " " ) ); | |
output( " shards:" ); | |
configDB.shards.find().sort( { _id : 1 } ).forEach( | |
function(z){ | |
output( " " + tojsononeline( z ) ); | |
} | |
); | |
// All of the balancer information functions below depend on a connection to a liveDB | |
// This isn't normally a problem, but can cause issues in testing and running with --nodb | |
if ( typeof db !== "undefined" ) { | |
output( " balancer:" ); | |
//Is the balancer currently enabled | |
output( "\tCurrently enabled: " + ( sh.getBalancerState() ? | |
colorize("yes", {color: "cyan"}) : | |
colorize("no", {color: "red"}) ) ); | |
//Is the balancer currently active | |
output( "\tCurrently running: " + | |
colorize(( sh.isBalancerRunning() ? "yes" : "no" ), {color: "gray"}) ); | |
//Output details of the current balancer round | |
var balLock = sh.getBalancerLockDetails(); | |
if ( balLock ) { | |
output( "\t\tBalancer lock taken at " + | |
colorize(balLock.when, {color: "gray"}) + " by " + | |
colorize(balLock.who, {color: "cyan"}) ); | |
} | |
//Output the balancer window | |
var balSettings = sh.getBalancerWindow(); | |
if ( balSettings ) { | |
output( "\t\tBalancer active window is set between " + | |
colorize(balSettings.start, {color: "gray"}) + " and " + | |
colorize(balSettings.stop, {color: "gray"}) + " server local time"); | |
} | |
//Output the list of active migrations | |
var activeMigrations = sh.getActiveMigrations(); | |
if (activeMigrations.length > 0 ){ | |
output("\tCollections with active migrations: "); | |
activeMigrations.forEach( function(migration){ | |
output("\t\t" + | |
colorize(migration._id, {color: "cyan"})+ " started at " + | |
colorize(migration.when, {color: "gray"}) ); | |
}); | |
} | |
// Actionlog and version checking only works on 2.7 and greater | |
var versionHasActionlog = false; | |
var metaDataVersion = configDB.getCollection("version").findOne().currentVersion; | |
if ( metaDataVersion > 5 ) { | |
versionHasActionlog = true; | |
} | |
if ( metaDataVersion == 5 ) { | |
var verArray = db.serverBuildInfo().versionArray; | |
if (verArray[0] == 2 && verArray[1] > 6){ | |
versionHasActionlog = true; | |
} | |
} | |
if ( versionHasActionlog ) { | |
//Review config.actionlog for errors | |
var actionReport = sh.getRecentFailedRounds(); | |
//Always print the number of failed rounds | |
output( "\tFailed balancer rounds in last 5 attempts: " + | |
colorize(actionReport.count, {color: "red"}) ); | |
//Only print the errors if there are any | |
if ( actionReport.count > 0 ){ | |
output( "\tLast reported error: " + actionReport.lastErr ); | |
output( "\tTime of Reported error: " + actionReport.lastTime ); | |
} | |
output("\tMigration Results for the last 24 hours: "); | |
var migrations = sh.getRecentMigrations(); | |
if(migrations.length > 0) { | |
migrations.forEach( function(x) { | |
if (x._id === "Success"){ | |
output( "\t\t" + colorize(x.count, {color: "gray"}) + | |
" : "+ colorize(x._id, {color: "cyan"})); | |
} else { | |
output( "\t\t" + colorize(x.count, {color: "gray"}) + | |
" : Failed with error '" + colorize(x._id, {color: "red"}) + | |
"', from " + x.from + " to " + x.to ); | |
} | |
}); | |
} else { | |
output( "\t\tNo recent migrations"); | |
} | |
} | |
} | |
output( " databases:" ); | |
configDB.databases.find().sort( { name : 1 } ).forEach( | |
function(db){ | |
output( " " + tojsononeline(db,"",true) ); | |
if (db.partitioned){ | |
configDB.collections.find( { _id : new RegExp( "^" + | |
RegExp.escape(db._id) + "\\." ) } ). | |
sort( { _id : 1 } ).forEach( function( coll ){ | |
if ( coll.dropped == false ){ | |
output( " " + coll._id ); | |
output( " shard key: " + tojson(coll.key, 0, true) ); | |
output( " chunks:" ); | |
res = configDB.chunks.aggregate( | |
{ "$match": { ns: coll._id } }, | |
{ "$group": { _id: "$shard", nChunks: { "$sum": 1 } } }, | |
{ "$project" : { _id : 0 , shard : "$_id" , nChunks : "$nChunks" } }, | |
{ "$sort" : { shard : 1 } } | |
).result | |
var totalChunks = 0; | |
res.forEach( function(z){ | |
totalChunks += z.nChunks; | |
output( " " + z.shard + ": " + z.nChunks ); | |
} ) | |
if ( totalChunks < 20 || verbose ){ | |
configDB.chunks.find( { "ns" : coll._id } ).sort( { min : 1 } ).forEach( | |
function(chunk){ | |
output( " " + | |
tojson( chunk.min, 0, true) + " -> " + | |
tojson( chunk.max, 0, true ) + | |
" on: " + colorize(chunk.shard, {color: 'cyan'}) + " " + tojson( chunk.lastmod ) + " " + | |
( 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 ); | |
} | |
// 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.getUsers().forEach(printjson); | |
return ""; | |
} | |
if (what == "roles") { | |
db.getRoles({showBuiltinRoles: true}).forEach(printjson); | |
return ""; | |
} | |
if (what == "collections" || what == "tables") { | |
var collectionNames = db.getCollectionNames(); | |
var collectionSizes = collectionNames.map(function (name) { | |
var stats = db.getCollection(name).stats(); | |
var size = (stats.size / 1024 / 1024).toFixed(3); | |
return (size + "MB"); | |
}); | |
var collectionStorageSizes = collectionNames.map(function (name) { | |
var stats = db.getCollection(name).stats(); | |
var storageSize = (stats.storageSize / 1024 / 1024).toFixed(3); | |
return (storageSize + "MB"); | |
}); | |
collectionNames = colorizeAll(collectionNames, mongo_hacker_config['colors']['collectionNames']); | |
printPaddedColumns(collectionNames, collectionSizes, collectionStorageSizes); | |
return ""; | |
} | |
if (what == "dbs" || what == "databases") { | |
var databases = db.getMongo().getDBs().databases.sort(function(a, b) { return a.name.localeCompare(b.name) }); | |
var databaseNames = databases.map(function(db) { | |
return db.name; | |
}); | |
var databaseSizes = databases.map(function(db) { | |
var sizeInGigaBytes = (db.sizeOnDisk / 1024 / 1024 / 1024).toFixed(3); | |
return (db.sizeOnDisk > 1) ? (sizeInGigaBytes + "GB") : "(empty)"; | |
}); | |
databaseNames = colorizeAll(databaseNames, mongo_hacker_config['colors']['databaseNames']); | |
printPaddedColumns(databaseNames, databaseSizes); | |
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<res.log.length; i++){ | |
print( res.log[i] ) | |
} | |
return "" | |
} | |
if (what == "logs" ) { | |
var res = db.adminCommand( { getLog : "*" } ) | |
if ( ! res.ok ) { | |
print("Error while trying to show logs: " + res.errmsg); | |
return ""; | |
} | |
for ( var i=0; i<res.names.length; i++){ | |
print( res.names[i] ) | |
} | |
return "" | |
} | |
if (what == "startupWarnings" ) { | |
var dbDeclared, ex; | |
try { | |
// !!db essentially casts db to a boolean | |
// Will throw a reference exception if db hasn't been declared. | |
dbDeclared = !!db; | |
} catch (ex) { | |
dbDeclared = false; | |
} | |
if (dbDeclared) { | |
var res = db.adminCommand( { getLog : "startupWarnings" } ); | |
if ( res.ok ) { | |
if (res.log.length == 0) { | |
return ""; | |
} | |
print( "Server has startup warnings: " ); | |
for ( var i=0; i<res.log.length; i++){ | |
print( res.log[i] ) | |
} | |
return ""; | |
} else if (res.errmsg == "no such cmd: getLog" ) { | |
// Don't print if the command is not available | |
return ""; | |
} else if (res.code == 13 /*unauthorized*/ || | |
res.errmsg == "unauthorized" || | |
res.errmsg == "need to login") { | |
// Don't print if startupWarnings command failed due to auth | |
return ""; | |
} else { | |
print("Error while trying to show server startup warnings: " + res.errmsg); | |
return ""; | |
} | |
} else { | |
print("Cannot show startupWarnings, \"db\" is not set"); | |
return ""; | |
} | |
} | |
throw "don't know how to show [" + what + "]"; | |
} | |
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", { color: 'red', bright: true }); | |
} else { | |
info += colorize(time + "ms", { color: 'green', bright: true }); | |
} | |
print(info); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment