matcher;
if ( ! query.isEmpty() )
matcher.reset( new CoveredIndexMatcher( query , cursor->indexKeyPattern() ) );
while ( cursor->ok() ){
if ( matcher.get() && ! matcher->matchesCurrent( cursor.get() ) ){
cursor->advance();
continue;
}
BSONObj obj = cursor->current();
cursor->advance();
BSONObj key = getKey( obj , keyPattern , keyFunction , keysize / keynum , s.get() );
keysize += key.objsize();
keynum++;
int& n = map[key];
if ( n == 0 ){
n = map.size();
s->setObject( "$key" , key , true );
uassert( 10043 , "group() can't handle more than 10000 unique keys" , n <= 10000 );
}
s->setObject( "obj" , obj , true );
s->setNumber( "n" , n - 1 );
if ( s->invoke( f , BSONObj() , 0 , true ) ){
throw UserException( 9010 , (string)"reduce invoke failed: " + s->getError() );
}
}
if (!finalize.empty()){
s->exec( "$finalize = " + finalize , "finalize define" , false , true , true , 100 );
ScriptingFunction g = s->createFunction(
"function(){ "
" for(var i=0; i < $arr.length; i++){ "
" var ret = $finalize($arr[i]); "
" if (ret !== undefined) "
" $arr[i] = ret; "
" } "
"}" );
s->invoke( g , BSONObj() , 0 , true );
}
result.appendArray( "retval" , s->getObject( "$arr" ) );
result.append( "count" , keynum - 1 );
result.append( "keys" , (int)(map.size()) );
s->exec( "$arr = [];" , "reduce setup 2" , false , true , true , 100 );
s->gc();
return true;
}
bool run(const string& dbname, BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool fromRepl ){
/* db.$cmd.findOne( { group : } ) */
const BSONObj& p = jsobj.firstElement().embeddedObjectUserCheck();
BSONObj q;
if ( p["cond"].type() == Object )
q = p["cond"].embeddedObject();
else if ( p["condition"].type() == Object )
q = p["condition"].embeddedObject();
else
q = getQuery( p );
if ( p["ns"].type() != String ){
errmsg = "ns has to be set";
return false;
}
string ns = dbname + "." + p["ns"].String();
BSONObj key;
string keyf;
if ( p["key"].type() == Object ){
key = p["key"].embeddedObjectUserCheck();
if ( ! p["$keyf"].eoo() ){
errmsg = "can't have key and $keyf";
return false;
}
}
else if ( p["$keyf"].type() ){
keyf = p["$keyf"]._asCode();
}
else {
// no key specified, will use entire object as key
}
BSONElement reduce = p["$reduce"];
if ( reduce.eoo() ){
errmsg = "$reduce has to be set";
return false;
}
BSONElement initial = p["initial"];
if ( initial.type() != Object ){
errmsg = "initial has to be an object";
return false;
}
string finalize;
if (p["finalize"].type())
finalize = p["finalize"]._asCode();
return group( dbname , ns , q ,
key , keyf , reduce._asCode() , reduce.type() != CodeWScope ? 0 : reduce.codeWScopeScopeData() ,
initial.embeddedObject() , finalize ,
errmsg , result );
}
} cmdGroup;
class DistinctCommand : public Command {
public:
DistinctCommand() : Command("distinct"){}
virtual bool slaveOk() const { return true; }
virtual LockType locktype() const { return READ; }
virtual void help( stringstream &help ) const {
help << "{ distinct : 'collection name' , key : 'a.b' }";
}
bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl ){
string ns = dbname + '.' + cmdObj.firstElement().valuestr();
string key = cmdObj["key"].valuestrsafe();
BSONObj keyPattern = BSON( key << 1 );
BSONObj query = getQuery( cmdObj );
BSONElementSet values;
shared_ptr cursor = MultiPlanScanner(ns.c_str() , query , BSONObj() ).getBestGuess()->newCursor();
auto_ptr matcher;
if ( ! query.isEmpty() )
matcher.reset( new CoveredIndexMatcher( query , cursor->indexKeyPattern() ) );
while ( cursor->ok() ){
if ( matcher.get() && ! matcher->matchesCurrent( cursor.get() ) ){
cursor->advance();
continue;
}
BSONObj o = cursor->current();
cursor->advance();
o.getFieldsDotted( key.c_str(), values );
}
BSONArrayBuilder b( result.subarrayStart( "values" ) );
for ( BSONElementSet::iterator i = values.begin() ; i != values.end(); i++ ){
b.append( *i );
}
BSONObj arr = b.done();
uassert(10044, "distinct too big, 4mb cap",
(arr.objsize() + 1024) < (4 * 1024 * 1024));
return true;
}
} distinctCmd;
/* Find and Modify an object returning either the old (default) or new value*/
class CmdFindAndModify : public Command {
public:
virtual void help( stringstream &help ) const {
help <<
"{ findandmodify: \"collection\", query: {processed:false}, update: {$set: {processed:true}}, new: true}\n"
"{ findandmodify: \"collection\", query: {processed:false}, remove: true, sort: {priority:-1}}\n"
"Either update or remove is required, all other fields have default values.\n"
"Output is in the \"value\" field\n";
}
CmdFindAndModify() : Command("findAndModify", false, "findandmodify") { }
virtual bool logTheOp() {
return false; // the modification will be logged directly
}
virtual bool slaveOk() const {
return false;
}
virtual LockType locktype() const { return WRITE; }
virtual bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) {
static DBDirectClient db;
string ns = dbname + '.' + cmdObj.firstElement().valuestr();
BSONObj origQuery = cmdObj.getObjectField("query"); // defaults to {}
Query q (origQuery);
BSONElement sort = cmdObj["sort"];
if (!sort.eoo())
q.sort(sort.embeddedObjectUserCheck());
BSONObj fieldsHolder (cmdObj.getObjectField("fields"));
const BSONObj* fields = (fieldsHolder.isEmpty() ? NULL : &fieldsHolder);
BSONObj out = db.findOne(ns, q, fields);
if (out.firstElement().eoo()){
errmsg = "No matching object found";
return false;
}
Query idQuery = QUERY( "_id" << out["_id"]);
if (cmdObj["remove"].trueValue()){
uassert(12515, "can't remove and update", cmdObj["update"].eoo());
db.remove(ns, idQuery, 1);
} else { // update
// need to include original query for $ positional operator
BSONObjBuilder b;
b.append(out["_id"]);
BSONObjIterator it(origQuery);
while (it.more()){
BSONElement e = it.next();
if (strcmp(e.fieldName(), "_id"))
b.append(e);
}
q = Query(b.obj());
BSONElement update = cmdObj["update"];
uassert(12516, "must specify remove or update", !update.eoo());
db.update(ns, q, update.embeddedObjectUserCheck());
if (cmdObj["new"].trueValue())
out = db.findOne(ns, idQuery, fields);
}
result.append("value", out);
return true;
}
} cmdFindAndModify;
/* Returns client's uri */
class CmdWhatsMyUri : public Command {
public:
CmdWhatsMyUri() : Command("whatsmyuri") { }
virtual bool logTheOp() {
return false; // the modification will be logged directly
}
virtual bool slaveOk() const {
return true;
}
virtual LockType locktype() const { return NONE; }
virtual bool requiresAuth() {
return false;
}
virtual void help( stringstream &help ) const {
help << "{whatsmyuri:1}";
}
virtual bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) {
BSONObj info = cc().curop()->infoNoauth();
result << "you" << info[ "client" ];
return true;
}
} cmdWhatsMyUri;
/* For testing only, not for general use */
class GodInsert : public Command {
public:
GodInsert() : Command( "godinsert" ) { }
virtual bool logTheOp() {
return true;
}
virtual bool slaveOk() const {
return false;
}
virtual LockType locktype() const { return WRITE; }
virtual bool requiresAuth() {
return true;
}
virtual void help( stringstream &help ) const {
help << "internal. for testing only.";
}
virtual bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) {
string coll = cmdObj[ "godinsert" ].valuestrsafe();
uassert( 13049, "godinsert must specify a collection", !coll.empty() );
string ns = dbname + "." + coll;
BSONObj obj = cmdObj[ "obj" ].embeddedObjectUserCheck();
DiskLoc loc = theDataFileMgr.insertWithObjMod( ns.c_str(), obj, true );
return true;
}
} cmdGodInsert;
class DBHashCmd : public Command {
public:
DBHashCmd() : Command( "dbHash", false, "dbhash" ){}
virtual bool slaveOk() const { return true; }
virtual LockType locktype() const { return READ; }
virtual bool run(const string& dbname , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool){
list colls;
Database* db = cc().database();
if ( db )
db->namespaceIndex.getNamespaces( colls );
colls.sort();
result.appendNumber( "numCollections" , (long long)colls.size() );
md5_state_t globalState;
md5_init(&globalState);
BSONObjBuilder bb( result.subobjStart( "collections" ) );
for ( list::iterator i=colls.begin(); i != colls.end(); i++ ){
string c = *i;
if ( c.find( ".system.profil" ) != string::npos )
continue;
shared_ptr cursor;
NamespaceDetails * nsd = nsdetails( c.c_str() );
// debug SERVER-761
NamespaceDetails::IndexIterator ii = nsd->ii();
while( ii.more() ) {
const IndexDetails &idx = ii.next();
if ( !idx.head.isValid() || !idx.info.isValid() ) {
log() << "invalid index for ns: " << c << " " << idx.head << " " << idx.info;
if ( idx.info.isValid() )
log() << " " << idx.info.obj();
log() << endl;
}
}
int idNum = nsd->findIdIndex();
if ( idNum >= 0 ){
cursor.reset( new BtreeCursor( nsd , idNum , nsd->idx( idNum ) , BSONObj() , BSONObj() , false , 1 ) );
}
else if ( c.find( ".system." ) != string::npos ){
continue;
}
else if ( nsd->capped ){
cursor = findTableScan( c.c_str() , BSONObj() );
}
else {
bb.done();
errmsg = (string)"can't find _id index for: " + c;
return 0;
}
md5_state_t st;
md5_init(&st);
long long n = 0;
while ( cursor->ok() ){
BSONObj c = cursor->current();
md5_append( &st , (const md5_byte_t*)c.objdata() , c.objsize() );
n++;
cursor->advance();
}
md5digest d;
md5_finish(&st, d);
string hash = digestToString( d );
bb.append( c.c_str() + ( dbname.size() + 1 ) , hash );
md5_append( &globalState , (const md5_byte_t*)hash.c_str() , hash.size() );
}
bb.done();
md5digest d;
md5_finish(&globalState, d);
string hash = digestToString( d );
result.append( "md5" , hash );
return 1;
}
} dbhashCmd;
/**
* this handles
- auth
- locking
- context
then calls run()
*/
bool execCommand( Command * c ,
Client& client , int queryOptions ,
const char *cmdns, BSONObj& cmdObj ,
BSONObjBuilder& result,
bool fromRepl ){
string dbname = nsToDatabase( cmdns );
AuthenticationInfo *ai = client.getAuthenticationInfo();
if( c->adminOnly() && c->localHostOnlyIfNoAuth( cmdObj ) && noauth && !ai->isLocalHost ) {
result.append( "errmsg" ,
"unauthorized: this command must run from localhost when running db without auth" );
log() << "command denied: " << cmdObj.toString() << endl;
return false;
}
if ( c->adminOnly() && ! fromRepl && dbname != "admin" ) {
result.append( "errmsg" , "access denied" );
log() << "command denied: " << cmdObj.toString() << endl;
return false;
}
if ( cmdObj["help"].trueValue() ){
stringstream ss;
ss << "help for: " << c->name << " ";
c->help( ss );
result.append( "help" , ss.str() );
result.append( "lockType" , c->locktype() );
return true;
}
bool canRunHere =
isMaster( dbname.c_str() ) ||
c->slaveOk() ||
( c->slaveOverrideOk() && ( queryOptions & QueryOption_SlaveOk ) ) ||
fromRepl;
if ( ! canRunHere ){
result.append( "errmsg" , "not master" );
return false;
}
if ( c->adminOnly() )
log( 2 ) << "command: " << cmdObj << endl;
if ( c->locktype() == Command::NONE ){
// we also trust that this won't crash
string errmsg;
int ok = c->run( dbname , cmdObj , errmsg , result , fromRepl );
if ( ! ok )
result.append( "errmsg" , errmsg );
return ok;
}
bool needWriteLock = c->locktype() == Command::WRITE;
if ( ! needWriteLock ){
assert( ! c->logTheOp() );
}
mongolock lk( needWriteLock );
Client::Context ctx( dbname , dbpath , &lk , c->requiresAuth() );
try {
string errmsg;
if ( ! c->run(dbname, cmdObj, errmsg, result, fromRepl ) ){
result.append( "errmsg" , errmsg );
return false;
}
}
catch ( DBException& e ){
stringstream ss;
ss << "exception: " << e.what();
result.append( "errmsg" , ss.str() );
result.append( "code" , e.getCode() );
return false;
}
if ( c->logTheOp() && ! fromRepl ){
logOp("c", cmdns, cmdObj);
}
return true;
}
/* TODO make these all command objects -- legacy stuff here
usage:
abc.$cmd.findOne( { ismaster:1 } );
returns true if ran a cmd
*/
bool _runCommands(const char *ns, BSONObj& _cmdobj, BufBuilder &b, BSONObjBuilder& anObjBuilder, bool fromRepl, int queryOptions) {
string dbname = nsToDatabase( ns );
if( logLevel >= 1 )
log() << "run command " << ns << ' ' << _cmdobj << endl;
const char *p = strchr(ns, '.');
if ( !p ) return false;
if ( strcmp(p, ".$cmd") != 0 ) return false;
BSONObj jsobj;
{
BSONElement e = _cmdobj.firstElement();
if ( e.type() == Object && string("query") == e.fieldName() ) {
jsobj = e.embeddedObject();
}
else {
jsobj = _cmdobj;
}
}
Client& client = cc();
bool ok = false;
BSONElement e = jsobj.firstElement();
Command * c = e.type() ? Command::findCommand( e.fieldName() ) : 0;
if ( c ){
ok = execCommand( c , client , queryOptions , ns , jsobj , anObjBuilder , fromRepl );
}
else {
anObjBuilder.append("errmsg", "no such cmd");
anObjBuilder.append("bad cmd" , _cmdobj );
}
anObjBuilder.append("ok", ok);
BSONObj x = anObjBuilder.done();
b.append((void*) x.objdata(), x.objsize());
return true;
}
} // namespace mongo