2009-09-21 15:55:24 -04:00
// mr.cpp
/**
2009-09-24 10:51:27 -04:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License , version 3 ,
* as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2009-09-21 15:55:24 -04:00
2010-04-27 15:27:52 -04:00
# include "pch.h"
2010-09-30 17:47:44 -04:00
# include "../db.h"
# include "../instance.h"
# include "../commands.h"
# include "../../scripting/engine.h"
# include "../../client/dbclient.h"
# include "../../client/connpool.h"
# include "../../client/parallel.h"
# include "../queryoptimizer.h"
# include "../matcher.h"
# include "../clientcursor.h"
2011-03-29 16:56:21 -07:00
# include "../replutil.h"
2011-01-22 00:27:28 -05:00
# include "../../s/d_chunk_manager.h"
# include "../../s/d_logic.h"
2009-09-21 15:55:24 -04:00
2010-11-15 14:39:42 -05:00
# include "mr.h"
2009-09-21 15:55:24 -04:00
namespace mongo {
2009-09-24 10:51:27 -04:00
namespace mr {
2009-11-04 17:11:17 -05:00
2010-12-06 17:39:05 -05:00
AtomicUInt Config : : JOB_NUMBER ;
2009-11-04 17:11:17 -05:00
2011-01-04 00:40:41 -05:00
JSFunction : : JSFunction ( string type , const BSONElement & e ) {
2010-12-14 11:23:40 -05:00
_type = type ;
_code = e . _asCode ( ) ;
2011-01-04 00:40:41 -05:00
2010-12-14 11:23:40 -05:00
if ( e . type ( ) = = CodeWScope )
_wantedScope = e . codeWScopeObject ( ) ;
}
2011-01-04 00:40:41 -05:00
void JSFunction : : init ( State * state ) {
2010-12-14 14:09:50 -05:00
_scope = state - > scope ( ) ;
2010-12-14 11:23:40 -05:00
assert ( _scope ) ;
_scope - > init ( & _wantedScope ) ;
2011-01-04 00:40:41 -05:00
2010-12-14 11:23:40 -05:00
_func = _scope - > createFunction ( _code . c_str ( ) ) ;
uassert ( 13598 , str : : stream ( ) < < " couldn't compile code for: " < < _type , _func ) ;
2011-05-05 16:30:55 -07:00
// install in JS scope so that it can be called in JS mode
_scope - > setFunction ( _type . c_str ( ) , _code . c_str ( ) ) ;
2010-12-14 11:23:40 -05:00
}
2011-01-04 00:40:41 -05:00
void JSMapper : : init ( State * state ) {
_func . init ( state ) ;
_params = state - > config ( ) . mapParams ;
2010-12-14 11:23:40 -05:00
}
2011-01-24 15:07:54 -08:00
/**
* Applies the map function to an object , which should internally call emit ( )
*/
2011-01-04 00:40:41 -05:00
void JSMapper : : map ( const BSONObj & o ) {
2010-12-14 11:23:40 -05:00
Scope * s = _func . scope ( ) ;
assert ( s ) ;
2011-05-25 17:41:40 -07:00
if ( s - > invoke ( _func . func ( ) , & _params , & o , 0 , true , false , true ) )
2010-12-14 11:23:40 -05:00
throw UserException ( 9014 , str : : stream ( ) < < " map invoke failed: " + s - > getError ( ) ) ;
}
2011-01-24 15:07:54 -08:00
/**
* Applies the finalize function to a tuple obj ( key , val )
* Returns tuple obj { _id : key , value : newval }
*/
2011-01-04 00:40:41 -05:00
BSONObj JSFinalizer : : finalize ( const BSONObj & o ) {
2010-12-14 11:23:40 -05:00
Scope * s = _func . scope ( ) ;
Scope : : NoDBAccess no = s - > disableDBAccess ( " can't access db inside finalize " ) ;
2011-04-27 11:57:10 -07:00
s - > invokeSafe ( _func . func ( ) , & o , 0 ) ;
2011-01-04 00:40:41 -05:00
// don't want to use o.objsize() to size b
2010-12-14 11:23:40 -05:00
// since there are many cases where the point of finalize
// is converting many fields to 1
2011-01-04 00:40:41 -05:00
BSONObjBuilder b ;
2010-12-16 16:38:24 -05:00
b . append ( o . firstElement ( ) ) ;
2010-12-14 11:23:40 -05:00
s - > append ( b , " value " , " return " ) ;
return b . obj ( ) ;
}
2011-05-05 16:30:55 -07:00
void JSReducer : : init ( State * state ) {
_func . init ( state ) ;
}
2011-01-24 15:07:54 -08:00
/**
* Reduces a list of tuple objects ( key , value ) to a single tuple { " 0 " : key , " 1 " : value }
*/
2011-01-04 00:40:41 -05:00
BSONObj JSReducer : : reduce ( const BSONList & tuples ) {
2011-01-24 15:07:54 -08:00
if ( tuples . size ( ) < = 1 )
return tuples [ 0 ] ;
2010-12-14 11:23:40 -05:00
BSONObj key ;
int endSizeEstimate = 16 ;
_reduce ( tuples , key , endSizeEstimate ) ;
BSONObjBuilder b ( endSizeEstimate ) ;
b . appendAs ( key . firstElement ( ) , " 0 " ) ;
_func . scope ( ) - > append ( b , " 1 " , " return " ) ;
return b . obj ( ) ;
}
2011-01-04 00:40:41 -05:00
2011-01-24 15:07:54 -08:00
/**
* Reduces a list of tuple object ( key , value ) to a single tuple { _id : key , value : val }
* Also applies a finalizer method if present .
*/
BSONObj JSReducer : : finalReduce ( const BSONList & tuples , Finalizer * finalizer ) {
2010-12-14 11:23:40 -05:00
2011-01-24 15:07:54 -08:00
BSONObj res ;
2009-11-04 17:11:17 -05:00
BSONObj key ;
2010-12-14 11:23:40 -05:00
2011-01-24 15:07:54 -08:00
if ( tuples . size ( ) = = 1 ) {
// 1 obj, just use it
key = tuples [ 0 ] ;
BSONObjBuilder b ( key . objsize ( ) ) ;
BSONObjIterator it ( key ) ;
b . appendAs ( it . next ( ) , " _id " ) ;
b . appendAs ( it . next ( ) , " value " ) ;
res = b . obj ( ) ;
}
else {
// need to reduce
int endSizeEstimate = 16 ;
_reduce ( tuples , key , endSizeEstimate ) ;
BSONObjBuilder b ( endSizeEstimate ) ;
b . appendAs ( key . firstElement ( ) , " _id " ) ;
_func . scope ( ) - > append ( b , " value " , " return " ) ;
res = b . obj ( ) ;
}
2010-12-14 11:23:40 -05:00
2011-01-04 00:40:41 -05:00
if ( finalizer ) {
2010-12-14 11:23:40 -05:00
res = finalizer - > finalize ( res ) ;
}
return res ;
}
2009-10-07 12:57:45 -04:00
2011-01-24 15:07:54 -08:00
/**
* actually applies a reduce , to a list of tuples ( key , value ) .
* After the call , tuples will hold a single tuple { " 0 " : key , " 1 " : value }
*/
2011-01-04 00:40:41 -05:00
void JSReducer : : _reduce ( const BSONList & tuples , BSONObj & key , int & endSizeEstimate ) {
2010-12-14 11:23:40 -05:00
uassert ( 10074 , " need values " , tuples . size ( ) ) ;
2011-01-04 00:40:41 -05:00
2010-12-14 11:23:40 -05:00
int sizeEstimate = ( tuples . size ( ) * tuples . begin ( ) - > getField ( " value " ) . size ( ) ) + 128 ;
2011-01-04 00:40:41 -05:00
2011-01-24 15:07:54 -08:00
// need to build the reduce args: ( key, [values] )
2009-11-04 17:11:17 -05:00
BSONObjBuilder reduceArgs ( sizeEstimate ) ;
2010-07-07 16:12:16 -04:00
boost : : scoped_ptr < BSONArrayBuilder > valueBuilder ;
2010-03-19 09:55:24 -04:00
int sizeSoFar = 0 ;
unsigned n = 0 ;
2011-01-04 00:40:41 -05:00
for ( ; n < tuples . size ( ) ; n + + ) {
2010-12-14 11:23:40 -05:00
BSONObjIterator j ( tuples [ n ] ) ;
2009-11-04 17:11:17 -05:00
BSONElement keyE = j . next ( ) ;
2011-01-04 00:40:41 -05:00
if ( n = = 0 ) {
2009-11-04 17:11:17 -05:00
reduceArgs . append ( keyE ) ;
2010-03-14 01:18:14 -05:00
key = keyE . wrap ( ) ;
2010-03-19 09:55:24 -04:00
sizeSoFar = 5 + keyE . size ( ) ;
2010-12-14 11:23:40 -05:00
valueBuilder . reset ( new BSONArrayBuilder ( reduceArgs . subarrayStart ( " tuples " ) ) ) ;
2009-11-04 17:11:17 -05:00
}
2011-01-04 00:40:41 -05:00
2010-03-19 09:55:24 -04:00
BSONElement ee = j . next ( ) ;
2011-01-04 00:40:41 -05:00
2011-01-24 15:07:54 -08:00
uassert ( 13070 , " value too large to reduce " , ee . size ( ) < ( BSONObjMaxUserSize / 2 ) ) ;
2010-03-19 09:55:24 -04:00
2011-01-04 00:40:41 -05:00
if ( sizeSoFar + ee . size ( ) > BSONObjMaxUserSize ) {
2010-03-19 09:55:24 -04:00
assert ( n > 1 ) ; // if not, inf. loop
break ;
}
2011-01-04 00:40:41 -05:00
2010-03-19 09:55:24 -04:00
valueBuilder - > append ( ee ) ;
sizeSoFar + = ee . size ( ) ;
2009-11-04 17:11:17 -05:00
}
2010-03-14 01:18:14 -05:00
assert ( valueBuilder ) ;
valueBuilder - > done ( ) ;
2011-01-04 00:40:41 -05:00
BSONObj args = reduceArgs . obj ( ) ;
2010-03-14 01:18:14 -05:00
2010-12-14 11:23:40 -05:00
Scope * s = _func . scope ( ) ;
2010-03-19 09:55:24 -04:00
2011-10-04 16:29:59 -07:00
s - > invokeSafe ( _func . func ( ) , & args , 0 , 0 , false , true , true ) ;
2011-05-13 14:59:10 -07:00
+ + numReduces ;
2010-03-14 01:18:14 -05:00
2011-01-04 00:40:41 -05:00
if ( s - > type ( " return " ) = = Array ) {
2010-12-14 11:23:40 -05:00
uasserted ( 10075 , " reduce -> multiple not supported yet " ) ;
return ;
2010-03-19 09:55:24 -04:00
}
2010-12-14 11:23:40 -05:00
endSizeEstimate = key . objsize ( ) + ( args . objsize ( ) / tuples . size ( ) ) ;
2010-03-19 09:55:24 -04:00
2010-12-14 11:23:40 -05:00
if ( n = = tuples . size ( ) )
return ;
2011-01-04 00:40:41 -05:00
2011-01-24 15:07:54 -08:00
// the input list was too large, add the rest of elmts to new tuples and reduce again
// note: would be better to use loop instead of recursion to avoid stack overflow
2010-12-14 11:23:40 -05:00
BSONList x ;
2011-01-04 00:40:41 -05:00
for ( ; n < tuples . size ( ) ; n + + ) {
2010-12-14 11:23:40 -05:00
x . push_back ( tuples [ n ] ) ;
}
BSONObjBuilder temp ( endSizeEstimate ) ;
temp . append ( key . firstElement ( ) ) ;
s - > append ( temp , " 1 " , " return " ) ;
x . push_back ( temp . obj ( ) ) ;
_reduce ( x , key , endSizeEstimate ) ;
2009-11-04 17:11:17 -05:00
}
2011-01-04 00:40:41 -05:00
Config : : Config ( const string & _dbname , const BSONObj & cmdObj ) {
2010-11-15 14:39:42 -05:00
dbname = _dbname ;
ns = dbname + " . " + cmdObj . firstElement ( ) . valuestr ( ) ;
2011-01-04 00:40:41 -05:00
2010-11-15 14:39:42 -05:00
verbose = cmdObj [ " verbose " ] . trueValue ( ) ;
2011-05-05 16:30:55 -07:00
jsMode = cmdObj [ " jsMode " ] . trueValue ( ) ;
2009-11-04 15:25:45 -05:00
2011-05-13 14:59:10 -07:00
jsMaxKeys = 500000 ;
2011-05-13 15:43:50 -07:00
reduceTriggerRatio = 2.0 ;
maxInMemSize = 5 * 1024 * 1024 ;
2011-05-13 14:59:10 -07:00
2010-12-16 16:38:24 -05:00
uassert ( 13602 , " outType is no longer a valid option " , cmdObj [ " outType " ] . eoo ( ) ) ;
2010-11-15 14:39:42 -05:00
2011-01-04 00:40:41 -05:00
if ( cmdObj [ " out " ] . type ( ) = = String ) {
2010-12-16 16:38:24 -05:00
finalShort = cmdObj [ " out " ] . String ( ) ;
outType = REPLACE ;
2010-11-15 14:39:42 -05:00
}
2011-01-04 00:40:41 -05:00
else if ( cmdObj [ " out " ] . type ( ) = = Object ) {
2010-12-16 16:38:24 -05:00
BSONObj o = cmdObj [ " out " ] . embeddedObject ( ) ;
BSONElement e = o . firstElement ( ) ;
string t = e . fieldName ( ) ;
2011-01-04 00:40:41 -05:00
if ( t = = " normal " | | t = = " replace " ) {
2010-12-14 14:09:50 -05:00
outType = REPLACE ;
2010-12-16 16:38:24 -05:00
finalShort = e . String ( ) ;
}
2011-01-04 00:40:41 -05:00
else if ( t = = " merge " ) {
2010-11-15 14:39:42 -05:00
outType = MERGE ;
2010-12-16 16:38:24 -05:00
finalShort = e . String ( ) ;
}
2011-01-04 00:40:41 -05:00
else if ( t = = " reduce " ) {
2010-11-15 14:39:42 -05:00
outType = REDUCE ;
2010-12-16 16:38:24 -05:00
finalShort = e . String ( ) ;
}
2011-01-04 00:40:41 -05:00
else if ( t = = " inline " ) {
2010-12-16 16:38:24 -05:00
outType = INMEMORY ;
}
2011-01-04 00:40:41 -05:00
else {
2010-12-16 16:38:24 -05:00
uasserted ( 13522 , str : : stream ( ) < < " unknown out specifier [ " < < t < < " ] " ) ;
2011-01-04 00:40:41 -05:00
}
2011-01-05 22:05:10 -08:00
if ( o . hasElement ( " db " ) ) {
outDB = o [ " db " ] . String ( ) ;
}
2011-08-20 19:15:37 -07:00
if ( o . hasElement ( " nonAtomic " ) ) {
outNonAtomic = o [ " nonAtomic " ] . Bool ( ) ;
if ( outNonAtomic )
2011-09-13 15:03:53 -07:00
uassert ( 15895 , " nonAtomic option cannot be used with this output type " , ( outType = = REDUCE | | outType = = MERGE ) ) ;
2011-08-20 19:15:37 -07:00
}
2010-11-15 14:39:42 -05:00
}
else {
2010-12-16 16:38:24 -05:00
uasserted ( 13606 , " 'out' has to be a string or an object " ) ;
}
2011-01-04 00:40:41 -05:00
if ( outType ! = INMEMORY ) { // setup names
2011-06-27 14:10:38 -07:00
tempLong = str : : stream ( ) < < ( outDB . empty ( ) ? dbname : outDB ) < < " .tmp.mr. " < < cmdObj . firstElement ( ) . String ( ) < < " _ " < < JOB_NUMBER + + ;
2011-01-04 00:40:41 -05:00
2010-12-16 16:38:24 -05:00
incLong = tempLong + " _inc " ;
2011-01-04 00:40:41 -05:00
2011-01-05 22:05:10 -08:00
finalLong = str : : stream ( ) < < ( outDB . empty ( ) ? dbname : outDB ) < < " . " < < finalShort ;
2010-11-15 14:39:42 -05:00
}
2010-11-15 14:10:28 -05:00
2011-01-04 00:40:41 -05:00
{
// scope and code
2010-12-14 11:23:40 -05:00
if ( cmdObj [ " scope " ] . type ( ) = = Object )
scopeSetup = cmdObj [ " scope " ] . embeddedObjectUserCheck ( ) ;
2011-01-04 00:40:41 -05:00
2010-12-14 11:23:40 -05:00
mapper . reset ( new JSMapper ( cmdObj [ " map " ] ) ) ;
reducer . reset ( new JSReducer ( cmdObj [ " reduce " ] ) ) ;
2011-01-16 00:51:19 -05:00
if ( cmdObj [ " finalize " ] . type ( ) & & cmdObj [ " finalize " ] . trueValue ( ) )
2010-12-14 11:23:40 -05:00
finalizer . reset ( new JSFinalizer ( cmdObj [ " finalize " ] ) ) ;
2010-10-08 13:00:09 -04:00
2011-01-04 00:40:41 -05:00
if ( cmdObj [ " mapparams " ] . type ( ) = = Array ) {
2010-12-15 09:51:01 -05:00
mapParams = cmdObj [ " mapparams " ] . embeddedObjectUserCheck ( ) ;
2009-11-04 15:25:45 -05:00
}
2011-01-04 00:40:41 -05:00
2010-11-15 14:39:42 -05:00
}
2011-01-04 00:40:41 -05:00
{
// query options
2010-12-17 11:07:10 -05:00
BSONElement q = cmdObj [ " query " ] ;
if ( q . type ( ) = = Object )
filter = q . embeddedObjectUserCheck ( ) ;
2011-01-04 00:40:41 -05:00
else
2010-12-17 11:07:10 -05:00
uassert ( 13608 , " query has to be blank or an Object " , ! q . trueValue ( ) ) ;
2011-01-04 00:40:41 -05:00
2010-12-17 11:07:10 -05:00
BSONElement s = cmdObj [ " sort " ] ;
if ( s . type ( ) = = Object )
sort = s . embeddedObjectUserCheck ( ) ;
2011-01-04 00:40:41 -05:00
else
2010-12-17 11:07:10 -05:00
uassert ( 13609 , " sort has to be blank or an Object " , ! s . trueValue ( ) ) ;
2011-01-04 00:40:41 -05:00
2010-11-15 14:39:42 -05:00
if ( cmdObj [ " limit " ] . isNumber ( ) )
limit = cmdObj [ " limit " ] . numberLong ( ) ;
2011-01-04 00:40:41 -05:00
else
2010-11-15 14:39:42 -05:00
limit = 0 ;
2010-02-23 22:01:07 -05:00
}
2010-11-15 14:39:42 -05:00
}
2011-01-24 15:07:54 -08:00
/**
* Create temporary collection , set up indexes
*/
2011-01-04 00:40:41 -05:00
void State : : prepTempCollection ( ) {
2010-12-16 16:38:24 -05:00
if ( ! _onDisk )
return ;
2011-09-29 19:40:40 -04:00
Client : : GodScope _ ;
2011-06-29 15:43:54 -07:00
if ( _config . incLong ! = _config . tempLong ) {
// create the inc collection and make sure we have index on "0" key
_db . dropCollection ( _config . incLong ) ;
{
writelock l ( _config . incLong ) ;
Client : : Context ctx ( _config . incLong ) ;
string err ;
if ( ! userCreateNS ( _config . incLong . c_str ( ) , BSON ( " autoIndexId " < < 0 ) , err , false ) ) {
uasserted ( 13631 , str : : stream ( ) < < " userCreateNS failed for mr incLong ns: " < < _config . incLong < < " err: " < < err ) ;
}
2011-06-27 13:29:30 -07:00
}
2011-06-29 15:43:54 -07:00
BSONObj sortKey = BSON ( " 0 " < < 1 ) ;
_db . ensureIndex ( _config . incLong , sortKey ) ;
}
2011-06-27 13:29:30 -07:00
// create temp collection
2011-06-29 15:43:54 -07:00
_db . dropCollection ( _config . tempLong ) ;
2011-01-04 00:40:41 -05:00
{
2010-12-14 14:09:50 -05:00
writelock lock ( _config . tempLong . c_str ( ) ) ;
Client : : Context ctx ( _config . tempLong . c_str ( ) ) ;
string errmsg ;
2011-01-21 12:44:00 -05:00
if ( ! userCreateNS ( _config . tempLong . c_str ( ) , BSONObj ( ) , errmsg , true ) ) {
uasserted ( 13630 , str : : stream ( ) < < " userCreateNS failed for mr tempLong ns: " < < _config . tempLong < < " err: " < < errmsg ) ;
}
2010-12-14 14:09:50 -05:00
}
2011-01-04 00:40:41 -05:00
{
// copy indexes
2010-12-14 14:09:50 -05:00
auto_ptr < DBClientCursor > idx = _db . getIndexes ( _config . finalLong ) ;
2011-01-04 00:40:41 -05:00
while ( idx - > more ( ) ) {
2010-12-14 14:09:50 -05:00
BSONObj i = idx - > next ( ) ;
2011-01-04 00:40:41 -05:00
2010-12-14 14:09:50 -05:00
BSONObjBuilder b ( i . objsize ( ) + 16 ) ;
b . append ( " ns " , _config . tempLong ) ;
BSONObjIterator j ( i ) ;
2011-01-04 00:40:41 -05:00
while ( j . more ( ) ) {
2010-12-14 14:09:50 -05:00
BSONElement e = j . next ( ) ;
2011-01-04 00:40:41 -05:00
if ( str : : equals ( e . fieldName ( ) , " _id " ) | |
str : : equals ( e . fieldName ( ) , " ns " ) )
2010-12-14 14:09:50 -05:00
continue ;
2011-01-04 00:40:41 -05:00
2010-12-14 14:09:50 -05:00
b . append ( e ) ;
2009-11-07 21:00:15 -05:00
}
2011-01-04 00:40:41 -05:00
2010-12-14 14:09:50 -05:00
BSONObj indexToInsert = b . obj ( ) ;
insert ( Namespace ( _config . tempLong . c_str ( ) ) . getSisterNS ( " system.indexes " ) . c_str ( ) , indexToInsert ) ;
}
2011-01-04 00:40:41 -05:00
2010-12-14 14:09:50 -05:00
}
2010-11-15 15:10:31 -05:00
2010-12-14 14:09:50 -05:00
}
2011-01-24 15:07:54 -08:00
/**
* For inline mode , appends results to output object .
* Makes sure ( key , value ) tuple is formatted as { _id : key , value : val }
*/
2011-01-04 00:40:41 -05:00
void State : : appendResults ( BSONObjBuilder & final ) {
2010-12-16 16:38:24 -05:00
if ( _onDisk )
return ;
2011-01-04 00:40:41 -05:00
2011-05-11 15:52:39 -07:00
if ( _jsMode ) {
ScriptingFunction getResult = _scope - > createFunction ( " var map = _mrMap; var result = []; for (key in map) { result.push({_id: key, value: map[key]}) } return result; " ) ;
_scope - > invoke ( getResult , 0 , 0 , 0 , false ) ;
BSONObj obj = _scope - > getObject ( " return " ) ;
final . append ( " results " , BSONArray ( obj ) ) ;
return ;
}
2011-09-19 16:31:08 -07:00
uassert ( 13604 , " too much data for in memory map/reduce " , _size < BSONObjMaxUserSize ) ;
2010-12-16 16:38:24 -05:00
BSONArrayBuilder b ( ( int ) ( _size * 1.2 ) ) ; // _size is data size, doesn't count overhead and keys
2011-01-04 00:40:41 -05:00
for ( InMemory : : iterator i = _temp - > begin ( ) ; i ! = _temp - > end ( ) ; + + i ) {
2010-12-16 16:38:24 -05:00
BSONObj key = i - > first ;
BSONList & all = i - > second ;
2011-01-04 00:40:41 -05:00
2010-12-16 16:38:24 -05:00
assert ( all . size ( ) = = 1 ) ;
BSONObjIterator vi ( all [ 0 ] ) ;
vi . next ( ) ;
BSONObjBuilder temp ( b . subobjStart ( ) ) ;
temp . appendAs ( key . firstElement ( ) , " _id " ) ;
temp . appendAs ( vi . next ( ) , " value " ) ;
temp . done ( ) ;
}
2011-01-04 00:40:41 -05:00
2010-12-16 16:38:24 -05:00
BSONArray res = b . arr ( ) ;
final . append ( " results " , res ) ;
}
2011-01-24 15:07:54 -08:00
/**
* Does post processing on output collection .
* This may involve replacing , merging or reducing .
*/
2011-09-21 18:08:41 -07:00
long long State : : postProcessCollection ( CurOp * op , ProgressMeterHolder & pm ) {
2011-01-16 00:38:18 -05:00
if ( _onDisk = = false | | _config . outType = = Config : : INMEMORY )
2010-12-16 16:38:24 -05:00
return _temp - > size ( ) ;
2011-01-04 00:40:41 -05:00
2011-09-29 19:40:40 -04:00
// Do following reads before writelock below so db.count's Context::_auth won't think we are trying to write when only trying to read. Needed when user has read-only permission. We allow read-only users to output only to new collections (or return inline).
bool newOutColl = ! _db . exists ( _config . finalLong ) ;
long long tempLongCount = _db . count ( _config . tempLong ) ;
if ( _config . outNonAtomic ) {
postProcessCollectionNonAtomic ( op , pm , newOutColl , tempLongCount ) ;
} else {
writelock lock ;
postProcessCollectionNonAtomic ( op , pm , newOutColl , tempLongCount ) ;
}
return _db . count ( _config . finalLong ) ;
2011-08-20 19:15:37 -07:00
}
2011-09-29 19:40:40 -04:00
void State : : postProcessCollectionNonAtomic ( CurOp * op , ProgressMeterHolder & pm , bool newOutColl , long long tempLongCount ) {
2010-12-16 16:38:24 -05:00
2010-12-14 14:09:50 -05:00
if ( _config . finalLong = = _config . tempLong )
2011-09-29 19:40:40 -04:00
return ;
2011-01-04 00:40:41 -05:00
2011-09-29 19:40:40 -04:00
if ( newOutColl ) {
Client : : GodScope _ ; // OK to output to new collection even if don't have write permission
writelock lock ;
BSONObj info ;
if ( ! _db . runCommand ( " admin " , BSON ( " renameCollection " < < _config . tempLong < < " to " < < _config . finalLong ) , info ) ) {
uasserted ( 15897 , str : : stream ( ) < < " rename failed: " < < info ) ;
}
}
else if ( _config . outType = = Config : : REPLACE ) {
2011-08-20 19:15:37 -07:00
writelock lock ;
2011-01-24 15:07:54 -08:00
// replace: just rename from temp to final collection name, dropping previous collection
2010-12-14 14:09:50 -05:00
_db . dropCollection ( _config . finalLong ) ;
BSONObj info ;
2011-04-20 09:06:34 -04:00
if ( ! _db . runCommand ( " admin " , BSON ( " renameCollection " < < _config . tempLong < < " to " < < _config . finalLong ) , info ) ) {
uasserted ( 10076 , str : : stream ( ) < < " rename failed: " < < info ) ;
}
2011-09-29 19:40:40 -04:00
//_db.dropCollection( _config.tempLong ); why drop after rename? -tony
2010-12-14 14:09:50 -05:00
}
2011-01-16 00:38:18 -05:00
else if ( _config . outType = = Config : : MERGE ) {
2011-01-24 15:07:54 -08:00
// merge: upsert new docs into old collection
2011-09-29 19:40:40 -04:00
op - > setMessage ( " m/r: merge post processing " , tempLongCount ) ;
2010-12-14 14:09:50 -05:00
auto_ptr < DBClientCursor > cursor = _db . query ( _config . tempLong , BSONObj ( ) ) ;
2011-01-04 00:40:41 -05:00
while ( cursor - > more ( ) ) {
2011-08-20 19:15:37 -07:00
writelock lock ;
2010-12-14 14:09:50 -05:00
BSONObj o = cursor - > next ( ) ;
Helpers : : upsert ( _config . finalLong , o ) ;
2011-02-11 19:27:08 -05:00
getDur ( ) . commitIfNeeded ( ) ;
2011-09-21 18:08:41 -07:00
pm . hit ( ) ;
2010-11-15 14:39:42 -05:00
}
2010-12-14 14:09:50 -05:00
_db . dropCollection ( _config . tempLong ) ;
2011-09-21 18:08:41 -07:00
pm . finished ( ) ;
2010-12-14 14:09:50 -05:00
}
2011-01-16 00:38:18 -05:00
else if ( _config . outType = = Config : : REDUCE ) {
2011-01-24 15:07:54 -08:00
// reduce: apply reduce op on new result and existing one
2010-12-14 14:09:50 -05:00
BSONList values ;
2011-01-04 00:40:41 -05:00
2011-09-29 19:40:40 -04:00
op - > setMessage ( " m/r: reduce post processing " , tempLongCount ) ;
2010-12-14 14:09:50 -05:00
auto_ptr < DBClientCursor > cursor = _db . query ( _config . tempLong , BSONObj ( ) ) ;
2011-01-04 00:40:41 -05:00
while ( cursor - > more ( ) ) {
2011-08-20 19:15:37 -07:00
writelock lock ;
2010-12-14 14:09:50 -05:00
BSONObj temp = cursor - > next ( ) ;
BSONObj old ;
2011-01-04 00:40:41 -05:00
2010-12-14 14:09:50 -05:00
bool found ;
{
Client : : Context tx ( _config . finalLong ) ;
found = Helpers : : findOne ( _config . finalLong . c_str ( ) , temp [ " _id " ] . wrap ( ) , old , true ) ;
}
2011-01-04 00:40:41 -05:00
if ( found ) {
2010-12-14 14:09:50 -05:00
// need to reduce
values . clear ( ) ;
values . push_back ( temp ) ;
values . push_back ( old ) ;
2011-01-24 15:07:54 -08:00
Helpers : : upsert ( _config . finalLong , _config . reducer - > finalReduce ( values , _config . finalizer . get ( ) ) ) ;
2010-12-14 14:09:50 -05:00
}
else {
Helpers : : upsert ( _config . finalLong , temp ) ;
}
2011-02-11 19:27:08 -05:00
getDur ( ) . commitIfNeeded ( ) ;
2011-09-21 18:08:41 -07:00
pm . hit ( ) ;
2009-11-04 15:25:45 -05:00
}
2010-12-14 14:09:50 -05:00
_db . dropCollection ( _config . tempLong ) ;
2011-09-21 18:08:41 -07:00
pm . finished ( ) ;
2009-11-04 15:25:45 -05:00
}
2010-11-15 14:39:42 -05:00
}
2011-01-04 00:40:41 -05:00
2011-01-24 15:07:54 -08:00
/**
* Insert doc in collection
*/
2011-05-05 16:30:55 -07:00
void State : : insert ( const string & ns , const BSONObj & o ) {
2010-12-16 16:38:24 -05:00
assert ( _onDisk ) ;
2010-11-15 14:39:42 -05:00
writelock l ( ns ) ;
Client : : Context ctx ( ns ) ;
2011-01-04 00:40:41 -05:00
2010-12-16 16:38:24 -05:00
theDataFileMgr . insertAndLog ( ns . c_str ( ) , o , false ) ;
2010-12-14 14:17:12 -05:00
}
2010-11-10 01:57:26 -05:00
2011-06-14 00:10:42 -07:00
/**
* Insert doc into the inc collection , taking proper lock
*/
void State : : insertToInc ( BSONObj & o ) {
2011-09-29 19:40:40 -04:00
Client : : GodScope _ ;
2011-06-14 00:10:42 -07:00
writelock l ( _config . incLong ) ;
Client : : Context ctx ( _config . incLong ) ;
_insertToInc ( o ) ;
}
2011-01-24 15:07:54 -08:00
/**
* Insert doc into the inc collection
*/
2011-01-04 00:40:41 -05:00
void State : : _insertToInc ( BSONObj & o ) {
2010-12-16 16:38:24 -05:00
assert ( _onDisk ) ;
2011-05-29 00:31:17 -04:00
theDataFileMgr . insertWithObjMod ( _config . incLong . c_str ( ) , o , true ) ;
2011-02-11 19:27:08 -05:00
getDur ( ) . commitIfNeeded ( ) ;
2010-11-15 14:39:42 -05:00
}
2010-11-10 01:57:26 -05:00
2011-03-31 08:23:40 -04:00
State : : State ( const Config & c ) : _config ( c ) , _size ( 0 ) , _dupCount ( 0 ) , _numEmits ( 0 ) {
2010-12-07 00:02:42 -05:00
_temp . reset ( new InMemory ( ) ) ;
2010-12-16 16:38:24 -05:00
_onDisk = _config . outType ! = Config : : INMEMORY ;
2010-11-15 15:10:31 -05:00
}
2010-12-14 14:37:24 -05:00
2011-01-04 00:40:41 -05:00
bool State : : sourceExists ( ) {
2010-12-14 14:37:24 -05:00
return _db . exists ( _config . ns ) ;
}
2011-01-04 00:40:41 -05:00
long long State : : incomingDocuments ( ) {
2011-01-04 23:16:44 -08:00
return _db . count ( _config . ns , _config . filter , QueryOption_SlaveOk , ( unsigned ) _config . limit ) ;
2010-12-14 14:37:24 -05:00
}
2011-01-04 00:40:41 -05:00
State : : ~ State ( ) {
if ( _onDisk ) {
2010-12-16 16:38:24 -05:00
try {
_db . dropCollection ( _config . tempLong ) ;
_db . dropCollection ( _config . incLong ) ;
}
2011-01-04 00:40:41 -05:00
catch ( std : : exception & e ) {
2010-12-16 16:38:24 -05:00
error ( ) < < " couldn't cleanup after map reduce: " < < e . what ( ) < < endl ;
}
}
2011-05-11 15:02:02 -07:00
2011-05-12 11:15:19 -07:00
if ( _scope ) {
// cleanup js objects
ScriptingFunction cleanup = _scope - > createFunction ( " delete _emitCt; delete _keyCt; delete _mrMap; " ) ;
_scope - > invoke ( cleanup , 0 , 0 , 0 , true ) ;
}
2010-12-14 14:37:24 -05:00
}
2011-01-24 15:07:54 -08:00
/**
* Initialize the mapreduce operation , creating the inc collection
*/
2011-01-04 00:40:41 -05:00
void State : : init ( ) {
2010-12-14 11:23:40 -05:00
// setup js
2010-12-14 14:09:50 -05:00
_scope . reset ( globalScriptEngine - > getPooledScope ( _config . dbname ) . release ( ) ) ;
_scope - > localConnect ( _config . dbname . c_str ( ) ) ;
2011-01-04 00:40:41 -05:00
2010-12-14 14:09:50 -05:00
if ( ! _config . scopeSetup . isEmpty ( ) )
_scope - > init ( & _config . scopeSetup ) ;
_config . mapper - > init ( this ) ;
_config . reducer - > init ( this ) ;
if ( _config . finalizer )
_config . finalizer - > init ( this ) ;
2011-05-10 19:14:32 -07:00
_scope - > setBoolean ( " _doFinal " , _config . finalizer ) ;
2010-12-14 14:09:50 -05:00
2011-05-05 16:30:55 -07:00
// by default start in JS mode, will be faster for small jobs
2011-05-12 11:15:19 -07:00
_jsMode = _config . jsMode ;
// _jsMode = true;
2011-05-05 16:30:55 -07:00
switchMode ( _jsMode ) ;
// global JS map/reduce hashmap
// we use a standard JS object which means keys are only simple types
// we could also add a real hashmap from a library, still we need to add object comparison methods
2011-05-10 21:12:03 -07:00
// _scope->setObject("_mrMap", BSONObj(), false);
2011-05-13 14:59:10 -07:00
ScriptingFunction init = _scope - > createFunction ( " _emitCt = 0; _keyCt = 0; _dupCt = 0; _redCt = 0; if (typeof(_mrMap) === 'undefined') { _mrMap = {}; } " ) ;
2011-05-10 21:12:03 -07:00
_scope - > invoke ( init , 0 , 0 , 0 , true ) ;
2011-05-05 16:30:55 -07:00
// js function to run reduce on all keys
// redfunc = _scope->createFunction("for (var key in hashmap) { print('Key is ' + key); list = hashmap[key]; ret = reduce(key, list); print('Value is ' + ret); };");
2011-05-13 14:59:10 -07:00
_reduceAll = _scope - > createFunction ( " var map = _mrMap; var list, ret; for (var key in map) { list = map[key]; if (list.length != 1) { ret = _reduce(key, list); map[key] = [ret]; ++_redCt; } } _dupCt = 0; " ) ;
_reduceAndEmit = _scope - > createFunction ( " var map = _mrMap; var list, ret; for (var key in map) { list = map[key]; if (list.length == 1) { ret = list[0]; } else { ret = _reduce(key, list); ++_redCt; } emit(key, ret); }; delete _mrMap; " ) ;
_reduceAndFinalize = _scope - > createFunction ( " var map = _mrMap; var list, ret; for (var key in map) { list = map[key]; if (list.length == 1) { if (!_doFinal) {continue;} ret = list[0]; } else { ret = _reduce(key, list); ++_redCt; }; if (_doFinal){ ret = _finalize(ret); } map[key] = ret; } " ) ;
_reduceAndFinalizeAndInsert = _scope - > createFunction ( " var map = _mrMap; var list, ret; for (var key in map) { list = map[key]; if (list.length == 1) { ret = list[0]; } else { ret = _reduce(key, list); ++_redCt; }; if (_doFinal){ ret = _finalize(ret); } _nativeToTemp({_id: key, value: ret}); } " ) ;
2011-01-04 00:40:41 -05:00
2010-11-15 14:39:42 -05:00
}
2011-01-04 00:40:41 -05:00
2011-05-05 16:30:55 -07:00
void State : : switchMode ( bool jsMode ) {
_jsMode = jsMode ;
if ( jsMode ) {
2011-05-10 15:23:45 -07:00
// emit function that stays in JS
2011-05-13 16:24:44 -07:00
_scope - > setFunction ( " emit " , " function(key, value) { if (typeof(key) === 'object') { _bailFromJS(key, value); return; }; ++_emitCt; var map = _mrMap; var list = map[key]; if (!list) { ++_keyCt; list = []; map[key] = list; } else { ++_dupCt; } list.push(value); } " ) ;
_scope - > injectNative ( " _bailFromJS " , _bailFromJS , this ) ;
2011-05-05 16:30:55 -07:00
} else {
// emit now populates C++ map
_scope - > injectNative ( " emit " , fast_emit , this ) ;
}
}
2011-05-13 16:24:44 -07:00
void State : : bailFromJS ( ) {
log ( 1 ) < < " M/R: Switching from JS mode to mixed mode " < < endl ;
// reduce and reemit into c++
switchMode ( false ) ;
_scope - > invoke ( _reduceAndEmit , 0 , 0 , 0 , true ) ;
// need to get the real number emitted so far
_numEmits = _scope - > getNumberInt ( " _emitCt " ) ;
_config . reducer - > numReduces = _scope - > getNumberInt ( " _redCt " ) ;
}
2011-01-24 15:07:54 -08:00
/**
* Applies last reduce and finalize on a list of tuples ( key , val )
* Inserts single result { _id : key , value : val } into temp collection
*/
2011-01-04 00:40:41 -05:00
void State : : finalReduce ( BSONList & values ) {
2011-01-24 15:07:54 -08:00
if ( ! _onDisk | | values . size ( ) = = 0 )
2010-11-15 14:39:42 -05:00
return ;
2011-01-04 00:40:41 -05:00
2011-01-24 15:07:54 -08:00
BSONObj res = _config . reducer - > finalReduce ( values , _config . finalizer . get ( ) ) ;
2010-12-14 14:09:50 -05:00
insert ( _config . tempLong , res ) ;
2010-11-15 14:39:42 -05:00
}
2009-09-24 10:51:27 -04:00
2011-05-13 14:59:10 -07:00
BSONObj _nativeToTemp ( const BSONObj & args , void * data ) {
2011-05-05 16:30:55 -07:00
State * state = ( State * ) data ;
BSONObjIterator it ( args ) ;
state - > insert ( state - > _config . tempLong , it . next ( ) . Obj ( ) ) ;
return BSONObj ( ) ;
}
2011-05-13 14:59:10 -07:00
// BSONObj _nativeToInc( const BSONObj& args, void* data ) {
// State* state = (State*) data;
// BSONObjIterator it(args);
// const BSONObj& obj = it.next().Obj();
// state->_insertToInc(const_cast<BSONObj&>(obj));
// return BSONObj();
// }
2011-01-24 15:07:54 -08:00
/**
* Applies last reduce and finalize .
* After calling this method , the temp collection will be completed .
* If inline , the results will be in the in memory map
*/
2011-01-04 00:40:41 -05:00
void State : : finalReduce ( CurOp * op , ProgressMeterHolder & pm ) {
2011-05-05 16:30:55 -07:00
2011-09-29 19:40:40 -04:00
Client : : GodScope _ ;
2011-05-05 16:30:55 -07:00
if ( _jsMode ) {
2011-05-10 19:14:32 -07:00
// apply the reduce within JS
2011-05-05 16:30:55 -07:00
if ( _onDisk ) {
2011-05-13 14:59:10 -07:00
_scope - > injectNative ( " _nativeToTemp " , _nativeToTemp , this ) ;
2011-05-10 19:14:32 -07:00
_scope - > invoke ( _reduceAndFinalizeAndInsert , 0 , 0 , 0 , true ) ;
return ;
} else {
2011-05-05 16:30:55 -07:00
_scope - > invoke ( _reduceAndFinalize , 0 , 0 , 0 , true ) ;
return ;
}
}
2011-01-04 00:40:41 -05:00
if ( ! _onDisk ) {
2011-01-24 15:07:54 -08:00
// all data has already been reduced, just finalize
2011-01-04 00:40:41 -05:00
if ( _config . finalizer ) {
2010-12-16 16:38:24 -05:00
long size = 0 ;
2011-01-04 00:40:41 -05:00
for ( InMemory : : iterator i = _temp - > begin ( ) ; i ! = _temp - > end ( ) ; + + i ) {
2010-12-16 16:38:24 -05:00
BSONObj key = i - > first ;
BSONList & all = i - > second ;
2011-01-04 00:40:41 -05:00
2010-12-16 16:38:24 -05:00
assert ( all . size ( ) = = 1 ) ;
2011-01-04 00:40:41 -05:00
2010-12-16 16:38:24 -05:00
BSONObj res = _config . finalizer - > finalize ( all [ 0 ] ) ;
all . clear ( ) ;
all . push_back ( res ) ;
size + = res . objsize ( ) ;
}
_size = size ;
}
return ;
}
2011-01-24 15:07:54 -08:00
// use index on "0" to pull sorted data
2010-12-14 14:37:24 -05:00
assert ( _temp - > size ( ) = = 0 ) ;
BSONObj sortKey = BSON ( " 0 " < < 1 ) ;
2011-01-17 21:56:08 -05:00
{
bool foundIndex = false ;
2011-01-24 15:07:54 -08:00
2011-01-17 21:56:08 -05:00
auto_ptr < DBClientCursor > idx = _db . getIndexes ( _config . incLong ) ;
2011-01-24 15:07:54 -08:00
while ( idx . get ( ) & & idx - > more ( ) ) {
2011-01-17 21:56:08 -05:00
BSONObj x = idx - > next ( ) ;
2011-01-24 15:07:54 -08:00
if ( sortKey . woCompare ( x [ " key " ] . embeddedObject ( ) ) = = 0 ) {
2011-01-17 21:56:08 -05:00
foundIndex = true ;
break ;
}
}
assert ( foundIndex ) ;
}
2010-12-14 14:37:24 -05:00
readlock rl ( _config . incLong . c_str ( ) ) ;
Client : : Context ctx ( _config . incLong ) ;
2011-01-04 00:40:41 -05:00
2010-12-14 14:37:24 -05:00
BSONObj prev ;
BSONList all ;
2011-01-04 00:40:41 -05:00
2011-01-05 12:39:12 -05:00
assert ( pm = = op - > setMessage ( " m/r: (3/3) final reduce to collection " , _db . count ( _config . incLong , BSONObj ( ) , QueryOption_SlaveOk ) ) ) ;
2011-01-04 00:40:41 -05:00
2010-12-14 14:37:24 -05:00
shared_ptr < Cursor > temp = bestGuessCursor ( _config . incLong . c_str ( ) , BSONObj ( ) , sortKey ) ;
auto_ptr < ClientCursor > cursor ( new ClientCursor ( QueryOption_NoCursorTimeout , temp , _config . incLong . c_str ( ) ) ) ;
2011-01-04 00:40:41 -05:00
2011-01-24 15:07:54 -08:00
// iterate over all sorted objects
2011-01-04 00:40:41 -05:00
while ( cursor - > ok ( ) ) {
2010-12-14 14:37:24 -05:00
BSONObj o = cursor - > current ( ) . getOwned ( ) ;
cursor - > advance ( ) ;
2011-01-04 00:40:41 -05:00
2010-12-14 14:37:24 -05:00
pm . hit ( ) ;
2011-01-04 00:40:41 -05:00
if ( o . woSortOrder ( prev , sortKey ) = = 0 ) {
2011-01-24 15:07:54 -08:00
// object is same as previous, add to array
2010-12-14 14:37:24 -05:00
all . push_back ( o ) ;
2011-01-04 00:40:41 -05:00
if ( pm - > hits ( ) % 1000 = = 0 ) {
if ( ! cursor - > yield ( ) ) {
2010-12-14 14:37:24 -05:00
cursor . release ( ) ;
break ;
2011-01-04 00:40:41 -05:00
}
2010-12-14 14:37:24 -05:00
killCurrentOp . checkForInterrupt ( ) ;
}
continue ;
}
2011-01-04 00:40:41 -05:00
2010-12-14 14:37:24 -05:00
ClientCursor : : YieldLock yield ( cursor . get ( ) ) ;
2011-07-07 17:26:17 -04:00
try {
// reduce a finalize array
finalReduce ( all ) ;
}
catch ( . . . ) {
yield . relock ( ) ;
cursor . release ( ) ;
throw ;
}
2011-01-04 00:40:41 -05:00
2010-12-14 14:37:24 -05:00
all . clear ( ) ;
prev = o ;
all . push_back ( o ) ;
2011-01-04 00:40:41 -05:00
if ( ! yield . stillOk ( ) ) {
2010-12-14 14:37:24 -05:00
cursor . release ( ) ;
break ;
}
2011-01-04 00:40:41 -05:00
2010-12-14 14:37:24 -05:00
killCurrentOp . checkForInterrupt ( ) ;
}
2011-02-12 17:39:44 -05:00
// we need to release here since we temp release below
cursor . release ( ) ;
2011-01-04 00:40:41 -05:00
2010-12-14 14:37:24 -05:00
{
dbtempreleasecond tl ;
if ( ! tl . unlocked ( ) )
log ( LL_WARNING ) < < " map/reduce can't temp release " < < endl ;
2011-01-24 15:07:54 -08:00
// reduce and finalize last array
2010-12-14 14:37:24 -05:00
finalReduce ( all ) ;
}
2011-01-04 00:40:41 -05:00
2010-12-14 14:37:24 -05:00
pm . finished ( ) ;
}
2011-01-04 00:40:41 -05:00
2011-01-24 15:07:54 -08:00
/**
* Attempts to reduce objects in the memory map .
* A new memory map will be created to hold the results .
* If applicable , objects with unique key may be dumped to inc collection .
* Input and output objects are both { " 0 " : key , " 1 " : val }
*/
2011-01-04 00:40:41 -05:00
void State : : reduceInMemory ( ) {
2010-12-16 16:38:24 -05:00
2011-05-05 16:30:55 -07:00
if ( _jsMode ) {
// in js mode the reduce is applied when writing to collection
return ;
}
2011-09-29 19:40:40 -04:00
Client : : GodScope _ ;
2011-02-10 00:56:53 -05:00
auto_ptr < InMemory > n ( new InMemory ( ) ) ; // for new data
2010-12-15 09:51:01 -05:00
long nSize = 0 ;
2011-05-13 15:43:50 -07:00
_dupCount = 0 ;
2011-01-04 00:40:41 -05:00
for ( InMemory : : iterator i = _temp - > begin ( ) ; i ! = _temp - > end ( ) ; + + i ) {
2010-11-15 14:39:42 -05:00
BSONObj key = i - > first ;
BSONList & all = i - > second ;
2011-01-04 00:40:41 -05:00
if ( all . size ( ) = = 1 ) {
2011-01-24 15:07:54 -08:00
// only 1 value for this key
2011-01-04 00:40:41 -05:00
if ( _onDisk ) {
2011-01-24 15:07:54 -08:00
// this key has low cardinality, so just write to collection
2010-12-16 16:38:24 -05:00
writelock l ( _config . incLong ) ;
Client : : Context ctx ( _config . incLong . c_str ( ) ) ;
_insertToInc ( * ( all . begin ( ) ) ) ;
}
else {
2011-01-24 15:07:54 -08:00
// add to new map
2011-05-13 15:43:50 -07:00
_add ( n . get ( ) , all [ 0 ] , nSize ) ;
2010-12-16 16:38:24 -05:00
}
2010-11-15 14:39:42 -05:00
}
2011-01-04 00:40:41 -05:00
else if ( all . size ( ) > 1 ) {
2011-01-24 15:07:54 -08:00
// several values, reduce and add to map
2010-12-14 14:09:50 -05:00
BSONObj res = _config . reducer - > reduce ( all ) ;
2011-05-13 15:43:50 -07:00
_add ( n . get ( ) , res , nSize ) ;
2009-09-24 10:51:27 -04:00
}
}
2011-01-04 00:40:41 -05:00
2011-01-24 15:07:54 -08:00
// swap maps
2011-02-10 00:56:53 -05:00
_temp . reset ( n . release ( ) ) ;
2010-12-15 09:51:01 -05:00
_size = nSize ;
2010-11-15 14:39:42 -05:00
}
2011-01-04 00:40:41 -05:00
2011-01-24 15:07:54 -08:00
/**
* Dumps the entire in memory map to the inc collection .
*/
2011-01-04 00:40:41 -05:00
void State : : dumpToInc ( ) {
2010-12-16 16:38:24 -05:00
if ( ! _onDisk )
return ;
2011-09-29 19:40:40 -04:00
Client : : GodScope _ ;
2010-12-14 14:09:50 -05:00
writelock l ( _config . incLong ) ;
Client : : Context ctx ( _config . incLong ) ;
2011-01-04 00:40:41 -05:00
for ( InMemory : : iterator i = _temp - > begin ( ) ; i ! = _temp - > end ( ) ; i + + ) {
2010-11-15 14:39:42 -05:00
BSONList & all = i - > second ;
if ( all . size ( ) < 1 )
continue ;
2011-01-04 00:40:41 -05:00
2010-11-15 14:39:42 -05:00
for ( BSONList : : iterator j = all . begin ( ) ; j ! = all . end ( ) ; j + + )
2010-12-14 14:17:12 -05:00
_insertToInc ( * j ) ;
2009-09-24 10:51:27 -04:00
}
2010-11-15 14:39:42 -05:00
_temp - > clear ( ) ;
_size = 0 ;
}
2011-01-04 00:40:41 -05:00
2011-01-24 15:07:54 -08:00
/**
* Adds object to in memory map
*/
2011-01-04 00:40:41 -05:00
void State : : emit ( const BSONObj & a ) {
2010-12-14 14:09:50 -05:00
_numEmits + + ;
2011-05-13 15:43:50 -07:00
_add ( _temp . get ( ) , a , _size ) ;
2010-12-14 14:09:50 -05:00
}
2011-05-13 15:43:50 -07:00
void State : : _add ( InMemory * im , const BSONObj & a , long & size ) {
2010-12-15 09:51:01 -05:00
BSONList & all = ( * im ) [ a ] ;
2010-11-15 14:39:42 -05:00
all . push_back ( a ) ;
2010-12-15 09:51:01 -05:00
size + = a . objsize ( ) + 16 ;
2011-01-25 14:41:16 -08:00
if ( all . size ( ) > 1 )
2011-05-13 15:43:50 -07:00
+ + _dupCount ;
2010-11-15 14:39:42 -05:00
}
2009-11-04 17:11:17 -05:00
2011-01-24 15:07:54 -08:00
/**
* this method checks the size of in memory map and potentially flushes to disk
*/
2011-01-04 00:40:41 -05:00
void State : : checkSize ( ) {
2011-09-29 19:40:40 -04:00
Client : : GodScope _ ;
2011-05-13 14:59:10 -07:00
if ( _jsMode ) {
// try to reduce if it is beneficial
int dupCt = _scope - > getNumberInt ( " _dupCt " ) ;
int keyCt = _scope - > getNumberInt ( " _keyCt " ) ;
if ( keyCt > _config . jsMaxKeys ) {
// too many keys for JS, switch to mixed
2011-05-13 16:24:44 -07:00
_bailFromJS ( BSONObj ( ) , this ) ;
2011-05-13 14:59:10 -07:00
// then fall through to check map size
2011-05-13 15:43:50 -07:00
} else if ( dupCt > ( keyCt * _config . reduceTriggerRatio ) ) {
2011-05-13 14:59:10 -07:00
// reduce now to lower mem usage
_scope - > invoke ( _reduceAll , 0 , 0 , 0 , true ) ;
return ;
}
}
2011-05-13 15:43:50 -07:00
if ( _jsMode )
2010-11-15 14:39:42 -05:00
return ;
2009-09-24 10:51:27 -04:00
2011-05-13 15:43:50 -07:00
bool dump = _onDisk & & _size > _config . maxInMemSize ;
2011-01-25 14:41:16 -08:00
// attempt to reduce in memory map, if we've seen duplicates
2011-05-13 15:43:50 -07:00
if ( dump | | _dupCount > ( _temp - > size ( ) * _config . reduceTriggerRatio ) ) {
2011-01-25 14:41:16 -08:00
long before = _size ;
reduceInMemory ( ) ;
log ( 1 ) < < " mr: did reduceInMemory " < < before < < " -->> " < < _size < < endl ;
}
2009-09-24 10:51:27 -04:00
2011-05-13 15:43:50 -07:00
// reevaluate size and potentially dump
if ( dump & & _size > _config . maxInMemSize ) {
dumpToInc ( ) ;
log ( 1 ) < < " mr: dumping to db " < < endl ;
}
2010-11-15 14:39:42 -05:00
}
2009-11-09 15:21:37 -05:00
2011-01-24 15:07:54 -08:00
/**
* emit that will be called by js function
*/
2011-05-05 16:30:55 -07:00
BSONObj fast_emit ( const BSONObj & args , void * data ) {
2010-03-19 09:55:24 -04:00
uassert ( 10077 , " fast_emit takes 2 args " , args . nFields ( ) = = 2 ) ;
2010-12-14 14:09:50 -05:00
uassert ( 13069 , " an emit can't be more than half max bson size " , args . objsize ( ) < ( BSONObjMaxUserSize / 2 ) ) ;
2011-03-30 03:23:37 -04:00
2011-05-05 16:30:55 -07:00
State * state = ( State * ) data ;
2011-03-30 03:23:37 -04:00
if ( args . firstElement ( ) . type ( ) = = Undefined ) {
BSONObjBuilder b ( args . objsize ( ) ) ;
b . appendNull ( " " ) ;
BSONObjIterator i ( args ) ;
i . next ( ) ;
b . append ( i . next ( ) ) ;
2011-05-05 16:30:55 -07:00
state - > emit ( b . obj ( ) ) ;
2011-03-30 03:23:37 -04:00
}
else {
2011-05-05 16:30:55 -07:00
state - > emit ( args ) ;
2011-03-30 03:23:37 -04:00
}
2009-10-07 12:57:45 -04:00
return BSONObj ( ) ;
}
2009-11-03 11:40:00 -05:00
2011-05-13 16:24:44 -07:00
/**
* function is called when we realize we cant use js mode for m / r on the 1 st key
*/
BSONObj _bailFromJS ( const BSONObj & args , void * data ) {
State * state = ( State * ) data ;
state - > bailFromJS ( ) ;
// emit this particular key if there is one
if ( ! args . isEmpty ( ) ) {
fast_emit ( args , data ) ;
}
return BSONObj ( ) ;
}
2011-01-24 15:07:54 -08:00
/**
* This class represents a map / reduce command executed on a single server
*/
2009-09-24 10:51:27 -04:00
class MapReduceCommand : public Command {
public :
2011-01-04 00:40:41 -05:00
MapReduceCommand ( ) : Command ( " mapReduce " , false , " mapreduce " ) { }
2011-01-06 12:47:48 -08:00
virtual bool slaveOk ( ) const { return ! replSet ; }
2011-01-04 23:16:44 -08:00
virtual bool slaveOverrideOk ( ) { return true ; }
2011-01-04 00:40:41 -05:00
2009-09-24 10:51:27 -04:00
virtual void help ( stringstream & help ) const {
2010-04-23 16:41:56 -04:00
help < < " Run a map/reduce operation on the server. \n " ;
help < < " Note this is used for aggregation, not querying, in MongoDB. \n " ;
help < < " http://www.mongodb.org/display/DOCS/MapReduce " ;
2009-09-24 10:51:27 -04:00
}
2011-01-04 00:40:41 -05:00
virtual LockType locktype ( ) const { return NONE ; }
2011-07-18 15:23:37 -04:00
bool run ( const string & dbname , BSONObj & cmd , int , string & errmsg , BSONObjBuilder & result , bool fromRepl ) {
2009-09-24 10:51:27 -04:00
Timer t ;
2010-03-15 11:26:56 -04:00
Client & client = cc ( ) ;
CurOp * op = client . curop ( ) ;
2010-12-14 11:23:40 -05:00
Config config ( dbname , cmd ) ;
2009-09-21 23:47:09 -04:00
2010-12-14 11:23:40 -05:00
log ( 1 ) < < " mr ns: " < < config . ns < < endl ;
2011-01-04 00:40:41 -05:00
2009-11-04 17:11:17 -05:00
bool shouldHaveData = false ;
2011-01-04 00:40:41 -05:00
2009-10-14 16:19:13 -04:00
long long num = 0 ;
2009-10-15 11:26:15 -04:00
long long inReduce = 0 ;
2011-01-04 00:40:41 -05:00
2009-10-15 11:26:15 -04:00
BSONObjBuilder countsBuilder ;
BSONObjBuilder timingBuilder ;
2010-12-14 11:23:40 -05:00
State state ( config ) ;
2011-01-04 00:40:41 -05:00
if ( ! state . sourceExists ( ) ) {
2010-12-14 14:37:24 -05:00
errmsg = " ns doesn't exist " ;
return false ;
}
2011-01-04 00:40:41 -05:00
2011-01-06 12:47:48 -08:00
if ( replSet & & state . isOnDisk ( ) ) {
2011-01-04 23:16:44 -08:00
// this means that it will be doing a write operation, make sure we are on Master
// ideally this check should be in slaveOk(), but at that point config is not known
if ( ! isMaster ( dbname . c_str ( ) ) ) {
errmsg = " not master " ;
return false ;
}
}
2009-09-24 10:51:27 -04:00
try {
2010-11-15 15:10:31 -05:00
state . init ( ) ;
2011-06-27 13:29:30 -07:00
state . prepTempCollection ( ) ;
2011-09-21 18:08:41 -07:00
ProgressMeterHolder pm ( op - > setMessage ( " m/r: (1/3) emit phase " , state.incomingDocuments() ) ) ;
2011-01-04 00:40:41 -05:00
2010-12-14 11:23:40 -05:00
wassert ( config . limit < 0x4000000 ) ; // see case on next line to 32 bit unsigned
2009-10-15 15:44:50 -04:00
long long mapTime = 0 ;
2010-03-30 11:01:02 -04:00
{
2010-12-14 11:23:40 -05:00
readlock lock ( config . ns ) ;
Client : : Context ctx ( config . ns ) ;
2011-01-24 15:07:54 -08:00
2011-01-24 11:39:12 -05:00
ShardChunkManagerPtr chunkManager ;
2011-01-23 18:42:14 -05:00
if ( shardingState . needShardChunkManager ( config . ns ) ) {
2011-01-24 11:39:12 -05:00
chunkManager = shardingState . getShardChunkManager ( config . ns ) ;
2011-01-23 18:42:14 -05:00
}
2011-01-24 15:07:54 -08:00
// obtain cursor on data to apply mr to, sorted
2011-05-17 22:53:48 -07:00
shared_ptr < Cursor > temp = NamespaceDetailsTransient : : getCursor ( config . ns . c_str ( ) , config . filter , config . sort ) ;
2011-08-03 17:10:51 -04:00
uassert ( 15876 , str : : stream ( ) < < " could not create cursor over " < < config . ns < < " for query : " < < config . filter < < " sort : " < < config . sort , temp . get ( ) ) ;
2010-12-14 11:23:40 -05:00
auto_ptr < ClientCursor > cursor ( new ClientCursor ( QueryOption_NoCursorTimeout , temp , config . ns . c_str ( ) ) ) ;
2011-08-03 17:10:51 -04:00
uassert ( 15877 , str : : stream ( ) < < " could not create client cursor over " < < config . ns < < " for query : " < < config . filter < < " sort : " < < config . sort , cursor . get ( ) ) ;
2011-01-24 15:07:54 -08:00
2010-03-30 11:01:02 -04:00
Timer mt ;
2011-01-24 15:07:54 -08:00
// go through each doc
2011-01-04 00:40:41 -05:00
while ( cursor - > ok ( ) ) {
2011-05-17 11:37:05 -07:00
if ( ! cursor - > currentMatches ( ) ) {
2010-11-09 18:36:25 -05:00
cursor - > advance ( ) ;
continue ;
}
2011-01-04 00:40:41 -05:00
2011-05-17 11:37:05 -07:00
// make sure we dont process duplicates in case data gets moved around during map
// TODO This won't actually help when data gets moved, it's to handle multikeys.
if ( cursor - > currentIsDup ( ) ) {
2010-03-30 11:01:02 -04:00
cursor - > advance ( ) ;
continue ;
}
2011-05-17 11:37:05 -07:00
2011-01-04 00:40:41 -05:00
BSONObj o = cursor - > current ( ) ;
2010-03-30 11:01:02 -04:00
cursor - > advance ( ) ;
2011-01-04 00:40:41 -05:00
2011-01-22 00:27:28 -05:00
// check to see if this is a new object we don't own yet
// because of a chunk migration
2011-01-24 11:39:12 -05:00
if ( chunkManager & & ! chunkManager - > belongsToMe ( o ) )
2011-01-22 00:27:28 -05:00
continue ;
2011-01-24 15:07:54 -08:00
// do map
2010-12-14 11:23:40 -05:00
if ( config . verbose ) mt . reset ( ) ;
config . mapper - > map ( o ) ;
if ( config . verbose ) mapTime + = mt . micros ( ) ;
2011-01-04 00:40:41 -05:00
2010-03-30 11:01:02 -04:00
num + + ;
2011-05-13 15:43:50 -07:00
if ( num % 1000 = = 0 ) {
2011-01-24 15:07:54 -08:00
// try to yield lock regularly
2010-05-19 17:52:27 -04:00
ClientCursor : : YieldLock yield ( cursor . get ( ) ) ;
2010-03-30 11:01:02 -04:00
Timer t ;
2011-01-24 15:07:54 -08:00
// check if map needs to be dumped to disk
2010-12-07 00:02:42 -05:00
state . checkSize ( ) ;
2010-03-30 11:01:02 -04:00
inReduce + = t . micros ( ) ;
2011-01-04 00:40:41 -05:00
if ( ! yield . stillOk ( ) ) {
2010-03-30 11:01:02 -04:00
cursor . release ( ) ;
break ;
}
killCurrentOp . checkForInterrupt ( ) ;
}
pm . hit ( ) ;
2011-01-04 00:40:41 -05:00
2010-12-14 11:23:40 -05:00
if ( config . limit & & num > = config . limit )
2010-03-30 11:01:02 -04:00
break ;
2009-09-24 10:51:27 -04:00
}
2009-09-21 23:47:09 -04:00
}
2010-03-15 11:26:56 -04:00
pm . finished ( ) ;
2011-01-04 00:40:41 -05:00
2010-03-30 11:01:02 -04:00
killCurrentOp . checkForInterrupt ( ) ;
2011-01-24 15:07:54 -08:00
// update counters
2010-03-16 00:09:52 -04:00
countsBuilder . appendNumber ( " input " , num ) ;
2010-12-14 14:09:50 -05:00
countsBuilder . appendNumber ( " emit " , state . numEmits ( ) ) ;
if ( state . numEmits ( ) )
2009-11-04 17:11:17 -05:00
shouldHaveData = true ;
2011-01-04 00:40:41 -05:00
2009-10-15 11:26:15 -04:00
timingBuilder . append ( " mapTime " , mapTime / 1000 ) ;
timingBuilder . append ( " emitLoop " , t . millis ( ) ) ;
2011-01-04 00:40:41 -05:00
2010-03-15 11:26:56 -04:00
op - > setMessage ( " m/r: (2/3) final reduce in memory " ) ;
2011-05-11 12:13:56 -07:00
Timer t ;
2011-01-24 15:07:54 -08:00
// do reduce in memory
// this will be the last reduce needed for inline mode
2010-12-07 00:02:42 -05:00
state . reduceInMemory ( ) ;
2011-01-24 15:07:54 -08:00
// if not inline: dump the in memory map to inc collection, all data is on disk
2010-12-14 14:17:12 -05:00
state . dumpToInc ( ) ;
2011-01-24 15:07:54 -08:00
// final reduce
2010-12-14 14:37:24 -05:00
state . finalReduce ( op , pm ) ;
2011-05-11 12:13:56 -07:00
inReduce + = t . micros ( ) ;
2011-05-13 14:59:10 -07:00
countsBuilder . appendNumber ( " reduce " , state . numReduces ( ) ) ;
2011-05-11 12:13:56 -07:00
timingBuilder . append ( " reduceTime " , inReduce / 1000 ) ;
timingBuilder . append ( " mode " , state . jsMode ( ) ? " js " : " mixed " ) ;
2011-09-21 18:08:41 -07:00
long long finalCount = state . postProcessCollection ( op , pm ) ;
state . appendResults ( result ) ;
timingBuilder . append ( " total " , t . millis ( ) ) ;
if ( ! config . outDB . empty ( ) ) {
BSONObjBuilder loc ;
if ( ! config . outDB . empty ( ) )
loc . append ( " db " , config . outDB ) ;
if ( ! config . finalShort . empty ( ) )
loc . append ( " collection " , config . finalShort ) ;
result . append ( " result " , loc . obj ( ) ) ;
}
else {
if ( ! config . finalShort . empty ( ) )
result . append ( " result " , config . finalShort ) ;
}
result . append ( " timeMillis " , t . millis ( ) ) ;
countsBuilder . appendNumber ( " output " , finalCount ) ;
if ( config . verbose ) result . append ( " timing " , timingBuilder . obj ( ) ) ;
result . append ( " counts " , countsBuilder . obj ( ) ) ;
if ( finalCount = = 0 & & shouldHaveData ) {
result . append ( " cmd " , cmd ) ;
errmsg = " there were emits but no data! " ;
return false ;
}
2009-09-24 10:51:27 -04:00
}
2011-08-05 11:28:28 -04:00
// TODO: The error handling code for queries is v. fragile,
// *requires* rethrow AssertionExceptions - should probably fix.
catch ( AssertionException & e ) {
2011-08-03 17:10:51 -04:00
log ( ) < < " mr failed, removing collection " < < causedBy ( e ) < < endl ;
throw e ;
}
catch ( std : : exception & e ) {
log ( ) < < " mr failed, removing collection " < < causedBy ( e ) < < endl ;
throw e ;
}
2011-01-04 00:40:41 -05:00
catch ( . . . ) {
2011-08-03 17:10:51 -04:00
log ( ) < < " mr failed for unknown reason, removing collection " < < endl ;
2009-09-24 10:51:27 -04:00
throw ;
2009-09-21 15:55:24 -04:00
}
2011-01-04 00:40:41 -05:00
2009-10-01 17:15:44 -04:00
return true ;
2009-09-24 10:51:27 -04:00
}
} mapReduceCommand ;
2011-01-04 00:40:41 -05:00
2011-01-24 15:07:54 -08:00
/**
* This class represents a map / reduce command executed on the output server of a sharded env
*/
2009-11-03 10:35:48 -05:00
class MapReduceFinishCommand : public Command {
public :
2011-01-04 00:40:41 -05:00
MapReduceFinishCommand ( ) : Command ( " mapreduce.shardedfinish " ) { }
2011-01-06 12:47:48 -08:00
virtual bool slaveOk ( ) const { return ! replSet ; }
2011-01-04 23:16:44 -08:00
virtual bool slaveOverrideOk ( ) { return true ; }
2011-01-04 00:40:41 -05:00
virtual LockType locktype ( ) const { return NONE ; }
2011-07-18 15:23:37 -04:00
bool run ( const string & dbname , BSONObj & cmdObj , int , string & errmsg , BSONObjBuilder & result , bool ) {
2009-11-03 11:40:00 -05:00
string shardedOutputCollection = cmdObj [ " shardedOutputCollection " ] . valuestrsafe ( ) ;
2011-06-26 18:59:37 -07:00
string postProcessCollection = cmdObj [ " postProcessCollection " ] . valuestrsafe ( ) ;
2011-06-27 13:29:30 -07:00
bool postProcessOnly = ! ( postProcessCollection . empty ( ) ) ;
2011-09-21 18:08:41 -07:00
Client & client = cc ( ) ;
CurOp * op = client . curop ( ) ;
2009-11-03 11:40:00 -05:00
2010-12-17 00:22:20 -05:00
Config config ( dbname , cmdObj . firstElement ( ) . embeddedObjectUserCheck ( ) ) ;
2011-06-27 13:29:30 -07:00
State state ( config ) ;
state . init ( ) ;
2011-06-26 18:59:37 -07:00
if ( postProcessOnly ) {
// the temp collection has been decided by mongos
config . tempLong = dbname + " . " + postProcessCollection ;
}
// no need for incremental collection because records are already sorted
2010-12-14 14:09:50 -05:00
config . incLong = config . tempLong ;
2009-11-03 10:35:48 -05:00
BSONObj shards = cmdObj [ " shards " ] . embeddedObjectUserCheck ( ) ;
2011-06-13 14:25:44 -07:00
BSONObj shardCounts = cmdObj [ " shardCounts " ] . embeddedObjectUserCheck ( ) ;
BSONObj counts = cmdObj [ " counts " ] . embeddedObjectUserCheck ( ) ;
2011-01-04 00:40:41 -05:00
2011-09-21 18:08:41 -07:00
ProgressMeterHolder pm ( op - > setMessage ( " m/r: merge sort and reduce " ) ) ;
2011-06-26 18:59:37 -07:00
if ( postProcessOnly ) {
if ( ! state . _db . exists ( config . tempLong ) ) {
// nothing to do
return 1 ;
2009-11-03 14:44:41 -05:00
}
2011-06-26 18:59:37 -07:00
} else {
set < ServerAndQuery > servers ;
vector < auto_ptr < DBClientCursor > > shardCursors ;
2011-01-04 00:40:41 -05:00
2011-06-26 18:59:37 -07:00
{
// parse per shard results
BSONObjIterator i ( shards ) ;
while ( i . more ( ) ) {
BSONElement e = i . next ( ) ;
string shard = e . fieldName ( ) ;
2010-11-15 15:10:31 -05:00
2011-06-26 18:59:37 -07:00
BSONObj res = e . embeddedObjectUserCheck ( ) ;
2011-01-04 00:40:41 -05:00
2011-06-26 18:59:37 -07:00
uassert ( 10078 , " something bad happened " , shardedOutputCollection = = res [ " result " ] . valuestrsafe ( ) ) ;
servers . insert ( shard ) ;
2011-01-04 00:40:41 -05:00
2011-06-26 18:59:37 -07:00
}
2011-01-04 00:40:41 -05:00
2011-01-19 00:14:07 -08:00
}
2011-01-04 00:40:41 -05:00
2011-06-26 18:59:37 -07:00
state . prepTempCollection ( ) ;
2011-01-04 00:40:41 -05:00
2011-06-26 18:59:37 -07:00
{
// reduce from each stream
BSONObj sortKey = BSON ( " _id " < < 1 ) ;
ParallelSortClusteredCursor cursor ( servers , dbname + " . " + shardedOutputCollection ,
Query ( ) . sort ( sortKey ) ) ;
cursor . init ( ) ;
BSONList values ;
if ( ! config . outDB . empty ( ) ) {
BSONObjBuilder loc ;
if ( ! config . outDB . empty ( ) )
loc . append ( " db " , config . outDB ) ;
if ( ! config . finalShort . empty ( ) )
loc . append ( " collection " , config . finalShort ) ;
result . append ( " result " , loc . obj ( ) ) ;
2010-07-22 16:40:46 -04:00
}
2011-06-26 18:59:37 -07:00
else {
if ( ! config . finalShort . empty ( ) )
result . append ( " result " , config . finalShort ) ;
2010-07-22 16:40:46 -04:00
}
2011-01-04 00:40:41 -05:00
2011-06-26 18:59:37 -07:00
while ( cursor . more ( ) | | ! values . empty ( ) ) {
BSONObj t ;
if ( cursor . more ( ) ) {
t = cursor . next ( ) . getOwned ( ) ;
if ( values . size ( ) = = 0 ) {
values . push_back ( t ) ;
continue ;
}
if ( t . woSortOrder ( * ( values . begin ( ) ) , sortKey ) = = 0 ) {
values . push_back ( t ) ;
continue ;
}
}
BSONObj res = config . reducer - > finalReduce ( values , config . finalizer . get ( ) ) ;
if ( state . isOnDisk ( ) )
state . insertToInc ( res ) ;
else
state . emit ( res ) ;
values . clear ( ) ;
if ( ! t . isEmpty ( ) )
values . push_back ( t ) ;
}
2009-11-03 11:40:00 -05:00
}
2011-01-04 00:40:41 -05:00
2011-06-26 18:59:37 -07:00
for ( set < ServerAndQuery > : : iterator i = servers . begin ( ) ; i ! = servers . end ( ) ; i + + ) {
ScopedDbConnection conn ( i - > _server ) ;
conn - > dropCollection ( dbname + " . " + shardedOutputCollection ) ;
conn . done ( ) ;
2011-06-14 00:10:42 -07:00
}
2011-06-26 18:59:37 -07:00
result . append ( " shardCounts " , shardCounts ) ;
2009-11-03 11:40:00 -05:00
}
2011-01-04 00:40:41 -05:00
2011-09-21 18:08:41 -07:00
long long finalCount = state . postProcessCollection ( op , pm ) ;
2010-12-16 16:38:24 -05:00
state . appendResults ( result ) ;
2011-01-04 00:40:41 -05:00
2011-06-13 16:19:06 -07:00
// fix the global counts
BSONObjBuilder countsB ( 32 ) ;
BSONObjIterator j ( counts ) ;
while ( j . more ( ) ) {
BSONElement elmt = j . next ( ) ;
if ( ! strcmp ( elmt . fieldName ( ) , " reduce " ) )
countsB . append ( " reduce " , elmt . numberLong ( ) + state . numReduces ( ) ) ;
else if ( ! strcmp ( elmt . fieldName ( ) , " output " ) )
countsB . append ( " output " , finalCount ) ;
else
countsB . append ( elmt ) ;
}
result . append ( " counts " , countsB . obj ( ) ) ;
2009-11-03 14:44:41 -05:00
2009-11-03 11:40:00 -05:00
return 1 ;
2009-11-03 10:35:48 -05:00
}
} mapReduceFinishCommand ;
2009-09-21 15:55:24 -04:00
2009-09-24 10:51:27 -04:00
}
2009-09-21 15:55:24 -04:00
}