0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00

flag to update multiple objects with $ operators SERVER-268

This commit is contained in:
Eliot Horowitz 2009-10-21 22:54:11 -04:00
parent c737ba61df
commit e8466f3dc5
6 changed files with 124 additions and 20 deletions

View File

@ -560,13 +560,16 @@ namespace mongo {
say( toSend );
}
void DBClientBase::update( const string & ns , Query query , BSONObj obj , bool upsert ) {
void DBClientBase::update( const string & ns , Query query , BSONObj obj , bool upsert , bool multi ) {
BufBuilder b;
b.append( (int)0 ); // reserverd
b.append( ns );
b.append( (int)upsert );
int flags = 0;
if ( upsert ) flags |= Option_Upsert;
if ( multi ) flags |= Option_Multi;
b.append( flags );
query.obj.appendSelfToBufBuilder( b );
obj.appendSelfToBufBuilder( b );

View File

@ -48,6 +48,11 @@ namespace mongo {
Option_NoCursorTimeout = 1 << 4
};
enum UpdateOptions {
Option_Upsert = 1 << 0,
Option_Multi = 1 << 1
};
class BSONObj;
/** Represents a Mongo query expression. Typically one uses the QUERY(...) macro to construct a Query object.
@ -301,7 +306,7 @@ namespace mongo {
virtual void remove( const string &ns , Query query, bool justOne = 0 ) = 0;
virtual void update( const string &ns , Query query , BSONObj obj , bool upsert = 0 ) = 0;
virtual void update( const string &ns , Query query , BSONObj obj , bool upsert = 0 , bool multi = 0 ) = 0;
virtual ~DBClientInterface() { }
};
@ -625,7 +630,7 @@ namespace mongo {
/**
updates objects matching query
*/
virtual void update( const string &ns , Query query , BSONObj obj , bool upsert = 0 );
virtual void update( const string &ns , Query query , BSONObj obj , bool upsert = 0 , bool multi = 0 );
/** Create an index if it does not already exist.
ensureIndex calls are remembered so it is safe/fast to call this function many
@ -842,8 +847,8 @@ namespace mongo {
}
/** update */
virtual void update( const string &ns , Query query , BSONObj obj , bool upsert = 0 ) {
return checkMaster().update(ns, query, obj, upsert);
virtual void update( const string &ns , Query query , BSONObj obj , bool upsert = 0 , bool multi = 0 ) {
return checkMaster().update(ns, query, obj, upsert,multi);
}
string toString();

View File

@ -349,8 +349,8 @@ namespace mongo {
uassert("update object too large", toupdate.objsize() <= MaxBSONObjectSize);
assert( toupdate.objsize() < m.data->dataLen() );
assert( query.objsize() + toupdate.objsize() < m.data->dataLen() );
bool upsert = flags & 1;
bool multi = flags & 2;
bool upsert = flags & Option_Upsert;
bool multi = flags & Option_Multi;
{
string s = query.toString();
/* todo: we shouldn't do all this ss stuff when we don't need it, it will slow us down. */

View File

@ -650,9 +650,7 @@ namespace mongo {
};
UpdateResult updateObjects(const char *ns, BSONObj updateobj, BSONObj pattern, bool upsert, bool multi, stringstream& ss, bool logop ) {
uassert("multi not coded yet", !multi);
UpdateResult updateObjects(const char *ns, BSONObj updateobjOrig, BSONObj patternOrig, bool upsert, bool multi, stringstream& ss, bool logop ) {
int profile = cc().database()->profile;
uassert("cannot update reserved $ collection", strchr(ns, '$') == 0 );
@ -661,15 +659,19 @@ namespace mongo {
uassert("cannot update system collection", legalClientSystemNS( ns , true ) );
}
QueryPlanSet qps( ns, pattern, BSONObj() );
QueryPlanSet qps( ns, patternOrig, BSONObj() );
UpdateOp original;
shared_ptr< UpdateOp > u = qps.runOp( original );
massert( u->exceptionMessage(), u->complete() );
auto_ptr< Cursor > c = u->c();
if ( c->ok() ) {
int numModded = 0;
while ( c->ok() ) {
Record *r = c->_current();
BSONObj js(r);
BSONObj pattern = patternOrig;
BSONObj updateobj = updateobjOrig;
if ( logop ) {
BSONObjBuilder idPattern;
BSONElement id;
@ -681,6 +683,9 @@ namespace mongo {
idPattern.append( id );
pattern = idPattern.obj();
}
else {
uassert( "multi-update requires all modified objects to have an _id" , ! multi );
}
}
/* note: we only update one row and quit. if you do multiple later,
@ -697,6 +702,9 @@ namespace mongo {
const char *firstField = updateobj.firstElement().fieldName();
if ( firstField[0] == '$' ) {
if ( multi )
updateobj = updateobj.copy();
ModSet mods;
mods.getMods(updateobj);
NamespaceDetailsTransient& ndt = NamespaceDetailsTransient::get(ns);
@ -719,10 +727,15 @@ namespace mongo {
}
logOp("u", ns, updateobj, &pattern );
}
return UpdateResult( 1 , 1 , 1 );
numModded++;
if ( ! multi )
break;
c->advance();
continue;
}
uassert( "multi update only works with $ operators" , ! multi );
BSONElementManipulator::lookForTimestamps( updateobj );
checkNoMods( updateobj );
theDataFileMgr.update(ns, r, c->currLoc(), updateobj.objdata(), updateobj.objsize(), ss);
@ -731,15 +744,19 @@ namespace mongo {
return UpdateResult( 1 , 0 , 1 );
}
if ( numModded )
return UpdateResult( 1 , 1 , numModded );
if ( profile )
ss << " nscanned:" << u->nscanned();
if ( upsert ) {
if ( updateobj.firstElement().fieldName()[0] == '$' ) {
if ( updateobjOrig.firstElement().fieldName()[0] == '$' ) {
/* upsert of an $inc. build a default */
ModSet mods;
mods.getMods(updateobj);
BSONObj newObj = pattern.copy();
mods.getMods(updateobjOrig);
BSONObj newObj = patternOrig.copy();
if ( mods.applyModsInPlace( newObj ) ) {
//
} else {
@ -755,12 +772,12 @@ namespace mongo {
return UpdateResult( 0 , 1 , 1 );
}
uassert( "multi update only works with $ operators" , ! multi );
checkNoMods( updateobj );
checkNoMods( updateobjOrig );
if ( profile )
ss << " upsert ";
theDataFileMgr.insert(ns, updateobj);
theDataFileMgr.insert(ns, updateobjOrig);
if ( logop )
logOp( "i", ns, updateobj );
logOp( "i", ns, updateobjOrig );
return UpdateResult( 0 , 0 , 1 );
}
return UpdateResult( 0 , 0 , 0 );

View File

@ -520,7 +520,49 @@ namespace ReplTests {
protected:
BSONObj o_, q_, u_, ou_;
};
class MultiInc : public Base {
public:
string s() const {
stringstream ss;
auto_ptr<DBClientCursor> cc = client()->query( ns() , Query().sort( BSON( "_id" << 1 ) ) );
bool first = true;
while ( cc->more() ){
if ( first ) first = false;
else ss << ",";
BSONObj o = cc->next();
ss << o["x"].numberInt();
}
return ss.str();
}
void doIt() const {
client()->insert( ns(), BSON( "_id" << 1 << "x" << 1 ) );
client()->insert( ns(), BSON( "_id" << 2 << "x" << 5 ) );
ASSERT_EQUALS( "1,5" , s() );
client()->update( ns() , BSON( "_id" << 1 ) , BSON( "$inc" << BSON( "x" << 1 ) ) );
ASSERT_EQUALS( "2,5" , s() );
client()->update( ns() , BSONObj() , BSON( "$inc" << BSON( "x" << 1 ) ) );
ASSERT_EQUALS( "3,5" , s() );
client()->update( ns() , BSONObj() , BSON( "$inc" << BSON( "x" << 1 ) ) , false , true );
check();
}
void check() const {
ASSERT_EQUALS( "4,6" , s() );
}
void reset() const {
deleteAll( ns() );
}
};
class UpdateWithoutPreexistingId : public Base {
public:
UpdateWithoutPreexistingId() :
@ -959,6 +1001,7 @@ namespace ReplTests {
add< Idempotence::UpsertInsertIdMod >();
add< Idempotence::UpsertInsertSet >();
add< Idempotence::UpsertInsertInc >();
add< Idempotence::MultiInc >();
// Don't worry about this until someone wants this functionality.
// add< Idempotence::UpdateWithoutPreexistingId >();
add< Idempotence::Remove >();

View File

@ -238,6 +238,41 @@ namespace UpdateTests {
}
};
class MultiInc : public SetBase {
public:
string s(){
stringstream ss;
auto_ptr<DBClientCursor> cc = client().query( ns() , Query().sort( BSON( "_id" << 1 ) ) );
bool first = true;
while ( cc->more() ){
if ( first ) first = false;
else ss << ",";
BSONObj o = cc->next();
ss << o["x"].numberInt();
}
return ss.str();
}
void run(){
client().insert( ns(), BSON( "_id" << 1 << "x" << 1 ) );
client().insert( ns(), BSON( "_id" << 2 << "x" << 5 ) );
ASSERT_EQUALS( "1,5" , s() );
client().update( ns() , BSON( "_id" << 1 ) , BSON( "$inc" << BSON( "x" << 1 ) ) );
ASSERT_EQUALS( "2,5" , s() );
client().update( ns() , BSONObj() , BSON( "$inc" << BSON( "x" << 1 ) ) );
ASSERT_EQUALS( "3,5" , s() );
client().update( ns() , BSONObj() , BSON( "$inc" << BSON( "x" << 1 ) ) , false , true );
ASSERT_EQUALS( "4,6" , s() );
}
};
class UnorderedNewSet : public SetBase {
public:
void run() {
@ -508,6 +543,7 @@ namespace UpdateTests {
add< SetMissingDotted >();
add< SetAdjacentDotted >();
add< IncMissing >();
add< MultiInc >();
add< UnorderedNewSet >();
add< UnorderedNewSetAdjacent >();
add< ArrayEmbeddedSet >();