1 // db.js
  2 
  3 if ( typeof DB == "undefined" ){                     
  4     DB = function( mongo , name ){
  5         this._mongo = mongo;
  6         this._name = name;
  7     }
  8 }
  9 
 10 DB.prototype.getMongo = function(){
 11     assert( this._mongo , "why no mongo!" );
 12     return this._mongo;
 13 }
 14 
 15 DB.prototype.getSiblingDB = function( name ){
 16     return this.getMongo().getDB( name );
 17 }
 18 
 19 DB.prototype.getSisterDB = DB.prototype.getSiblingDB;
 20 
 21 DB.prototype.getName = function(){
 22     return this._name;
 23 }
 24 
 25 DB.prototype.stats = function(){
 26     return this.runCommand( { dbstats : 1 } );
 27 }
 28 
 29 DB.prototype.getCollection = function( name ){
 30     return new DBCollection( this._mongo , this , name , this._name + "." + name );
 31 }
 32 
 33 DB.prototype.commandHelp = function( name ){
 34     var c = {};
 35     c[name] = 1;
 36     c.help = true;
 37     var res = this.runCommand( c );
 38     if ( ! res.ok )
 39         throw res.errmsg;
 40     return res.help;
 41 }
 42 
 43 DB.prototype.runCommand = function( obj ){
 44     if ( typeof( obj ) == "string" ){
 45         var n = {};
 46         n[obj] = 1;
 47         obj = n;
 48     }
 49     return this.getCollection( "$cmd" ).findOne( obj );
 50 }
 51 
 52 DB.prototype._dbCommand = DB.prototype.runCommand;
 53 
 54 DB.prototype.adminCommand = function( obj ){
 55     if ( this._name == "admin" )
 56         return this.runCommand( obj );
 57     return this.getSiblingDB( "admin" ).runCommand( obj );
 58 }
 59 
 60 DB.prototype._adminCommand = DB.prototype.adminCommand; // alias old name
 61 
 62 DB.prototype.addUser = function( username , pass, readOnly ){
 63     readOnly = readOnly || false;
 64     var c = this.getCollection( "system.users" );
 65     
 66     var u = c.findOne( { user : username } ) || { user : username };
 67     u.readOnly = readOnly;
 68     u.pwd = hex_md5( username + ":mongo:" + pass );
 69     print( tojson( u ) );
 70 
 71     c.save( u );
 72 }
 73 
 74 DB.prototype.removeUser = function( username ){
 75     this.getCollection( "system.users" ).remove( { user : username } );
 76 }
 77 
 78 DB.prototype.__pwHash = function( nonce, username, pass ) {
 79     return hex_md5( nonce + username + hex_md5( username + ":mongo:" + pass ) );
 80 }
 81 
 82 DB.prototype.auth = function( username , pass ){
 83     var n = this.runCommand( { getnonce : 1 } );
 84 
 85     var a = this.runCommand( 
 86         { 
 87             authenticate : 1 , 
 88             user : username , 
 89             nonce : n.nonce , 
 90             key : this.__pwHash( n.nonce, username, pass )
 91         }
 92     );
 93 
 94     return a.ok;
 95 }
 96 
 97 /**
 98   Create a new collection in the database.  Normally, collection creation is automatic.  You would
 99    use this function if you wish to specify special options on creation.
100 
101    If the collection already exists, no action occurs.
102    
103    <p>Options:</p>
104    <ul>
105    	<li>
106      size: desired initial extent size for the collection.  Must be <= 1000000000.
107            for fixed size (capped) collections, this size is the total/max size of the 
108            collection.
109     </li>
110     <li>
111      capped: if true, this is a capped collection (where old data rolls out).
112     </li>
113     <li> max: maximum number of objects if capped (optional).</li>
114     </ul>
115 
116    <p>Example: </p>
117    
118    <code>db.createCollection("movies", { size: 10 * 1024 * 1024, capped:true } );</code>
119  
120  * @param {String} name Name of new collection to create 
121  * @param {Object} options Object with options for call.  Options are listed above.
122  * @return SOMETHING_FIXME
123 */
124 DB.prototype.createCollection = function(name, opt) {
125     var options = opt || {};
126     var cmd = { create: name, capped: options.capped, size: options.size, max: options.max };
127     var res = this._dbCommand(cmd);
128     return res;
129 }
130 
131 /**
132  * @deprecated use getProfilingStatus
133  *  Returns the current profiling level of this database
134  *  @return SOMETHING_FIXME or null on error
135  */
136 DB.prototype.getProfilingLevel  = function() { 
137     var res = this._dbCommand( { profile: -1 } );
138     return res ? res.was : null;
139 }
140 
141 /**
142  *  @return the current profiling status
143  *  example { was : 0, slowms : 100 }
144  *  @return SOMETHING_FIXME or null on error
145  */
146 DB.prototype.getProfilingStatus  = function() { 
147     var res = this._dbCommand( { profile: -1 } );
148     if ( ! res.ok )
149         throw "profile command failed: " + tojson( res );
150     delete res.ok
151     return res;
152 }
153 
154 
155 /**
156   Erase the entire database.  (!)
157  
158  * @return Object returned has member ok set to true if operation succeeds, false otherwise.
159 */
160 DB.prototype.dropDatabase = function() { 	
161     if ( arguments.length )
162         throw "dropDatabase doesn't take arguments";
163     return this._dbCommand( { dropDatabase: 1 } );
164 }
165 
166 
167 DB.prototype.shutdownServer = function() { 
168     if( "admin" != this._name ){
169 	return "shutdown command only works with the admin database; try 'use admin'";
170     }
171 
172     try {
173         var res = this._dbCommand("shutdown");
174 	if( res ) 
175 	    throw "shutdownServer failed: " + res.errmsg;
176 	throw "shutdownServer failed";
177     }
178     catch ( e ){
179         assert( tojson( e ).indexOf( "error doing query: failed" ) >= 0 , "unexpected error: " + tojson( e ) );
180         print( "server should be down..." );
181     }
182 }
183 
184 /**
185   Clone database on another server to here.
186   <p>
187   Generally, you should dropDatabase() first as otherwise the cloned information will MERGE 
188   into whatever data is already present in this database.  (That is however a valid way to use 
189   clone if you are trying to do something intentionally, such as union three non-overlapping
190   databases into one.)
191   <p>
192   This is a low level administrative function will is not typically used.
193 
194  * @param {String} from Where to clone from (dbhostname[:port]).  May not be this database 
195                    (self) as you cannot clone to yourself.
196  * @return Object returned has member ok set to true if operation succeeds, false otherwise.
197  * See also: db.copyDatabase()
198 */
199 DB.prototype.cloneDatabase = function(from) { 
200     assert( isString(from) && from.length );
201     //this.resetIndexCache();
202     return this._dbCommand( { clone: from } );
203 }
204 
205 
206 /**
207  Clone collection on another server to here.
208  <p>
209  Generally, you should drop() first as otherwise the cloned information will MERGE 
210  into whatever data is already present in this collection.  (That is however a valid way to use 
211  clone if you are trying to do something intentionally, such as union three non-overlapping
212  collections into one.)
213  <p>
214  This is a low level administrative function is not typically used.
215  
216  * @param {String} from mongod instance from which to clnoe (dbhostname:port).  May
217  not be this mongod instance, as clone from self is not allowed.
218  * @param {String} collection name of collection to clone.
219  * @param {Object} query query specifying which elements of collection are to be cloned.
220  * @return Object returned has member ok set to true if operation succeeds, false otherwise.
221  * See also: db.cloneDatabase()
222  */
223 DB.prototype.cloneCollection = function(from, collection, query) { 
224     assert( isString(from) && from.length );
225     assert( isString(collection) && collection.length );
226     collection = this._name + "." + collection;
227     query = query || {};
228     //this.resetIndexCache();
229     return this._dbCommand( { cloneCollection:collection, from:from, query:query } );
230 }
231 
232 
233 /**
234   Copy database from one server or name to another server or name.
235 
236   Generally, you should dropDatabase() first as otherwise the copied information will MERGE 
237   into whatever data is already present in this database (and you will get duplicate objects 
238   in collections potentially.)
239 
240   For security reasons this function only works when executed on the "admin" db.  However, 
241   if you have access to said db, you can copy any database from one place to another.
242 
243   This method provides a way to "rename" a database by copying it to a new db name and 
244   location.  Additionally, it effectively provides a repair facility.
245 
246   * @param {String} fromdb database name from which to copy.
247   * @param {String} todb database name to copy to.
248   * @param {String} fromhost hostname of the database (and optionally, ":port") from which to 
249                     copy the data.  default if unspecified is to copy from self.
250   * @return Object returned has member ok set to true if operation succeeds, false otherwise.
251   * See also: db.clone()
252 */
253 DB.prototype.copyDatabase = function(fromdb, todb, fromhost, username, password) { 
254     assert( isString(fromdb) && fromdb.length );
255     assert( isString(todb) && todb.length );
256     fromhost = fromhost || "";
257     if ( username && password ) {
258         var n = this._adminCommand( { copydbgetnonce : 1, fromhost:fromhost } );
259         return this._adminCommand( { copydb:1, fromhost:fromhost, fromdb:fromdb, todb:todb, username:username, nonce:n.nonce, key:this.__pwHash( n.nonce, username, password ) } );
260     } else {
261         return this._adminCommand( { copydb:1, fromhost:fromhost, fromdb:fromdb, todb:todb } );
262     }
263 }
264 
265 /**
266   Repair database.
267  
268  * @return Object returned has member ok set to true if operation succeeds, false otherwise.
269 */
270 DB.prototype.repairDatabase = function() {
271     return this._dbCommand( { repairDatabase: 1 } );
272 }
273 
274 
275 DB.prototype.help = function() {
276     print("DB methods:");
277     print("\tdb.addUser(username, password[, readOnly=false])");
278     print("\tdb.auth(username, password)");
279     print("\tdb.cloneDatabase(fromhost)");
280     print("\tdb.commandHelp(name) returns the help for the command");
281     print("\tdb.copyDatabase(fromdb, todb, fromhost)");
282     print("\tdb.createCollection(name, { size : ..., capped : ..., max : ... } )");
283     print("\tdb.currentOp() displays the current operation in the db");
284     print("\tdb.dropDatabase()");
285     print("\tdb.eval(func, args) run code server-side");
286     print("\tdb.getCollection(cname) same as db['cname'] or db.cname");
287     print("\tdb.getCollectionNames()");
288     print("\tdb.getLastError() - just returns the err msg string");
289     print("\tdb.getLastErrorObj() - return full status object");
290     print("\tdb.getMongo() get the server connection object");
291     print("\tdb.getMongo().setSlaveOk() allow this connection to read from the nonmaster member of a replica pair");
292     print("\tdb.getName()");
293     print("\tdb.getPrevError()");
294     print("\tdb.getProfilingLevel() - deprecated");
295     print("\tdb.getProfilingStatus() - returns if profiling is on and slow threshold ");
296     print("\tdb.getReplicationInfo()");
297     print("\tdb.getSiblingDB(name) get the db at the same server as this one");
298     print("\tdb.isMaster() check replica primary status");
299     print("\tdb.killOp(opid) kills the current operation in the db");
300     print("\tdb.listCommands() lists all the db commands");
301     print("\tdb.printCollectionStats()");
302     print("\tdb.printReplicationInfo()");
303     print("\tdb.printSlaveReplicationInfo()");
304     print("\tdb.printShardingStatus()");
305     print("\tdb.removeUser(username)");
306     print("\tdb.repairDatabase()");
307     print("\tdb.resetError()");
308     print("\tdb.runCommand(cmdObj) run a database command.  if cmdObj is a string, turns it into { cmdObj : 1 }");
309     print("\tdb.serverStatus()");
310     print("\tdb.setProfilingLevel(level,<slowms>) 0=off 1=slow 2=all");
311     print("\tdb.shutdownServer()");
312     print("\tdb.stats()");
313     print("\tdb.version() current version of the server");
314     print("\tdb.getMongo().setSlaveOk() allow queries on a replication slave server");
315 
316     return __magicNoPrint;
317 }
318 
319 DB.prototype.printCollectionStats = function(){
320     var mydb = this;
321     this.getCollectionNames().forEach(
322         function(z){
323             print( z );
324             printjson( mydb.getCollection(z).stats() );
325             print( "---" );
326         }
327     );
328 }
329 
330 /**
331  * <p> Set profiling level for your db.  Profiling gathers stats on query performance. </p>
332  * 
333  * <p>Default is off, and resets to off on a database restart -- so if you want it on,
334  *    turn it on periodically. </p>
335  *  
336  *  <p>Levels :</p>
337  *   <ul>
338  *    <li>0=off</li>
339  *    <li>1=log very slow operations; optional argument slowms specifies slowness threshold</li>
340  *    <li>2=log all</li>
341  *  @param {String} level Desired level of profiling
342  *  @param {String} slowms For slow logging, query duration that counts as slow (default 100ms)
343  *  @return SOMETHING_FIXME or null on error
344  */
345 DB.prototype.setProfilingLevel = function(level,slowms) {
346     
347     if (level < 0 || level > 2) { 
348         throw { dbSetProfilingException : "input level " + level + " is out of range [0..2]" };        
349     }
350 
351     var cmd = { profile: level };
352     if ( slowms )
353         cmd["slowms"] = slowms;
354     return this._dbCommand( cmd );
355 }
356 
357 
358 /**
359  *  <p> Evaluate a js expression at the database server.</p>
360  * 
361  * <p>Useful if you need to touch a lot of data lightly; in such a scenario
362  *  the network transfer of the data could be a bottleneck.  A good example
363  *  is "select count(*)" -- can be done server side via this mechanism.
364  * </p>
365  *
366  * <p>
367  * If the eval fails, an exception is thrown of the form:
368  * </p>
369  * <code>{ dbEvalException: { retval: functionReturnValue, ok: num [, errno: num] [, errmsg: str] } }</code>
370  * 
371  * <p>Example: </p>
372  * <code>print( "mycount: " + db.eval( function(){db.mycoll.find({},{_id:ObjId()}).length();} );</code>
373  *
374  * @param {Function} jsfunction Javascript function to run on server.  Note this it not a closure, but rather just "code".
375  * @return result of your function, or null if error
376  * 
377  */
378 DB.prototype.eval = function(jsfunction) {
379     var cmd = { $eval : jsfunction };
380     if ( arguments.length > 1 ) {
381 	cmd.args = argumentsToArray( arguments ).slice(1);
382     }
383     
384     var res = this._dbCommand( cmd );
385     
386     if (!res.ok)
387     	throw tojson( res );
388     
389     return res.retval;
390 }
391 
392 DB.prototype.dbEval = DB.prototype.eval;
393 
394 
395 /**
396  * 
397  *  <p>
398  *   Similar to SQL group by.  For example: </p>
399  *
400  *  <code>select a,b,sum(c) csum from coll where active=1 group by a,b</code>
401  *
402  *  <p>
403  *    corresponds to the following in 10gen:
404  *  </p>
405  * 
406  *  <code>
407      db.group(
408        {
409          ns: "coll",
410          key: { a:true, b:true },
411 	 // keyf: ...,
412 	 cond: { active:1 },
413 	 reduce: function(obj,prev) { prev.csum += obj.c; } ,
414 	 initial: { csum: 0 }
415 	 });
416 	 </code>
417  *
418  * 
419  * <p>
420  *  An array of grouped items is returned.  The array must fit in RAM, thus this function is not
421  * suitable when the return set is extremely large.
422  * </p>
423  * <p>
424  * To order the grouped data, simply sort it client side upon return.
425  * <p>
426    Defaults
427      cond may be null if you want to run against all rows in the collection
428      keyf is a function which takes an object and returns the desired key.  set either key or keyf (not both).
429  * </p>
430 */
431 DB.prototype.groupeval = function(parmsObj) {
432 	
433     var groupFunction = function() {
434 	var parms = args[0];
435     	var c = db[parms.ns].find(parms.cond||{});
436     	var map = new Map();
437         var pks = parms.key ? Object.keySet( parms.key ) : null;
438         var pkl = pks ? pks.length : 0;
439         var key = {};
440         
441     	while( c.hasNext() ) {
442 	    var obj = c.next();
443 	    if ( pks ) {
444 	    	for( var i=0; i<pkl; i++ ){
445                     var k = pks[i];
446 		    key[k] = obj[k];
447                 }
448 	    }
449 	    else {
450 	    	key = parms.$keyf(obj);
451 	    }
452 
453 	    var aggObj = map.get(key);
454 	    if( aggObj == null ) {
455 		var newObj = Object.extend({}, key); // clone
456 	    	aggObj = Object.extend(newObj, parms.initial)
457                 map.put( key , aggObj );
458 	    }
459 	    parms.$reduce(obj, aggObj);
460 	}
461         
462 	return map.values();
463     }
464     
465     return this.eval(groupFunction, this._groupFixParms( parmsObj ));
466 }
467 
468 DB.prototype.groupcmd = function( parmsObj ){
469     var ret = this.runCommand( { "group" : this._groupFixParms( parmsObj ) } );
470     if ( ! ret.ok ){
471         throw "group command failed: " + tojson( ret );
472     }
473     return ret.retval;
474 }
475 
476 DB.prototype.group = DB.prototype.groupcmd;
477 
478 DB.prototype._groupFixParms = function( parmsObj ){
479     var parms = Object.extend({}, parmsObj);
480     
481     if( parms.reduce ) {
482 	parms.$reduce = parms.reduce; // must have $ to pass to db
483 	delete parms.reduce;
484     }
485     
486     if( parms.keyf ) {
487 	parms.$keyf = parms.keyf;
488 	delete parms.keyf;
489     }
490     
491     return parms;
492 }
493 
494 DB.prototype.resetError = function(){
495     return this.runCommand( { reseterror : 1 } );
496 }
497 
498 DB.prototype.forceError = function(){
499     return this.runCommand( { forceerror : 1 } );
500 }
501 
502 DB.prototype.getLastError = function( w , wtimeout ){
503     var res = this.getLastErrorObj( w , wtimeout );
504     if ( ! res.ok )
505         throw "getlasterror failed: " + tojson( res );
506     return res.err;
507 }
508 DB.prototype.getLastErrorObj = function( w , wtimeout ){
509     var cmd = { getlasterror : 1 };
510     if ( w ){
511         cmd.w = w;
512         if ( wtimeout )
513             cmd.wtimeout = wtimeout;
514     }
515     var res = this.runCommand( cmd );
516 
517     if ( ! res.ok )
518         throw "getlasterror failed: " + tojson( res );
519     return res;
520 }
521 DB.prototype.getLastErrorCmd = DB.prototype.getLastErrorObj;
522 
523 
524 /* Return the last error which has occurred, even if not the very last error.
525 
526    Returns: 
527     { err : <error message>, nPrev : <how_many_ops_back_occurred>, ok : 1 }
528 
529    result.err will be null if no error has occurred.
530  */
531 DB.prototype.getPrevError = function(){
532     return this.runCommand( { getpreverror : 1 } );
533 }
534 
535 DB.prototype.getCollectionNames = function(){
536     var all = [];
537 
538     var nsLength = this._name.length + 1;
539     
540     var c = this.getCollection( "system.namespaces" ).find();
541     while ( c.hasNext() ){
542         var name = c.next().name;
543         
544         if ( name.indexOf( "$" ) >= 0 && name.indexOf( ".oplog.$" ) < 0 )
545             continue;
546         
547         all.push( name.substring( nsLength ) );
548     }
549     
550     return all.sort();
551 }
552 
553 DB.prototype.tojson = function(){
554     return this._name;
555 }
556 
557 DB.prototype.toString = function(){
558     return this._name;
559 }
560 
561 DB.prototype.isMaster = function () { return this.runCommand("isMaster"); }
562 
563 DB.prototype.currentOp = function( arg ){
564     var q = {}
565     if ( arg ) {
566         if ( typeof( arg ) == "object" )
567             Object.extend( q , arg );
568         else if ( arg )
569             q["$all"] = true;
570     }
571     return db.$cmd.sys.inprog.findOne( q );
572 }
573 DB.prototype.currentOP = DB.prototype.currentOp;
574 
575 DB.prototype.killOp = function(op) {
576     if( !op ) 
577         throw "no opNum to kill specified";
578     return db.$cmd.sys.killop.findOne({'op':op});
579 }
580 DB.prototype.killOP = DB.prototype.killOp;
581 
582 DB.tsToSeconds = function(x){
583     if ( x.t && x.i )
584         return x.t / 1000;
585     return x / 4294967296; // low 32 bits are ordinal #s within a second
586 }
587 
588 /** 
589   Get a replication log information summary.
590   <p>
591   This command is for the database/cloud administer and not applicable to most databases.
592   It is only used with the local database.  One might invoke from the JS shell:
593   <pre>
594        use local
595        db.getReplicationInfo();
596   </pre>
597   It is assumed that this database is a replication master -- the information returned is 
598   about the operation log stored at local.oplog.$main on the replication master.  (It also 
599   works on a machine in a replica pair: for replica pairs, both machines are "masters" from 
600   an internal database perspective.
601   <p>
602   * @return Object timeSpan: time span of the oplog from start to end  if slave is more out 
603   *                          of date than that, it can't recover without a complete resync
604 */
605 DB.prototype.getReplicationInfo = function() { 
606     var db = this.getSiblingDB("local");
607 
608     var result = { };
609     var oplog;
610     if (db.system.namespaces.findOne({name:"local.oplog.rs"}) != null) {
611         oplog = 'oplog.rs';
612     }
613     else if (db.system.namespaces.findOne({name:"local.oplog.$main"}) != null) {
614         oplog = 'oplog.$main';
615     }
616     else {
617         result.errmsg = "neither master/slave nor replica set replication detected";
618         return result;
619     }
620     
621     var ol_entry = db.system.namespaces.findOne({name:"local."+oplog});
622     if( ol_entry && ol_entry.options ) {
623 	result.logSizeMB = ol_entry.options.size / ( 1024 * 1024 );
624     } else {
625         result.errmsg  = "local."+oplog+", or its options, not found in system.namespaces collection";
626         return result;
627     }
628     ol = db.getCollection(oplog);
629 
630     result.usedMB = ol.stats().size / ( 1024 * 1024 );
631     result.usedMB = Math.ceil( result.usedMB * 100 ) / 100;
632     
633     var firstc = ol.find().sort({$natural:1}).limit(1);
634     var lastc = ol.find().sort({$natural:-1}).limit(1);
635     if( !firstc.hasNext() || !lastc.hasNext() ) { 
636 	result.errmsg = "objects not found in local.oplog.$main -- is this a new and empty db instance?";
637 	result.oplogMainRowCount = ol.count();
638 	return result;
639     }
640 
641     var first = firstc.next();
642     var last = lastc.next();
643     {
644 	var tfirst = first.ts;
645 	var tlast = last.ts;
646         
647 	if( tfirst && tlast ) { 
648 	    tfirst = DB.tsToSeconds( tfirst ); 
649 	    tlast = DB.tsToSeconds( tlast );
650 	    result.timeDiff = tlast - tfirst;
651 	    result.timeDiffHours = Math.round(result.timeDiff / 36)/100;
652 	    result.tFirst = (new Date(tfirst*1000)).toString();
653 	    result.tLast  = (new Date(tlast*1000)).toString();
654 	    result.now = Date();
655 	}
656 	else { 
657 	    result.errmsg = "ts element not found in oplog objects";
658 	}
659     }
660 
661     return result;
662 };
663 
664 DB.prototype.printReplicationInfo = function() {
665     var result = this.getReplicationInfo();
666     if( result.errmsg ) { 
667 	print(tojson(result));
668 	return;
669     }
670     print("configured oplog size:   " + result.logSizeMB + "MB");
671     print("log length start to end: " + result.timeDiff + "secs (" + result.timeDiffHours + "hrs)");
672     print("oplog first event time:  " + result.tFirst);
673     print("oplog last event time:   " + result.tLast);
674     print("now:                     " + result.now);
675 }
676 
677 DB.prototype.printSlaveReplicationInfo = function() {
678     function getReplLag(st) {
679         var now = new Date();
680         print("\t syncedTo: " + st.toString() );
681         var ago = (now-st)/1000;
682         var hrs = Math.round(ago/36)/100;
683         print("\t\t = " + Math.round(ago) + "secs ago (" + hrs + "hrs)"); 
684     };
685     
686     function g(x) {
687         assert( x , "how could this be null (printSlaveReplicationInfo gx)" )
688         print("source:   " + x.host);
689         if ( x.syncedTo ){
690             var st = new Date( DB.tsToSeconds( x.syncedTo ) * 1000 );
691             getReplLag(st);
692         }
693         else {
694             print( "\t doing initial sync" );
695         }
696     };
697 
698     function r(x) {
699         assert( x , "how could this be null (printSlaveReplicationInfo rx)" );
700         if ( x.state == 1 ) {
701             return;
702         }
703         
704         print("source:   " + x.name);
705         if ( x.optime ) {
706             getReplLag(x.optimeDate);
707         }
708         else {
709             print( "\t no replication info, yet.  State: " + x.stateStr );
710         }
711     };
712     
713     var L = this.getSiblingDB("local");
714     if( L.sources.count() != 0 ) { 
715         L.sources.find().forEach(g);
716     }
717     else if (L.system.replset.count() != 0) {
718         var status = this.adminCommand({'replSetGetStatus' : 1});
719         status.members.forEach(r);
720     }
721     else {
722         print("local.sources is empty; is this db a --slave?");
723         return;
724     }
725 }
726 
727 DB.prototype.serverBuildInfo = function(){
728     return this._adminCommand( "buildinfo" );
729 }
730 
731 DB.prototype.serverStatus = function(){
732     return this._adminCommand( "serverStatus" );
733 }
734 
735 DB.prototype.serverCmdLineOpts = function(){
736     return this._adminCommand( "getCmdLineOpts" );
737 }
738 
739 DB.prototype.version = function(){
740     return this.serverBuildInfo().version;
741 }
742 
743 DB.prototype.serverBits = function(){
744     return this.serverBuildInfo().bits;
745 }
746 
747 DB.prototype.listCommands = function(){
748     var x = this.runCommand( "listCommands" );
749     for ( var name in x.commands ){
750         var c = x.commands[name];
751 
752         var s = name + ": ";
753         
754         switch ( c.lockType ){
755         case -1: s += "read-lock"; break;
756         case  0: s += "no-lock"; break;
757         case  1: s += "write-lock"; break;
758         default: s += c.lockType;
759         }
760         
761         if (c.adminOnly) s += " adminOnly ";
762         if (c.adminOnly) s += " slaveOk ";
763 
764         s += "\n  ";
765         s += c.help.replace(/\n/g, '\n  ');
766         s += "\n";
767         
768         print( s );
769     }
770 }
771 
772 DB.prototype.printShardingStatus = function( verbose ){
773     printShardingStatus( this.getSiblingDB( "config" ) , verbose );
774 }
775 
776 DB.autocomplete = function(obj){
777     var colls = obj.getCollectionNames();
778     var ret=[];
779     for (var i=0; i<colls.length; i++){
780         if (colls[i].match(/^[a-zA-Z0-9_.\$]+$/))
781             ret.push(colls[i]);
782     }
783     return ret;
784 }
785