Skip to content

Instantly share code, notes, and snippets.

@ronalstal
Created December 4, 2012 11:56
Show Gist options
  • Save ronalstal/4203035 to your computer and use it in GitHub Desktop.
Save ronalstal/4203035 to your computer and use it in GitHub Desktop.
mongodb aggregate: Convert Aggregate Result To Collection
/*
convertAggregateResultToCollection (alias: carc)
Author: Ronald Stalder <[email protected]>
*/
var convertAggregateResultToCollection = function (aggResult, options) {
// void function to supress output
var _v = function() {};
// SET DEFAULT OPTIONS UNLESS SPECIFIED
// options = {
//
// "out" : name of the resulting collection, default: "out"
//
// "mode" : "replace" eventually existing "out" or create new one
// (default, will drop the collection first,
// then same as "append")
// "append" to existing collection using insert (see "key")
// "merge" into existing collection using update-upsert on key
//
// "key" : <field> use this field as "_id" for the documents
// in the "out" collection
// If the option "removeKey" is true, the <field> will
// be "renamed" to _id, by default it will be copied
//
// "objId" an existing "_id" field in the result will be renamed
// to the value of the "renameKey" option,
// or deleted if "removeKey" is true,
// in order to force the generation of an objectId
//
// "_id" Default behavior is to take the "_id" from the result.
// If it does not exist, an ObecjtId will automatically be
// generated (this will result in "merge" being an "append")
//
// "removeKey" : true or false, default=false. See above
//
// "renameKey" : (string) existing "_id" in the result will be renamed to this
// (see above). Default: "origId"
//
// "constFields" : array of {name:"name", value:"value"} pairs - default: empty array
// In the resuling document, a "name": value field will be
// inserted for each pair in this array
//
// "debug": 0 - (default) quiet
// 1 - print out some basic information
// 2 - print out some stuff for debugging
// 3 - print also the result of the aggregate
// }
if ( typeof options == 'undefined' ) var options = {};
if ( typeof options.out == 'undefined' ) options.out = "out";
if ( typeof options.mode == 'undefined' ) options.mode = "replace";
if ( typeof options.key == 'undefined' ) options.key = "_id";
if ( typeof options.removeKey == 'undefined' ) options.removeKey = false;
if ( typeof options.renameKey == 'undefined' ) options.renameKey = "origId";
if ( typeof options.constFields == 'undefined' ) options.constFields = [];
if ( typeof options.debug == 'undefined' ) options.debug = 0;
var debug = options.debug;
if ( debug ) print ( "\n*** convertAggregateResultToCollection (carc) -- start\n" );
if ( debug > 2 ) printjson( aggResult );
if ( debug > 1 ) printjson( options );
// check for valid aggregate result in first parameter
if ( typeof aggResult.result !== "object" ) {
print( "ERROR: first parameter must be a valid output of aggregate() !\nexitting function" );
return false;
}
try { _v(aggResult.result[0]) }
catch(err) {
print( "ERROR: output of aggregate() is empty\nexitting function" );
return false;
}
// set the output collection
var out = db.getCollection( options.out );
if ( debug ) print( 'output collection: "' + out + '" output mode is: "'+options.mode+'"' );
// drop it if in "replace" mode and switch to "append"
if ( options.mode === "replace" ) {
out.drop();
options.mode = "append";
if ( debug ) print( 'output collection dropped; switched to "append" mode' );
}
var r = aggResult.result;
// DETERMINE _ID FOR OUT COLLECTION
// asking for _id but there isn't any: switch to "objId"
var txt = 'the "_id" of the resulting documents will be ';
if ( options.key === "_id" && typeof r[0]._id === "undefined" ) {
if ( debug > 1 ) print( 'no _id in result, using "objectId"' );
options.key = "objId";
}
// as a generated objectId
if ( options.key === "objId" ) {
if ( debug ) print( txt+'a generated objectId' );
// from a field in the result
} else {
eval( "var _idField = r[0]."+options.key );
var _idType = typeof _idField;
// check that given key field exists
if ( options.key !== "_id" && _idType === "undefined" ) {
print( "ERROR: given key field '"+options.key
+"' does not exist\nexitting function" );
return false;
}
if ( debug ) {
if ( _idType === "object" ) {
print( txt+"the compound:");
printjson( _idField );
} else {
print( txt+"the key: "+_idField );
}
}
}
// LOOP THE RESULTS AND CREATE THE DOCUMENTS
if ( debug ) print( 'processing ' + r.length + ' result records' );
for ( var i in aggResult.result ) {
doc = aggResult.result[i];
// set _id
if ( !(options.key === "_id" || options.key === "objId") ) {
// copy the key <field> to _id
eval( "_idField = doc."+options.key );
doc._id = _idField;
// and eventually remove it
if ( options.removeKey ) delete _idField;
// if we want objectId and there is an existing _id
} else if ( options.key === "objId"
&& typeof doc._id !== 'undefined' ) {
// rename the _id, unless the removeKey option is set to true
if ( !options.removeKey ) eval( "doc."+options.renameKey+" = doc._id" );
// delete the _id, so an objectId will be generated
delete doc._id;
}
// add constant Fields
for ( var j in options.constFields ) {
eval( "doc."+options.constFields[j].name+"='"+options.constFields[j].value+"'" );
}
// insert or update-upsert
if ( options.mode === "append" ) {
out.insert( doc );
} else {
// prepare update command for the "merge"
var updCmd = 'out.update({"_id":doc._id},{$set:{';
// update set all field from the result
for ( var key in doc ) {
if ( key == "_id" ) continue;
updCmd += '"'+key+'": "'+doc[key]+'",'
}
updCmd += '}},{"upsert":true})';
// execute it
eval( updCmd );
}
if ( debug > 1 ) printjson( doc );
}
if ( debug ) print ( "\n*** convertAggregateResultToCollection (carc) -- end\n" );
}
// set the alias
var carc = convertAggregateResultToCollection;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment