BSONObj DBClientWithCommands::mapreduce(const string &ns, const string &jsmapf, const string &jsreducef, BSONObj query, MROutput output) { BSONObjBuilder b; b.append("mapreduce", nsGetCollection(ns)); b.appendCode("map", jsmapf); b.appendCode("reduce", jsreducef); if( !query.isEmpty() ) b.append("query", query); b.append("out", output.out); BSONObj info; runCommand(nsGetDB(ns), b.done(), info); return info; }
void run(){ Scope * s = globalScriptEngine->createScope(); { BSONObjBuilder b; b.append( "a" , 1 ); b.appendCode( "b" , "function(){ out.b = 11; }" ); b.appendCodeWScope( "c" , "function(){ out.c = 12; }" , BSONObj() ); b.appendCodeWScope( "d" , "function(){ out.d = 13 + bleh; }" , BSON( "bleh" << 5 ) ); s->setObject( "foo" , b.obj() ); } s->invokeSafe( "out = {}; out.a = foo.a; foo.b(); foo.c();" , BSONObj() ); BSONObj out = s->getObject( "out" ); ASSERT_EQUALS( 1 , out["a"].number() ); ASSERT_EQUALS( 11 , out["b"].number() ); ASSERT_EQUALS( 12 , out["c"].number() ); //s->invokeSafe( "foo.d() " , BSONObj() ); //out = s->getObject( "out" ); //ASSERT_EQUALS( 18 , out["d"].number() ); delete s; }
void append( BSONObjBuilder& b , string name , jsval val , BSONType oldType = EOO , int depth=0 ) { //cout << "name: " << name << "\t" << typeString( val ) << " oldType: " << oldType << endl; switch ( JS_TypeOfValue( _context , val ) ) { case JSTYPE_VOID: b.appendUndefined( name.c_str() ); break; case JSTYPE_NULL: b.appendNull( name.c_str() ); break; case JSTYPE_NUMBER: { double d = toNumber( val ); if ( oldType == NumberInt && ((int)d) == d ) b.append( name.c_str() , (int)d ); else b.append( name.c_str() , d ); break; } case JSTYPE_STRING: b.append( name.c_str() , toString( val ) ); break; case JSTYPE_BOOLEAN: b.appendBool( name.c_str() , toBoolean( val ) ); break; case JSTYPE_OBJECT: { JSObject * o = JSVAL_TO_OBJECT( val ); if ( ! o || o == JSVAL_NULL ) { b.appendNull( name.c_str() ); } else if ( ! appendSpecialDBObject( this , b , name , val , o ) ) { BSONObj sub = toObject( o , depth ); if ( JS_IsArrayObject( _context , o ) ) { b.appendArray( name.c_str() , sub ); } else { b.append( name.c_str() , sub ); } } break; } case JSTYPE_FUNCTION: { string s = toString(val); if ( s[0] == '/' ) { appendRegex( b , name , s ); } else { b.appendCode( name.c_str() , getFunctionCode( val ).c_str() ); } break; } default: uassert( 10217 , (string)"can't append field. name:" + name + " type: " + typeString( val ) , 0 ); } }
bool DBClientWithCommands::eval(const string &dbname, const string &jscode, BSONObj& info, BSONElement& retValue, BSONObj *args) { BSONObjBuilder b; b.appendCode("$eval", jscode.c_str()); if ( args ) b.appendArray("args", *args); bool ok = runCommand(dbname, b.done(), info); if ( ok ) retValue = info.getField("retval"); return ok; }
void Scope::append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName ){ int t = type( scopeName ); switch ( t ){ case Object: builder.append( fieldName , getObject( scopeName ) ); break; case Array: builder.appendArray( fieldName , getObject( scopeName ) ); break; case NumberDouble: builder.append( fieldName , getNumber( scopeName ) ); break; case NumberInt: builder.append( fieldName , getNumberInt( scopeName ) ); break; case NumberLong: builder.append( fieldName , getNumberLongLong( scopeName ) ); break; case String: builder.append( fieldName , getString( scopeName ).c_str() ); break; case Bool: builder.appendBool( fieldName , getBoolean( scopeName ) ); break; case jstNULL: case Undefined: builder.appendNull( fieldName ); break; case Date: // TODO: make signed builder.appendDate( fieldName , Date_t((unsigned long long)getNumber( scopeName )) ); break; case Code: builder.appendCode( fieldName , getString( scopeName ) ); break; default: stringstream temp; temp << "can't append type from:"; temp << t; uassert( 10206 , temp.str() , 0 ); } }
void Scope::append(BSONObjBuilder& builder, const char* fieldName, const char* scopeName) { int t = type(scopeName); switch (t) { case Object: builder.append(fieldName, getObject(scopeName)); break; case Array: builder.appendArray(fieldName, getObject(scopeName)); break; case NumberDouble: builder.append(fieldName, getNumber(scopeName)); break; case NumberInt: builder.append(fieldName, getNumberInt(scopeName)); break; case NumberLong: builder.append(fieldName, getNumberLongLong(scopeName)); break; case NumberDecimal: builder.append(fieldName, getNumberDecimal(scopeName)); break; case String: builder.append(fieldName, getString(scopeName)); break; case Bool: builder.appendBool(fieldName, getBoolean(scopeName)); break; case jstNULL: case Undefined: builder.appendNull(fieldName); break; case Date: builder.appendDate( fieldName, Date_t::fromMillisSinceEpoch(static_cast<long long>(getNumber(scopeName)))); break; case Code: builder.appendCode(fieldName, getString(scopeName)); break; default: uassert(10206, str::stream() << "can't append type from: " << t, 0); } }
void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , const string sname , v8::Handle<v8::Value> value ){ if ( value->IsString() ){ if ( sname == "$where" ) b.appendCode( sname.c_str() , toSTLString( value ).c_str() ); else b.append( sname.c_str() , toSTLString( value ).c_str() ); return; } if ( value->IsFunction() ){ b.appendCode( sname.c_str() , toSTLString( value ).c_str() ); return; } if ( value->IsNumber() ){ b.append( sname.c_str() , value->ToNumber()->Value() ); return; } if ( value->IsArray() ){ BSONObj sub = v8ToMongo( value->ToObject() ); b.appendArray( sname.c_str() , sub ); return; } if ( value->IsDate() ){ b.appendDate( sname.c_str() , (unsigned long long )(v8::Date::Cast( *value )->NumberValue()) ); return; } if ( value->IsObject() ){ string s = toSTLString( value ); if ( s.size() && s[0] == '/' ){ s = s.substr( 1 ); string r = s.substr( 0 , s.find( "/" ) ); string o = s.substr( s.find( "/" ) + 1 ); b.appendRegex( sname.c_str() , r.c_str() , o.c_str() ); } else if ( value->ToObject()->GetPrototype()->IsObject() && value->ToObject()->GetPrototype()->ToObject()->HasRealNamedProperty( String::New( "isObjectId" ) ) ){ OID oid; oid.init( toSTLString( value ) ); b.appendOID( sname.c_str() , &oid ); } else { BSONObj sub = v8ToMongo( value->ToObject() ); b.append( sname.c_str() , sub ); } return; } if ( value->IsBoolean() ){ b.appendBool( sname.c_str() , value->ToBoolean()->Value() ); return; } else if ( value->IsUndefined() ){ return; } else if ( value->IsNull() ){ b.appendNull( sname.c_str() ); return; } cout << "don't know how to covert to mongo field [" << name << "]\t" << value << endl; }
/* **************************************************************************** * * addTriggeredSubscriptions - */ static bool addTriggeredSubscriptions ( ContextRegistration cr, map<string, TriggeredSubscription*>& subs, std::string& err, std::string tenant ) { BSONArrayBuilder entitiesNoPatternA; std::vector<std::string> idJsV; std::vector<std::string> typeJsV; for (unsigned int ix = 0; ix < cr.entityIdVector.size(); ++ix) { // FIXME: take into account subscriptions with no type EntityId* enP = cr.entityIdVector[ix]; // The registration of isPattern=true entities is not supported, so we don't include them here if (enP->isPattern == "false") { entitiesNoPatternA.append(BSON(CASUB_ENTITY_ID << enP->id << CASUB_ENTITY_TYPE << enP->type << CASUB_ENTITY_ISPATTERN << "false")); idJsV.push_back(enP->id); typeJsV.push_back(enP->type); } } BSONArrayBuilder attrA; for (unsigned int ix = 0; ix < cr.contextRegistrationAttributeVector.size(); ++ix) { ContextRegistrationAttribute* craP = cr.contextRegistrationAttributeVector[ix]; attrA.append(craP->name); } BSONObjBuilder queryNoPattern; queryNoPattern.append(CASUB_ENTITIES, BSON("$in" << entitiesNoPatternA.arr())); if (attrA.arrSize() > 0) { // If we don't do this checking, the {$in: [] } in the attribute name part will // make the query fail // // queryB.append(CASUB_ATTRS, BSON("$in" << attrA.arr())); queryNoPattern.append("$or", BSON_ARRAY( BSON(CASUB_ATTRS << BSON("$in" << attrA.arr())) << BSON(CASUB_ATTRS << BSON("$size" << 0)))); } else { queryNoPattern.append(CASUB_ATTRS, BSON("$size" << 0)); } queryNoPattern.append(CASUB_EXPIRATION, BSON("$gt" << (long long) getCurrentTime())); // // This is JavaScript code that runs in MongoDB engine. As far as I know, this is the only // way to do a "reverse regex" query in MongoDB (see // http://stackoverflow.com/questions/15966991/mongodb-reverse-regex/15989520). // Note that although we are using a isPattern=true in the MongoDB query besides $where, we // also need to check that in the if statement in the JavaScript function given that a given // sub document could include both isPattern=true and isPattern=false documents // std::string idJsString = "[ "; for (unsigned int ix = 0; ix < idJsV.size(); ++ix) { if (ix != idJsV.size() - 1) { idJsString += "\""+idJsV[ix]+ "\" ,"; } else { idJsString += "\"" +idJsV[ix]+ "\""; } } idJsString += " ]"; std::string typeJsString = "[ "; for (unsigned int ix = 0; ix < typeJsV.size(); ++ix) { if (ix != typeJsV.size() - 1) { typeJsString += "\"" +typeJsV[ix] + "\" ,"; } else { typeJsString += "\"" + typeJsV[ix] + "\""; } } typeJsString += " ]"; std::string function = std::string("function()") + "{" + "enId = " + idJsString + ";" + "enType = " + typeJsString + ";" + "for (var i=0; i < this." + CASUB_ENTITIES + ".length; i++) {" + "if (this." + CASUB_ENTITIES + "[i]." + CASUB_ENTITY_ISPATTERN + " == \"true\") {" + "for (var j = 0; j < enId.length; j++) {" + "if (enId[j].match(this." + CASUB_ENTITIES + "[i]." + CASUB_ENTITY_ID + ") && this." + CASUB_ENTITIES + "[i]." + CASUB_ENTITY_TYPE + " == enType[j]) {" + "return true; " + "}" + "}" + "}" + "}" + "return false; " + "}"; LM_T(LmtMongo, ("JS function: %s", function.c_str())); std::string entPatternQ = CSUB_ENTITIES "." CSUB_ENTITY_ISPATTERN; BSONObjBuilder queryPattern; queryPattern.append(entPatternQ, "true"); queryPattern.append(CASUB_EXPIRATION, BSON("$gt" << (long long) getCurrentTime())); queryPattern.appendCode("$where", function); auto_ptr<DBClientCursor> cursor; BSONObj query = BSON("$or" << BSON_ARRAY(queryNoPattern.obj() << queryPattern.obj())); TIME_STAT_MONGO_READ_WAIT_START(); DBClientBase* connection = getMongoConnection(); if (!collectionQuery(connection, getSubscribeContextAvailabilityCollectionName(tenant), query, &cursor, &err)) { TIME_STAT_MONGO_READ_WAIT_STOP(); releaseMongoConnection(connection); return false; } TIME_STAT_MONGO_READ_WAIT_STOP(); /* For each one of the subscriptions found, add it to the map (if not already there) */ while (moreSafe(cursor)) { BSONObj sub; std::string err; if (!nextSafeOrErrorF(cursor, &sub, &err)) { LM_E(("Runtime Error (exception in nextSafe(): %s - query: %s)", err.c_str(), query.toString().c_str())); continue; } BSONElement idField = getFieldF(sub, "_id"); // // BSONElement::eoo returns true if 'not found', i.e. the field "_id" doesn't exist in 'sub' // // Now, if 'getFieldF(sub, "_id")' is not found, if we continue, calling OID() on it, then we get // an exception and the broker crashes. // if (idField.eoo() == true) { std::string details = std::string("error retrieving _id field in doc: '") + sub.toString() + "'"; alarmMgr.dbError(details); continue; } alarmMgr.dbErrorReset(); std::string subIdStr = idField.OID().toString(); if (subs.count(subIdStr) == 0) { LM_T(LmtMongo, ("adding subscription: '%s'", sub.toString().c_str())); // // FIXME P4: Once ctx availability notification formats get defined for NGSIv2, // the first parameter for TriggeredSubscription will have "normalized" as default value // TriggeredSubscription* trigs = new TriggeredSubscription( sub.hasField(CASUB_FORMAT)? stringToRenderFormat(getStringFieldF(sub, CASUB_FORMAT)) : NGSI_V1_LEGACY, getStringFieldF(sub, CASUB_REFERENCE), subToAttributeList(sub)); subs.insert(std::pair<string, TriggeredSubscription*>(subIdStr, trigs)); } } releaseMongoConnection(connection); return true; }
void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name , const string sname , v8::Handle<v8::Value> value ){ if ( value->IsString() ){ b.append( sname.c_str() , toSTLString( value ).c_str() ); return; } if ( value->IsFunction() ){ b.appendCode( sname.c_str() , toSTLString( value ).c_str() ); return; } if ( value->IsNumber() ){ if ( value->IsInt32() ) b.append( sname.c_str(), int( value->ToInt32()->Value() ) ); else b.append( sname.c_str() , value->ToNumber()->Value() ); return; } if ( value->IsArray() ){ BSONObj sub = v8ToMongo( value->ToObject() ); b.appendArray( sname.c_str() , sub ); return; } if ( value->IsDate() ){ b.appendDate( sname.c_str() , Date_t(v8::Date::Cast( *value )->NumberValue()) ); return; } if ( value->IsExternal() ) return; if ( value->IsObject() ){ // The user could potentially modify the fields of these special objects, // wreaking havoc when we attempt to reinterpret them. Not doing any validation // for now... Local< v8::Object > obj = value->ToObject(); if ( obj->InternalFieldCount() && obj->GetInternalField( 0 )->IsNumber() ) { switch( obj->GetInternalField( 0 )->ToInt32()->Value() ) { // NOTE Uint32's Value() gave me a linking error, so going with this instead case Timestamp: b.appendTimestamp( sname.c_str(), Date_t( v8::Date::Cast( *obj->Get( v8::String::New( "time" ) ) )->NumberValue() ), obj->Get( v8::String::New( "i" ) )->ToInt32()->Value() ); return; case MinKey: b.appendMinKey( sname.c_str() ); return; case MaxKey: b.appendMaxKey( sname.c_str() ); return; default: assert( "invalid internal field" == 0 ); } } string s = toSTLString( value ); if ( s.size() && s[0] == '/' ){ s = s.substr( 1 ); string r = s.substr( 0 , s.rfind( "/" ) ); string o = s.substr( s.rfind( "/" ) + 1 ); b.appendRegex( sname.c_str() , r.c_str() , o.c_str() ); } else if ( value->ToObject()->GetPrototype()->IsObject() && value->ToObject()->GetPrototype()->ToObject()->HasRealNamedProperty( v8::String::New( "isObjectId" ) ) ){ OID oid; oid.init( toSTLString( value ) ); b.appendOID( sname.c_str() , &oid ); } else if ( !value->ToObject()->GetHiddenValue( v8::String::New( "__NumberLong" ) ).IsEmpty() ) { // TODO might be nice to potentially speed this up with an indexed internal // field, but I don't yet know how to use an ObjectTemplate with a // constructor. unsigned long long val = ( (unsigned long long)( value->ToObject()->Get( v8::String::New( "top" ) )->ToInt32()->Value() ) << 32 ) + (unsigned)( value->ToObject()->Get( v8::String::New( "bottom" ) )->ToInt32()->Value() ); b.append( sname.c_str(), (long long)val ); } else if ( !value->ToObject()->GetHiddenValue( v8::String::New( "__DBPointer" ) ).IsEmpty() ) { OID oid; oid.init( toSTLString( value->ToObject()->Get( v8::String::New( "id" ) ) ) ); string ns = toSTLString( value->ToObject()->Get( v8::String::New( "ns" ) ) ); b.appendDBRef( sname.c_str(), ns.c_str(), oid ); } else if ( !value->ToObject()->GetHiddenValue( v8::String::New( "__BinData" ) ).IsEmpty() ) { int len = obj->Get( v8::String::New( "len" ) )->ToInt32()->Value(); v8::String::Utf8Value data( obj->Get( v8::String::New( "data" ) ) ); const char *dataArray = *data; assert( data.length() == len ); b.appendBinData( sname.c_str(), len, mongo::BinDataType( obj->Get( v8::String::New( "type" ) )->ToInt32()->Value() ), dataArray ); } else { BSONObj sub = v8ToMongo( value->ToObject() ); b.append( sname.c_str() , sub ); } return; } if ( value->IsBoolean() ){ b.appendBool( sname.c_str() , value->ToBoolean()->Value() ); return; } else if ( value->IsUndefined() ){ b.appendUndefined( sname.c_str() ); return; } else if ( value->IsNull() ){ b.appendNull( sname.c_str() ); return; } cout << "don't know how to convert to mongo field [" << name << "]\t" << value << endl; }
/* **************************************************************************** * * addTriggeredSubscriptions * */ static bool addTriggeredSubscriptions(ContextRegistration cr, map<string, TriggeredSubscription*>& subs, std::string& err, std::string tenant) { DBClientBase* connection = NULL; BSONArrayBuilder entitiesNoPatternA; std::vector<std::string> idJsV; std::vector<std::string> typeJsV; for (unsigned int ix = 0; ix < cr.entityIdVector.size(); ++ix ) { //FIXME: take into account subscriptions with no type EntityId* enP = cr.entityIdVector.get(ix); /* The registration of isPattern=true entities is not supported, so we don't include them here */ if (enP->isPattern == "false") { entitiesNoPatternA.append(BSON(CASUB_ENTITY_ID << enP->id << CASUB_ENTITY_TYPE << enP->type << CASUB_ENTITY_ISPATTERN << "false")); idJsV.push_back(enP->id); typeJsV.push_back(enP->type); } } BSONArrayBuilder attrA; for (unsigned int ix = 0; ix < cr.contextRegistrationAttributeVector.size(); ++ix) { ContextRegistrationAttribute* craP = cr.contextRegistrationAttributeVector.get(ix); attrA.append(craP->name); } BSONObjBuilder queryNoPattern; queryNoPattern.append(CASUB_ENTITIES, BSON("$in" << entitiesNoPatternA.arr())); if (attrA.arrSize() > 0) { /* If we don't do this checking, the {$in: [] } in the attribute name part will * make the query fail*/ //queryB.append(CASUB_ATTRS, BSON("$in" << attrA.arr())); queryNoPattern.append("$or", BSON_ARRAY( BSON(CASUB_ATTRS << BSON("$in" << attrA.arr())) << BSON(CASUB_ATTRS << BSON("$size" << 0)) )); } else { queryNoPattern.append(CASUB_ATTRS, BSON("$size" << 0)); } queryNoPattern.append(CASUB_EXPIRATION, BSON("$gt" << (long long) getCurrentTime())); /* This is JavaScript code that runs in MongoDB engine. As far as I know, this is the only * way to do a "reverse regex" query in MongoDB (see * http://stackoverflow.com/questions/15966991/mongodb-reverse-regex/15989520). * Note that although we are using a isPattern=true in the MongoDB query besides $where, we * also need to check that in the if statement in the JavaScript function given that a given * sub document could include both isPattern=true and isPattern=false documents */ std::string idJsString = "[ "; for (unsigned int ix = 0; ix < idJsV.size(); ++ix ) { if (ix != idJsV.size()-1) { idJsString += "\""+idJsV[ix]+ "\" ,"; } else { idJsString += "\"" +idJsV[ix]+ "\""; } } idJsString += " ]"; std::string typeJsString = "[ "; for (unsigned int ix = 0; ix < typeJsV.size(); ++ix ) { if (ix != typeJsV.size()-1) { typeJsString += "\"" +typeJsV[ix] + "\" ,"; } else { typeJsString += "\"" + typeJsV[ix] + "\""; } } typeJsString += " ]"; std::string function = std::string("function()") + "{" + "enId = "+idJsString+ ";" + "enType = "+typeJsString+ ";" + "for (var i=0; i < this."+CASUB_ENTITIES+".length; i++) {" + "if (this."+CASUB_ENTITIES+"[i]."+CASUB_ENTITY_ISPATTERN+" == \"true\") {" + "for (var j=0; j < enId.length; j++) {" + "if (enId[j].match(this."+CASUB_ENTITIES+"[i]."+CASUB_ENTITY_ID+") && this."+CASUB_ENTITIES+"[i]."+CASUB_ENTITY_TYPE+" == enType[j]) {" + "return true; " + "}" + "}" + "}" + "}" + "return false; " + "}"; LM_T(LmtMongo, ("JS function: %s", function.c_str())); std::string entPatternQ = CSUB_ENTITIES "." CSUB_ENTITY_ISPATTERN; BSONObjBuilder queryPattern; queryPattern.append(entPatternQ, "true"); queryPattern.append(CASUB_EXPIRATION, BSON("$gt" << (long long) getCurrentTime())); queryPattern.appendCode("$where", function); BSONObj query = BSON("$or" << BSON_ARRAY(queryNoPattern.obj() << queryPattern.obj())); /* Do the query */ auto_ptr<DBClientCursor> cursor; LM_T(LmtMongo, ("query() in '%s' collection: '%s'", getSubscribeContextAvailabilityCollectionName(tenant).c_str(), query.toString().c_str())); try { connection = getMongoConnection(); cursor = connection->query(getSubscribeContextAvailabilityCollectionName(tenant).c_str(), query); /* * We have observed that in some cases of DB errors (e.g. the database daemon is down) instead of * raising an exception, the query() method sets the cursor to NULL. In this case, we raise the * exception ourselves */ if (cursor.get() == NULL) { throw DBException("Null cursor from mongo (details on this is found in the source code)", 0); } releaseMongoConnection(connection); LM_I(("Database Operation Successful (%s)", query.toString().c_str())); } catch (const DBException &e) { releaseMongoConnection(connection); err = std::string("collection: ") + getSubscribeContextAvailabilityCollectionName(tenant).c_str() + " - query(): " + query.toString() + " - exception: " + e.what(); LM_E(("Database Error (%s)", err.c_str())); return false; } catch (...) { releaseMongoConnection(connection); err = std::string("collection: ") + getSubscribeContextAvailabilityCollectionName(tenant).c_str() + " - query(): " + query.toString() + " - exception: " + "generic"; LM_E(("Database Error (%s)", err.c_str())); return false; } /* For each one of the subscriptions found, add it to the map (if not already there) */ while (cursor->more()) { BSONObj sub = cursor->next(); BSONElement idField = sub.getField("_id"); // // BSONElement::eoo returns true if 'not found', i.e. the field "_id" doesn't exist in 'sub' // // Now, if 'sub.getField("_id")' is not found, if we continue, calling OID() on it, then we get // an exception and the broker crashes. // if (idField.eoo() == true) { LM_E(("Database Error (error retrieving _id field in doc: %s)", sub.toString().c_str())); continue; } std::string subIdStr = idField.OID().toString(); if (subs.count(subIdStr) == 0) { LM_T(LmtMongo, ("adding subscription: '%s'", sub.toString().c_str())); TriggeredSubscription* trigs = new TriggeredSubscription(sub.hasField(CASUB_FORMAT) ? stringToFormat(STR_FIELD(sub, CASUB_FORMAT)) : XML, STR_FIELD(sub, CASUB_REFERENCE), subToAttributeList(sub)); subs.insert(std::pair<string, TriggeredSubscription*>(subIdStr, trigs)); } } return true; }