mirror of
https://github.com/mongodb/mongo.git
synced 2024-11-30 17:10:48 +01:00
616 lines
18 KiB
JavaScript
616 lines
18 KiB
JavaScript
// @file collection.js - DBCollection support in the mongo shell
|
|
// db.colName is a DBCollection object
|
|
// or db["colName"]
|
|
|
|
if ( ( typeof DBCollection ) == "undefined" ){
|
|
DBCollection = function( mongo , db , shortName , fullName ){
|
|
this._mongo = mongo;
|
|
this._db = db;
|
|
this._shortName = shortName;
|
|
this._fullName = fullName;
|
|
|
|
this.verify();
|
|
}
|
|
}
|
|
|
|
DBCollection.prototype.verify = function(){
|
|
assert( this._fullName , "no fullName" );
|
|
assert( this._shortName , "no shortName" );
|
|
assert( this._db , "no db" );
|
|
|
|
assert.eq( this._fullName , this._db._name + "." + this._shortName , "name mismatch" );
|
|
|
|
assert( this._mongo , "no mongo in DBCollection" );
|
|
}
|
|
|
|
DBCollection.prototype.getName = function(){
|
|
return this._shortName;
|
|
}
|
|
|
|
DBCollection.prototype.help = function () {
|
|
var shortName = this.getName();
|
|
print("DBCollection help");
|
|
print("\tdb." + shortName + ".find().help() - show DBCursor help");
|
|
print("\tdb." + shortName + ".count()");
|
|
print("\tdb." + shortName + ".dataSize()");
|
|
print("\tdb." + shortName + ".distinct( key ) - eg. db." + shortName + ".distinct( 'x' )");
|
|
print("\tdb." + shortName + ".drop() drop the collection");
|
|
print("\tdb." + shortName + ".dropIndex(name)");
|
|
print("\tdb." + shortName + ".dropIndexes()");
|
|
print("\tdb." + shortName + ".ensureIndex(keypattern[,options]) - options is an object with these possible fields: name, unique, dropDups");
|
|
print("\tdb." + shortName + ".reIndex()");
|
|
print("\tdb." + shortName + ".find([query],[fields]) - query is an optional query filter. fields is optional set of fields to return.");
|
|
print("\t e.g. db." + shortName + ".find( {x:77} , {name:1, x:1} )");
|
|
print("\tdb." + shortName + ".find(...).count()");
|
|
print("\tdb." + shortName + ".find(...).limit(n)");
|
|
print("\tdb." + shortName + ".find(...).skip(n)");
|
|
print("\tdb." + shortName + ".find(...).sort(...)");
|
|
print("\tdb." + shortName + ".findOne([query])");
|
|
print("\tdb." + shortName + ".findAndModify( { update : ... , remove : bool [, query: {}, sort: {}, 'new': false] } )");
|
|
print("\tdb." + shortName + ".getDB() get DB object associated with collection");
|
|
print("\tdb." + shortName + ".getIndexes()");
|
|
print("\tdb." + shortName + ".group( { key : ..., initial: ..., reduce : ...[, cond: ...] } )");
|
|
print("\tdb." + shortName + ".mapReduce( mapFunction , reduceFunction , <optional params> )");
|
|
print("\tdb." + shortName + ".remove(query)");
|
|
print("\tdb." + shortName + ".renameCollection( newName , <dropTarget> ) renames the collection.");
|
|
print("\tdb." + shortName + ".runCommand( name , <options> ) runs a db command with the given name where the first param is the collection name");
|
|
print("\tdb." + shortName + ".save(obj)");
|
|
print("\tdb." + shortName + ".stats()");
|
|
print("\tdb." + shortName + ".storageSize() - includes free space allocated to this collection");
|
|
print("\tdb." + shortName + ".totalIndexSize() - size in bytes of all the indexes");
|
|
print("\tdb." + shortName + ".totalSize() - storage allocated for all data and indexes");
|
|
print("\tdb." + shortName + ".update(query, object[, upsert_bool, multi_bool])");
|
|
print("\tdb." + shortName + ".validate() - SLOW");
|
|
print("\tdb." + shortName + ".getShardVersion() - only for use with sharding");
|
|
return __magicNoPrint;
|
|
}
|
|
|
|
DBCollection.prototype.getFullName = function(){
|
|
return this._fullName;
|
|
}
|
|
DBCollection.prototype.getMongo = function(){
|
|
return this._db.getMongo();
|
|
}
|
|
DBCollection.prototype.getDB = function(){
|
|
return this._db;
|
|
}
|
|
|
|
DBCollection.prototype._dbCommand = function( cmd , params ){
|
|
if ( typeof( cmd ) == "object" )
|
|
return this._db._dbCommand( cmd );
|
|
|
|
var c = {};
|
|
c[cmd] = this.getName();
|
|
if ( params )
|
|
Object.extend( c , params );
|
|
return this._db._dbCommand( c );
|
|
}
|
|
|
|
DBCollection.prototype.runCommand = DBCollection.prototype._dbCommand;
|
|
|
|
DBCollection.prototype._massageObject = function( q ){
|
|
if ( ! q )
|
|
return {};
|
|
|
|
var type = typeof q;
|
|
|
|
if ( type == "function" )
|
|
return { $where : q };
|
|
|
|
if ( q.isObjectId )
|
|
return { _id : q };
|
|
|
|
if ( type == "object" )
|
|
return q;
|
|
|
|
if ( type == "string" ){
|
|
if ( q.length == 24 )
|
|
return { _id : q };
|
|
|
|
return { $where : q };
|
|
}
|
|
|
|
throw "don't know how to massage : " + type;
|
|
|
|
}
|
|
|
|
|
|
DBCollection.prototype._validateObject = function( o ){
|
|
if ( o._ensureSpecial && o._checkModify )
|
|
throw "can't save a DBQuery object";
|
|
}
|
|
|
|
DBCollection._allowedFields = { $id : 1 , $ref : 1 };
|
|
|
|
DBCollection.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] );
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
DBCollection.prototype.find = function( query , fields , limit , skip ){
|
|
return new DBQuery( this._mongo , this._db , this ,
|
|
this._fullName , this._massageObject( query ) , fields , limit , skip );
|
|
}
|
|
|
|
DBCollection.prototype.findOne = function( query , fields ){
|
|
var cursor = this._mongo.find( this._fullName , this._massageObject( query ) || {} , fields , -1 , 0 , 0 );
|
|
if ( ! cursor.hasNext() )
|
|
return null;
|
|
var ret = cursor.next();
|
|
if ( cursor.hasNext() ) throw "findOne has more than 1 result!";
|
|
if ( ret.$err )
|
|
throw "error " + tojson( ret );
|
|
return ret;
|
|
}
|
|
|
|
DBCollection.prototype.insert = function( obj , _allow_dot ){
|
|
if ( ! obj )
|
|
throw "no object passed to insert!";
|
|
if ( ! _allow_dot ) {
|
|
this._validateForStorage( obj );
|
|
}
|
|
if ( typeof( obj._id ) == "undefined" ){
|
|
var tmp = obj; // don't want to modify input
|
|
obj = {_id: new ObjectId()};
|
|
for (var key in tmp){
|
|
obj[key] = tmp[key];
|
|
}
|
|
}
|
|
this._mongo.insert( this._fullName , obj );
|
|
this._lastID = obj._id;
|
|
}
|
|
|
|
DBCollection.prototype.remove = function( t , justOne ){
|
|
this._mongo.remove( this._fullName , this._massageObject( t ) , justOne ? true : false );
|
|
}
|
|
|
|
DBCollection.prototype.update = function( query , obj , upsert , multi ){
|
|
assert( query , "need a query" );
|
|
assert( obj , "need an object" );
|
|
|
|
var firstKey = null;
|
|
for (var k in obj) { firstKey = k; break; }
|
|
|
|
if (firstKey != null && firstKey[0] == '$') {
|
|
// for mods we only validate partially, for example keys may have dots
|
|
this._validateObject( obj );
|
|
} else {
|
|
// we're basically inserting a brand new object, do full validation
|
|
this._validateForStorage( obj );
|
|
}
|
|
this._mongo.update( this._fullName , query , obj , upsert ? true : false , multi ? true : false );
|
|
}
|
|
|
|
DBCollection.prototype.save = function( obj ){
|
|
if ( obj == null || typeof( obj ) == "undefined" )
|
|
throw "can't save a null";
|
|
|
|
if ( typeof( obj._id ) == "undefined" ){
|
|
obj._id = new ObjectId();
|
|
return this.insert( obj );
|
|
}
|
|
else {
|
|
return this.update( { _id : obj._id } , obj , true );
|
|
}
|
|
}
|
|
|
|
DBCollection.prototype._genIndexName = function( keys ){
|
|
var name = "";
|
|
for ( var k in keys ){
|
|
var v = keys[k];
|
|
if ( typeof v == "function" )
|
|
continue;
|
|
|
|
if ( name.length > 0 )
|
|
name += "_";
|
|
name += k + "_";
|
|
|
|
if ( typeof v == "number" )
|
|
name += v;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
DBCollection.prototype._indexSpec = function( keys, options ) {
|
|
var ret = { ns : this._fullName , key : keys , name : this._genIndexName( keys ) };
|
|
|
|
if ( ! options ){
|
|
}
|
|
else if ( typeof ( options ) == "string" )
|
|
ret.name = options;
|
|
else if ( typeof ( options ) == "boolean" )
|
|
ret.unique = true;
|
|
else if ( typeof ( options ) == "object" ){
|
|
if ( options.length ){
|
|
var nb = 0;
|
|
for ( var i=0; i<options.length; i++ ){
|
|
if ( typeof ( options[i] ) == "string" )
|
|
ret.name = options[i];
|
|
else if ( typeof( options[i] ) == "boolean" ){
|
|
if ( options[i] ){
|
|
if ( nb == 0 )
|
|
ret.unique = true;
|
|
if ( nb == 1 )
|
|
ret.dropDups = true;
|
|
}
|
|
nb++;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Object.extend( ret , options );
|
|
}
|
|
}
|
|
else {
|
|
throw "can't handle: " + typeof( options );
|
|
}
|
|
/*
|
|
return ret;
|
|
|
|
var name;
|
|
var nTrue = 0;
|
|
|
|
if ( ! isObject( options ) ) {
|
|
options = [ options ];
|
|
}
|
|
|
|
if ( options.length ){
|
|
for( var i = 0; i < options.length; ++i ) {
|
|
var o = options[ i ];
|
|
if ( isString( o ) ) {
|
|
ret.name = o;
|
|
} else if ( typeof( o ) == "boolean" ) {
|
|
if ( o ) {
|
|
++nTrue;
|
|
}
|
|
}
|
|
}
|
|
if ( nTrue > 0 ) {
|
|
ret.unique = true;
|
|
}
|
|
if ( nTrue > 1 ) {
|
|
ret.dropDups = true;
|
|
}
|
|
}
|
|
*/
|
|
return ret;
|
|
}
|
|
|
|
DBCollection.prototype.createIndex = function( keys , options ){
|
|
var o = this._indexSpec( keys, options );
|
|
this._db.getCollection( "system.indexes" ).insert( o , true );
|
|
}
|
|
|
|
DBCollection.prototype.ensureIndex = function( keys , options ){
|
|
var name = this._indexSpec( keys, options ).name;
|
|
this._indexCache = this._indexCache || {};
|
|
if ( this._indexCache[ name ] ){
|
|
return;
|
|
}
|
|
|
|
this.createIndex( keys , options );
|
|
if ( this.getDB().getLastError() == "" ) {
|
|
this._indexCache[name] = true;
|
|
}
|
|
}
|
|
|
|
DBCollection.prototype.resetIndexCache = function(){
|
|
this._indexCache = {};
|
|
}
|
|
|
|
DBCollection.prototype.reIndex = function() {
|
|
return this._db.runCommand({ reIndex: this.getName() });
|
|
}
|
|
|
|
DBCollection.prototype.dropIndexes = function(){
|
|
this.resetIndexCache();
|
|
|
|
var res = this._db.runCommand( { deleteIndexes: this.getName(), index: "*" } );
|
|
assert( res , "no result from dropIndex result" );
|
|
if ( res.ok )
|
|
return res;
|
|
|
|
if ( res.errmsg.match( /not found/ ) )
|
|
return res;
|
|
|
|
throw "error dropping indexes : " + tojson( res );
|
|
}
|
|
|
|
|
|
DBCollection.prototype.drop = function(){
|
|
if ( arguments.length > 0 )
|
|
throw "drop takes no argument";
|
|
this.resetIndexCache();
|
|
var ret = this._db.runCommand( { drop: this.getName() } );
|
|
if ( ! ret.ok ){
|
|
if ( ret.errmsg == "ns not found" )
|
|
return false;
|
|
throw "drop failed: " + tojson( ret );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
DBCollection.prototype.findAndModify = function(args){
|
|
var cmd = { findandmodify: this.getName() };
|
|
for (var key in args){
|
|
cmd[key] = args[key];
|
|
}
|
|
|
|
var ret = this._db.runCommand( cmd );
|
|
if ( ! ret.ok ){
|
|
if (ret.errmsg == "No matching object found"){
|
|
return null;
|
|
}
|
|
throw "findAndModifyFailed failed: " + tojson( ret.errmsg );
|
|
}
|
|
return ret.value;
|
|
}
|
|
|
|
DBCollection.prototype.renameCollection = function( newName , dropTarget ){
|
|
return this._db._adminCommand( { renameCollection : this._fullName ,
|
|
to : this._db._name + "." + newName ,
|
|
dropTarget : dropTarget } )
|
|
}
|
|
|
|
DBCollection.prototype.validate = function() {
|
|
var res = this._db.runCommand( { validate: this.getName() } );
|
|
|
|
res.valid = false;
|
|
|
|
var raw = res.result || res.raw;
|
|
|
|
if ( raw ){
|
|
var str = "-" + tojson( raw );
|
|
res.valid = ! ( str.match( /exception/ ) || str.match( /corrupt/ ) );
|
|
|
|
var p = /lastExtentSize:(\d+)/;
|
|
var r = p.exec( str );
|
|
if ( r ){
|
|
res.lastExtentSize = Number( r[1] );
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
DBCollection.prototype.getShardVersion = function(){
|
|
return this._db._adminCommand( { getShardVersion : this._fullName } );
|
|
}
|
|
|
|
DBCollection.prototype.getIndexes = function(){
|
|
return this.getDB().getCollection( "system.indexes" ).find( { ns : this.getFullName() } ).toArray();
|
|
}
|
|
|
|
DBCollection.prototype.getIndices = DBCollection.prototype.getIndexes;
|
|
DBCollection.prototype.getIndexSpecs = DBCollection.prototype.getIndexes;
|
|
|
|
DBCollection.prototype.getIndexKeys = function(){
|
|
return this.getIndexes().map(
|
|
function(i){
|
|
return i.key;
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
DBCollection.prototype.count = function( x ){
|
|
return this.find( x ).count();
|
|
}
|
|
|
|
/**
|
|
* Drop free lists. Normally not used.
|
|
* Note this only does the collection itself, not the namespaces of its indexes (see cleanAll).
|
|
*/
|
|
DBCollection.prototype.clean = function() {
|
|
return this._dbCommand( { clean: this.getName() } );
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* <p>Drop a specified index.</p>
|
|
*
|
|
* <p>
|
|
* Name is the name of the index in the system.indexes name field. (Run db.system.indexes.find() to
|
|
* see example data.)
|
|
* </p>
|
|
*
|
|
* <p>Note : alpha: space is not reclaimed </p>
|
|
* @param {String} name of index to delete.
|
|
* @return A result object. result.ok will be true if successful.
|
|
*/
|
|
DBCollection.prototype.dropIndex = function(index) {
|
|
assert(index , "need to specify index to dropIndex" );
|
|
|
|
if ( ! isString( index ) && isObject( index ) )
|
|
index = this._genIndexName( index );
|
|
|
|
var res = this._dbCommand( "deleteIndexes" ,{ index: index } );
|
|
this.resetIndexCache();
|
|
return res;
|
|
}
|
|
|
|
DBCollection.prototype.copyTo = function( newName ){
|
|
return this.getDB().eval(
|
|
function( collName , newName ){
|
|
var from = db[collName];
|
|
var to = db[newName];
|
|
to.ensureIndex( { _id : 1 } );
|
|
var count = 0;
|
|
|
|
var cursor = from.find();
|
|
while ( cursor.hasNext() ){
|
|
var o = cursor.next();
|
|
count++;
|
|
to.save( o );
|
|
}
|
|
|
|
return count;
|
|
} , this.getName() , newName
|
|
);
|
|
}
|
|
|
|
DBCollection.prototype.getCollection = function( subName ){
|
|
return this._db.getCollection( this._shortName + "." + subName );
|
|
}
|
|
|
|
DBCollection.prototype.stats = function( scale ){
|
|
return this._db.runCommand( { collstats : this._shortName , scale : scale } );
|
|
}
|
|
|
|
DBCollection.prototype.dataSize = function(){
|
|
return this.stats().size;
|
|
}
|
|
|
|
DBCollection.prototype.storageSize = function(){
|
|
return this.stats().storageSize;
|
|
}
|
|
|
|
DBCollection.prototype.totalIndexSize = function( verbose ){
|
|
var stats = this.stats();
|
|
if (verbose){
|
|
for (var ns in stats.indexSizes){
|
|
print( ns + "\t" + stats.indexSizes[ns] );
|
|
}
|
|
}
|
|
return stats.totalIndexSize;
|
|
}
|
|
|
|
|
|
DBCollection.prototype.totalSize = function(){
|
|
var total = this.storageSize();
|
|
var mydb = this._db;
|
|
var shortName = this._shortName;
|
|
this.getIndexes().forEach(
|
|
function( spec ){
|
|
var coll = mydb.getCollection( shortName + ".$" + spec.name );
|
|
var mysize = coll.storageSize();
|
|
//print( coll + "\t" + mysize + "\t" + tojson( coll.validate() ) );
|
|
total += coll.dataSize();
|
|
}
|
|
);
|
|
return total;
|
|
}
|
|
|
|
|
|
DBCollection.prototype.convertToCapped = function( bytes ){
|
|
if ( ! bytes )
|
|
throw "have to specify # of bytes";
|
|
return this._dbCommand( { convertToCapped : this._shortName , size : bytes } )
|
|
}
|
|
|
|
DBCollection.prototype.exists = function(){
|
|
return this._db.system.namespaces.findOne( { name : this._fullName } );
|
|
}
|
|
|
|
DBCollection.prototype.isCapped = function(){
|
|
var e = this.exists();
|
|
return ( e && e.options && e.options.capped ) ? true : false;
|
|
}
|
|
|
|
DBCollection.prototype.distinct = function( keyString , query ){
|
|
var res = this._dbCommand( { distinct : this._shortName , key : keyString , query : query || {} } );
|
|
if ( ! res.ok )
|
|
throw "distinct failed: " + tojson( res );
|
|
return res.values;
|
|
}
|
|
|
|
DBCollection.prototype.group = function( params ){
|
|
params.ns = this._shortName;
|
|
return this._db.group( params );
|
|
}
|
|
|
|
DBCollection.prototype.groupcmd = function( params ){
|
|
params.ns = this._shortName;
|
|
return this._db.groupcmd( params );
|
|
}
|
|
|
|
MapReduceResult = function( db , o ){
|
|
Object.extend( this , o );
|
|
this._o = o;
|
|
this._keys = Object.keySet( o );
|
|
this._db = db;
|
|
this._coll = this._db.getCollection( this.result );
|
|
}
|
|
|
|
MapReduceResult.prototype._simpleKeys = function(){
|
|
return this._o;
|
|
}
|
|
|
|
MapReduceResult.prototype.find = function(){
|
|
if ( this.results )
|
|
return this.results;
|
|
return DBCollection.prototype.find.apply( this._coll , arguments );
|
|
}
|
|
|
|
MapReduceResult.prototype.drop = function(){
|
|
return this._coll.drop();
|
|
}
|
|
|
|
/**
|
|
* just for debugging really
|
|
*/
|
|
MapReduceResult.prototype.convertToSingleObject = function(){
|
|
var z = {};
|
|
this._coll.find().forEach( function(a){ z[a._id] = a.value; } );
|
|
return z;
|
|
}
|
|
|
|
/**
|
|
* @param optional object of optional fields;
|
|
*/
|
|
DBCollection.prototype.mapReduce = function( map , reduce , optionsOrOutString ){
|
|
var c = { mapreduce : this._shortName , map : map , reduce : reduce };
|
|
assert( optionsOrOutString , "need to an optionsOrOutString" )
|
|
|
|
if ( typeof( optionsOrOutString ) == "string" )
|
|
c["out"] = optionsOrOutString;
|
|
else
|
|
Object.extend( c , optionsOrOutString );
|
|
|
|
var raw = this._db.runCommand( c );
|
|
if ( ! raw.ok )
|
|
throw "map reduce failed: " + tojson( raw );
|
|
return new MapReduceResult( this._db , raw );
|
|
|
|
}
|
|
|
|
DBCollection.prototype.toString = function(){
|
|
return this.getFullName();
|
|
}
|
|
|
|
DBCollection.prototype.toString = function(){
|
|
return this.getFullName();
|
|
}
|
|
|
|
|
|
DBCollection.prototype.tojson = DBCollection.prototype.toString;
|
|
|
|
DBCollection.prototype.shellPrint = DBCollection.prototype.toString;
|
|
|
|
DBCollection.autocomplete = function(obj){
|
|
var colls = DB.autocomplete(obj.getDB());
|
|
var ret = [];
|
|
for (var i=0; i<colls.length; i++){
|
|
var c = colls[i];
|
|
if (c.length <= obj.getName().length) continue;
|
|
if (c.slice(0,obj.getName().length+1) != obj.getName()+'.') continue;
|
|
|
|
ret.push(c.slice(obj.getName().length+1));
|
|
}
|
|
return ret;
|
|
}
|