diff options
author | Mathias Stearn <mathias@10gen.com> | 2010-02-09 12:30:04 -0500 |
---|---|---|
committer | Mathias Stearn <mathias@10gen.com> | 2010-02-09 12:30:58 -0500 |
commit | 611e665e478cf780ca9117e2301be860d8400d71 (patch) | |
tree | b0f6e137e5345e678b50a6156a5ebe20894a293f | |
parent | 95fa5a3bc7e3f33dae53abe75180fba98b667f78 (diff) | |
download | mongo-611e665e478cf780ca9117e2301be860d8400d71.tar.gz |
Sharded findAndModify SHARDING-64
-rw-r--r-- | jstests/sharding/findandmodify1.js | 57 | ||||
-rw-r--r-- | s/commands_public.cpp | 93 |
2 files changed, 150 insertions, 0 deletions
diff --git a/jstests/sharding/findandmodify1.js b/jstests/sharding/findandmodify1.js new file mode 100644 index 00000000000..774701f38eb --- /dev/null +++ b/jstests/sharding/findandmodify1.js @@ -0,0 +1,57 @@ +s = new ShardingTest( "find_and_modify_sharded" , 2 ); + +s.adminCommand( { enablesharding : "test" } ); +db = s.getDB( "test" ); +primary = s.getServer( "test" ).getDB( "test" ); +seconday = s.getOther( primary ).getDB( "test" ); + +numObjs = 20; + +s.adminCommand( { shardcollection : "test.stuff" , key : {_id:1} } ); + +for (var i=0; i < numObjs; i++){ + db.stuff.insert({_id: i}); +} + +for (var i=0; i < numObjs; i+=2){ + s.adminCommand( { split: "test.stuff" , middle : {_id: i} } ); +} + +for (var i=0; i < numObjs; i+=4){ + s.adminCommand( { movechunk : "test.stuff" , find : {_id: i} , to : seconday.getMongo().name } ); +} + +//sorted update +for (var i=0; i < numObjs; i++){ + assert.eq(db.stuff.count({a:1}), i, "1 A"); + + var out = db.stuff.findAndModify({query: {a:null}, update: {$set: {a:1}}, sort: {_id:1}}); + + assert.eq(db.stuff.count({a:1}), i+1, "1 B"); + assert.eq(db.stuff.findOne({_id:i}).a, 1, "1 C"); + assert.eq(out._id, i, "1 D"); +} + +// unsorted update +for (var i=0; i < numObjs; i++){ + assert.eq(db.stuff.count({b:1}), i, "2 A"); + + var out = db.stuff.findAndModify({query: {b:null}, update: {$set: {b:1}}}); + + assert.eq(db.stuff.count({b:1}), i+1, "2 B"); + assert.eq(db.stuff.findOne({_id:out._id}).a, 1, "2 C"); +} + +//sorted remove (no query) +for (var i=0; i < numObjs; i++){ + assert.eq(db.stuff.count(), numObjs - i, "3 A"); + assert.eq(db.stuff.count({_id: i}), 1, "3 B"); + + var out = db.stuff.findAndModify({remove: true, sort: {_id:1}}); + + assert.eq(db.stuff.count(), numObjs - i - 1, "3 C"); + assert.eq(db.stuff.count({_id: i}), 0, "3 D"); + assert.eq(out._id, i, "3 E"); +} + +s.stop(); diff --git a/s/commands_public.cpp b/s/commands_public.cpp index 29baae466d4..1b4c985d09b 100644 --- a/s/commands_public.cpp +++ b/s/commands_public.cpp @@ -234,6 +234,99 @@ namespace mongo { } } collectionStatsCmd; + class FindAndModifyCmd : public PublicGridCommand { + public: + FindAndModifyCmd() : PublicGridCommand("findandmodify") { } + bool run(const char *ns, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool){ + string dbName = getDBName( ns ); + string collection = cmdObj.firstElement().valuestrsafe(); + string fullns = dbName + "." + collection; + + BSONObj filter = cmdObj.getObjectField("query"); + + DBConfig * conf = grid.getDBConfig( dbName , false ); + + if ( ! conf || ! conf->isShardingEnabled() || ! conf->isSharded( fullns ) ){ + return passthrough( conf , cmdObj , result); + } + + ChunkManager * cm = conf->getChunkManager( fullns ); + massert( 13002 , "how could chunk manager be null!" , cm ); + + vector<Chunk*> chunks; + cm->getChunksForQuery( chunks , filter ); + + BSONObj sort = cmdObj.getObjectField("sort"); + if (!sort.isEmpty()){ + ShardKeyPattern& sk = cm->getShardKey(); + { + BSONObjIterator k (sk.key()); + BSONObjIterator s (sort); + bool good = true; + while (k.more()){ + if (!s.more()){ + good = false; + break; + } + + BSONElement ke = k.next(); + BSONElement se = s.next(); + + // TODO consider values when we support compound keys + if (strcmp(ke.fieldName(), se.fieldName()) != 0){ + good = false; + break; + } + } + + uassert(13001, "Sort must match shard key for sharded findandmodify", good); + } + + std::sort(chunks.begin(), chunks.end(), ChunkCmp(sort)); + } + + for ( vector<Chunk*>::iterator i = chunks.begin() ; i != chunks.end() ; i++ ){ + Chunk * c = *i; + + ScopedDbConnection conn( c->getShard() ); + BSONObj res; + bool ok = conn->runCommand( conf->getName() , fixCmdObj(cmdObj, c) , res ); + conn.done(); + + if (ok || (strcmp(res["errmsg"].valuestrsafe(), "No matching object found") != 0)){ + result.appendElements(res); + return ok; + } + } + + return true; + } + + private: + BSONObj fixCmdObj(const BSONObj& cmdObj, const Chunk* chunk){ + assert(chunk); + + BSONObjBuilder b; + BSONObjIterator i(cmdObj); + bool foundQuery = false; + while (i.more()){ + BSONElement e = i.next(); + if (strcmp(e.fieldName(), "query") != 0){ + b.append(e); + }else{ + foundQuery = true; + b.append("query", ClusteredCursor::concatQuery(e.embeddedObjectUserCheck(), chunk->getFilter())); + } + } + + if (!foundQuery) + b.append("query", chunk->getFilter()); + + return b.obj(); + } + + } findAndModifyCmd; + class ConvertToCappedCmd : public NotAllowedOnShardedCollectionCmd { public: ConvertToCappedCmd() : NotAllowedOnShardedCollectionCmd("convertToCapped"){} |