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:
parent
c737ba61df
commit
e8466f3dc5
@ -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 );
|
||||
|
@ -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();
|
||||
|
@ -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. */
|
||||
|
@ -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 );
|
||||
|
@ -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 >();
|
||||
|
@ -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 >();
|
||||
|
Loading…
Reference in New Issue
Block a user