diff --git a/db/btree.h b/db/btree.h index d49db2a8729..5413b270d78 100644 --- a/db/btree.h +++ b/db/btree.h @@ -263,11 +263,15 @@ namespace mongo { return s; } + BSONObj prettyKey( const BSONObj &key ) const { + return key.replaceFieldNames( indexDetails.keyPattern() ).clientReadable(); + } + virtual BSONObj prettyStartKey() const { - return startKey.replaceFieldNames( indexDetails.keyPattern() ).clientReadable(); + return prettyKey( startKey ); } virtual BSONObj prettyEndKey() const { - return endKey.replaceFieldNames( indexDetails.keyPattern() ).clientReadable(); + return prettyKey( endKey ); } private: diff --git a/db/dbcommands.cpp b/db/dbcommands.cpp index 5eaddaba180..6e68db5fe8b 100644 --- a/db/dbcommands.cpp +++ b/db/dbcommands.cpp @@ -798,6 +798,53 @@ namespace mongo { } } cmdFileMD5; + class CmdMedianKey : public Command { + public: + CmdMedianKey() : Command( "medianKey" ) {} + virtual bool slaveOk() { return true; } + virtual void help( stringstream &help ) const { + help << " example: { medianKey:\"blog.posts\", keyPattern:{x:1} min:{x:10}, max:{x:55} }"; + } + bool run(const char *dbname, BSONObj& jsobj, string& errmsg, BSONObjBuilder& result, bool fromRepl ){ + const char *ns = jsobj.getStringField( "medianKey" ); + BSONObj keyPattern = jsobj.getObjectField( "keyPattern" ); + BSONObj min = jsobj.getObjectField( "min" ).extractFieldsUnDotted( keyPattern ); + BSONObj max = jsobj.getObjectField( "max" ).extractFieldsUnDotted( keyPattern ); + if ( ns[ 0 ] == '\0' || keyPattern.isEmpty() || min.isEmpty() || max.isEmpty() ) { + errmsg = "invalid command syntax"; + return false; + } + setClient( ns ); + const IndexDetails *id = 0; + NamespaceDetails *d = nsdetails( ns ); + for (int i = 0; i < d->nIndexes; i++ ) { + IndexDetails& ii = d->indexes[i]; + if( ii.keyPattern().woCompare(keyPattern) == 0 ) { + id = ⅈ + break; + } + } + if ( !id ) { + errmsg = "no index found for specified keyPattern"; + return false; + } + + Timer t; + int num = 0; + for( BtreeCursor c( *id, min, max, 1 ); c.ok(); c.advance(), ++num ); + num /= 2; + BtreeCursor c( *id, min, max, 1 ); + for( ; num; c.advance(), --num ); + int ms = t.millis(); + if ( ms > 100 ) { + out() << "Finding median for index: " << keyPattern << " between " << min << " and " << max << " took " << ms << "ms." << endl; + } + + result.append( "median", c.prettyKey( c.currKey() ) ); + return true; + } + } cmdMedianKey; + extern map *commands; /* TODO make these all command objects -- legacy stuff here diff --git a/jstests/median.js b/jstests/median.js new file mode 100644 index 00000000000..f90a91490f5 --- /dev/null +++ b/jstests/median.js @@ -0,0 +1,33 @@ +f = db.jstests_median; +f.drop(); + +f.ensureIndex( {i:1} ); +for( i = 0; i < 1000; ++i ) { + f.save( {i:i} ); +} + +assert.eq( 500, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1}, min:{i:0}, max:{i:999} } ).median.i ); +assert.eq( 0, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1}, min:{i:0}, max:{i:0} } ).median.i ); +assert.eq( 500, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1}, min:{i:500}, max:{i:500} } ).median.i ); +assert.eq( 1, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1}, min:{i:0}, max:{i:1} } ).median.i ); +assert.eq( 1, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1}, min:{i:0}, max:{i:2} } ).median.i ); + +f.drop(); +f.ensureIndex( {i:1,j:-1} ); +for( i = 0; i < 100; ++i ) { + for( j = 0; j < 100; ++j ) { + f.save( {i:i,j:j} ); + } +} + +assert.eq( 50, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1,j:-1}, min:{i:0,j:0}, max:{i:99,j:0} } ).median.i ); +assert.eq( 0, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1,j:-1}, min:{i:0,j:0}, max:{i:0,j:0} } ).median.i ); +assert.eq( 50, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1,j:-1}, min:{i:50,j:0}, max:{i:50,j:0} } ).median.i ); +assert.eq( 1, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1,j:-1}, min:{i:0,j:0}, max:{i:1,j:0} } ).median.i ); +assert.eq( 1, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1,j:-1}, min:{i:0,j:0}, max:{i:2,j:0} } ).median.i ); + +assert.eq( 49, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1,j:-1}, min:{i:0,j:99}, max:{i:0,j:0} } ).median.j ); +assert.eq( 44, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1,j:-1}, min:{i:0,j:49}, max:{i:0,j:40} } ).median.j ); + +assert.eq( 10, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1,j:-1}, min:{i:10,j:50}, max:{i:11,j:75} } ).median.i ); +assert.eq( 12, db.runCommand( {medianKey:"test.jstests_median", keyPattern:{i:1,j:-1}, min:{i:10,j:50}, max:{i:11,j:75} } ).median.j ); diff --git a/mongo.xcodeproj/project.pbxproj b/mongo.xcodeproj/project.pbxproj index c47ab2f4fd4..d9117af6249 100644 --- a/mongo.xcodeproj/project.pbxproj +++ b/mongo.xcodeproj/project.pbxproj @@ -246,6 +246,7 @@ 93D9469F0F7ABB0600C3C768 /* repl6.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = repl6.js; sourceTree = ""; }; 93D948200F7BF4FA00C3C768 /* remove5.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = remove5.js; sourceTree = ""; }; 93D948210F7BF4FA00C3C768 /* shellfork.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = shellfork.js; sourceTree = ""; }; + 93D949B40F7D2A7700C3C768 /* median.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = median.js; sourceTree = ""; }; 93E3C5310F704C9D0029011E /* repl4.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = repl4.js; sourceTree = ""; }; 93E3C5960F7149F40029011E /* repl5.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = repl5.js; sourceTree = ""; }; 93E727090F4B5B5B004F9B5D /* shardkey.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = shardkey.cpp; sourceTree = ""; }; @@ -530,6 +531,7 @@ 93A8D1D10F37544800C92B85 /* jstests */ = { isa = PBXGroup; children = ( + 93D949B40F7D2A7700C3C768 /* median.js */, 93D948200F7BF4FA00C3C768 /* remove5.js */, 93D948210F7BF4FA00C3C768 /* shellfork.js */, 93D19B300F5EF09C0084C329 /* clone */,