|
|
@@ -18,7 +18,7 @@ var GroupDocumentSource = module.exports = (function(){
|
|
|
* @constructor
|
|
|
* @param [ctx] {ExpressionContext}
|
|
|
**/
|
|
|
- var klass = module.exports = GroupDocumentSource = function GroupDocumentSource(expCtx){
|
|
|
+ var klass = module.exports = GroupDocumentSource = function GroupDocumentSource(expCtx) {
|
|
|
if(arguments.length > 1) throw new Error("up to one arg expected");
|
|
|
base.call(this, expCtx);
|
|
|
|
|
|
@@ -47,7 +47,7 @@ var GroupDocumentSource = module.exports = (function(){
|
|
|
};
|
|
|
|
|
|
klass.groupName = "$group";
|
|
|
- proto.getSourceName = function getSourceName(){
|
|
|
+ proto.getSourceName = function getSourceName() {
|
|
|
return klass.groupName;
|
|
|
};
|
|
|
|
|
|
@@ -80,39 +80,36 @@ var GroupDocumentSource = module.exports = (function(){
|
|
|
};
|
|
|
|
|
|
klass.createFromJson = function createFromJson(groupObj, ctx) {
|
|
|
- if(!(groupObj instanceof Object && groupObj.constructor.name === "Object")) throw new Error("a group's fields must be specified in an object");
|
|
|
+ if (!(groupObj instanceof Object && groupObj.constructor === Object)) throw new Error("a group's fields must be specified in an object");
|
|
|
|
|
|
var idSet = false,
|
|
|
group = new GroupDocumentSource(ctx);
|
|
|
|
|
|
- for(var groupFieldName in groupObj){
|
|
|
- if(groupObj.hasOwnProperty(groupFieldName)){
|
|
|
+ for (var groupFieldName in groupObj) {
|
|
|
+ if (groupObj.hasOwnProperty(groupFieldName)) {
|
|
|
var groupField = groupObj[groupFieldName];
|
|
|
|
|
|
- if(groupFieldName === "_id"){
|
|
|
+ if (groupFieldName === "_id") {
|
|
|
|
|
|
- if(idSet) {
|
|
|
- throw new Error("15948 a group's _id may only be specified once");
|
|
|
- }
|
|
|
+ if(idSet) throw new Error("15948 a group's _id may only be specified once");
|
|
|
|
|
|
- if(groupField instanceof Object && groupField.constructor.name === "Object"){
|
|
|
+ if (groupField instanceof Object && groupField.constructor === Object) {
|
|
|
var objCtx = new Expression.ObjectCtx({isDocumentOk:true});
|
|
|
group.idExpression = Expression.parseObject(groupField, objCtx);
|
|
|
idSet = true;
|
|
|
|
|
|
- }else if( typeof groupField === "string"){
|
|
|
- if(groupField[0] !== "$") {
|
|
|
+ } else if (typeof groupField === "string") {
|
|
|
+ if (groupField[0] !== "$") {
|
|
|
group.idExpression = new ConstantExpression(groupField);
|
|
|
- }
|
|
|
- else {
|
|
|
+ } else {
|
|
|
var pathString = Expression.removeFieldPrefix(groupField);
|
|
|
group.idExpression = new FieldPathExpression(pathString);
|
|
|
}
|
|
|
-
|
|
|
idSet = true;
|
|
|
- }else{
|
|
|
+
|
|
|
+ } else {
|
|
|
var typeStr = group._getTypeStr(groupField);
|
|
|
- switch(typeStr){
|
|
|
+ switch (typeStr) {
|
|
|
case "number":
|
|
|
case "string":
|
|
|
case "boolean":
|
|
|
@@ -128,30 +125,26 @@ var GroupDocumentSource = module.exports = (function(){
|
|
|
}
|
|
|
|
|
|
|
|
|
- }else{
|
|
|
- if(groupFieldName.indexOf(".") !== -1)
|
|
|
- throw new Error("16414 the group aggregate field name '" + groupFieldName + "' cannot contain '.'");
|
|
|
- if(groupFieldName[0] === "$")
|
|
|
- throw new Error("15950 the group aggregate field name '" + groupFieldName + "' cannot be an operator name");
|
|
|
- if(group._getTypeStr(groupFieldName) === "Object")
|
|
|
- throw new Error("15951 the group aggregate field '" + groupFieldName + "' must be defined as an expression inside an object");
|
|
|
+ } else {
|
|
|
+ if (groupFieldName.indexOf(".") !== -1) throw new Error("16414 the group aggregate field name '" + groupFieldName + "' cannot contain '.'");
|
|
|
+ if (groupFieldName[0] === "$") throw new Error("15950 the group aggregate field name '" + groupFieldName + "' cannot be an operator name");
|
|
|
+ if (group._getTypeStr(groupFieldName) === "Object") throw new Error("15951 the group aggregate field '" + groupFieldName + "' must be defined as an expression inside an object");
|
|
|
|
|
|
var subFieldCount = 0;
|
|
|
- for(var subFieldName in groupField){
|
|
|
- if(groupField.hasOwnProperty(subFieldName)){
|
|
|
+ for (var subFieldName in groupField) {
|
|
|
+ if (groupField.hasOwnProperty(subFieldName)) {
|
|
|
var subField = groupField[subFieldName],
|
|
|
op = klass.groupOps[subFieldName];
|
|
|
- if(!op)
|
|
|
- throw new Error("15952 unknown group operator '" + subFieldName + "'");
|
|
|
+ if (!op) throw new Error("15952 unknown group operator '" + subFieldName + "'");
|
|
|
|
|
|
var groupExpression,
|
|
|
subFieldTypeStr = group._getTypeStr(subField);
|
|
|
- if(subFieldTypeStr === "Object"){
|
|
|
+ if (subFieldTypeStr === "Object") {
|
|
|
var subFieldObjCtx = new Expression.ObjectCtx({isDocumentOk:true});
|
|
|
groupExpression = Expression.parseObject(subField, subFieldObjCtx);
|
|
|
- }else if(subFieldTypeStr === "Array"){
|
|
|
+ } else if (subFieldTypeStr === "Array") {
|
|
|
throw new Error("15953 aggregating group operators are unary (" + subFieldName + ")");
|
|
|
- }else{
|
|
|
+ } else {
|
|
|
groupExpression = Expression.parseOperand(subField);
|
|
|
}
|
|
|
group.addAccumulator(groupFieldName,op, groupExpression);
|
|
|
@@ -159,35 +152,30 @@ var GroupDocumentSource = module.exports = (function(){
|
|
|
++subFieldCount;
|
|
|
}
|
|
|
}
|
|
|
- if(subFieldCount != 1)
|
|
|
- throw new Error("15954 the computed aggregate '" + groupFieldName + "' must specify exactly one operator");
|
|
|
+ if (subFieldCount != 1) throw new Error("15954 the computed aggregate '" + groupFieldName + "' must specify exactly one operator");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if(!idSet) {
|
|
|
- throw new Error("15955 a group specification must include an _id");
|
|
|
- }
|
|
|
+ if (!idSet) throw new Error("15955 a group specification must include an _id");
|
|
|
|
|
|
return group;
|
|
|
};
|
|
|
|
|
|
- proto._getTypeStr = function _getTypeStr(obj){
|
|
|
- var typeofStr=typeof obj,
|
|
|
- typeStr=((typeofStr == "object" && obj !== null ) ? obj.constructor.name : typeofStr);
|
|
|
-
|
|
|
+ proto._getTypeStr = function _getTypeStr(obj) {
|
|
|
+ var typeofStr = typeof obj,
|
|
|
+ typeStr = (typeofStr == "object" && obj !== null) ? obj.constructor.name : typeofStr;
|
|
|
return typeStr;
|
|
|
};
|
|
|
|
|
|
- proto.advance = function advance(){
|
|
|
+ proto.advance = function advance() {
|
|
|
base.prototype.advance.call(this); // Check for interupts ????
|
|
|
- if(!this.populated)
|
|
|
- this.populate();
|
|
|
+ if(!this.populated) this.populate();
|
|
|
|
|
|
//verify(this.currentGroupsKeysIndex < this.groupsKeys.length);
|
|
|
|
|
|
++this.currentGroupsKeysIndex;
|
|
|
- if(this.currentGroupsKeysIndex === this.groupsKeys.length){
|
|
|
+ if (this.currentGroupsKeysIndex === this.groupsKeys.length) {
|
|
|
this.currentDocument = null;
|
|
|
return false;
|
|
|
}
|
|
|
@@ -196,18 +184,13 @@ var GroupDocumentSource = module.exports = (function(){
|
|
|
return true;
|
|
|
};
|
|
|
|
|
|
- proto.eof = function eof(){
|
|
|
- if(!this.populated)
|
|
|
- this.populate();
|
|
|
-
|
|
|
+ proto.eof = function eof() {
|
|
|
+ if (!this.populated) this.populate();
|
|
|
return this.currentGroupsKeysIndex === this.groupsKeys.length;
|
|
|
-
|
|
|
};
|
|
|
|
|
|
- proto.getCurrent = function getCurrent(){
|
|
|
- if(!this.populated)
|
|
|
- this.populate();
|
|
|
-
|
|
|
+ proto.getCurrent = function getCurrent() {
|
|
|
+ if (!this.populated) this.populate();
|
|
|
return this.currentDocument;
|
|
|
};
|
|
|
|
|
|
@@ -216,38 +199,36 @@ var GroupDocumentSource = module.exports = (function(){
|
|
|
// add _id
|
|
|
this.idExpression.addDependencies(deps);
|
|
|
// add the rest
|
|
|
- this.fieldNames.forEach(function(field, i) {
|
|
|
+ this.fieldNames.forEach(function (field, i) {
|
|
|
self.expressions[i].addDependencies(deps);
|
|
|
});
|
|
|
|
|
|
return DocumentSource.GetDepsReturn.EXHAUSTIVE;
|
|
|
};
|
|
|
|
|
|
- proto.addAccumulator = function addAccumulator(fieldName, accumulatorFactory, expression){
|
|
|
+ proto.addAccumulator = function addAccumulator(fieldName, accumulatorFactory, expression) {
|
|
|
this.fieldNames.push(fieldName);
|
|
|
this.accumulatorFactories.push(accumulatorFactory);
|
|
|
this.expressions.push(expression);
|
|
|
};
|
|
|
|
|
|
- proto.populate = function populate(){
|
|
|
- for(var hasNext = !this.source.eof(); hasNext; hasNext = this.source.advance()){
|
|
|
+ proto.populate = function populate() {
|
|
|
+ for (var hasNext = !this.source.eof(); hasNext; hasNext = this.source.advance()) {
|
|
|
var group,
|
|
|
currentDocument = this.source.getCurrent(),
|
|
|
_id = this.idExpression.evaluate(currentDocument);
|
|
|
|
|
|
- if(undefined === _id) {
|
|
|
- _id = null;
|
|
|
- }
|
|
|
+ if (undefined === _id) _id = null;
|
|
|
|
|
|
- var idHash = JSON.stringify(_id); //! @todo USE A REAL HASH. I didn't have time to take collision into account.
|
|
|
+ var idHash = JSON.stringify(_id); //TODO: USE A REAL HASH. I didn't have time to take collision into account.
|
|
|
|
|
|
- if(idHash in this.groups){
|
|
|
+ if (idHash in this.groups) {
|
|
|
group = this.groups[idHash];
|
|
|
- }else{
|
|
|
+ } else {
|
|
|
this.groups[idHash] = group = [];
|
|
|
this.groupsKeys[this.currentGroupsKeysIndex] = idHash;
|
|
|
++this.currentGroupsKeysIndex;
|
|
|
- for(var ai =0; ai < this.accumulatorFactories.length; ++ai){
|
|
|
+ for (var ai = 0; ai < this.accumulatorFactories.length; ++ai) {
|
|
|
var accumulator = new this.accumulatorFactories[ai]();
|
|
|
accumulator.addOperand(this.expressions[ai]);
|
|
|
group.push(accumulator);
|
|
|
@@ -256,29 +237,31 @@ var GroupDocumentSource = module.exports = (function(){
|
|
|
|
|
|
|
|
|
// tickle all the accumulators for the group we found
|
|
|
- for(var gi=0; gi < group.length; ++gi)
|
|
|
+ for (var gi = 0; gi < group.length; ++gi) {
|
|
|
group[gi].evaluate(currentDocument);
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
this.currentGroupsKeysIndex = 0; // Start the group
|
|
|
- if(this.groupsKeys.length > 0)
|
|
|
+ if (this.groupsKeys.length > 0) {
|
|
|
this.currentDocument = this.makeDocument(this.currentGroupsKeysIndex);
|
|
|
+ }
|
|
|
this.populated = true;
|
|
|
|
|
|
};
|
|
|
|
|
|
- proto.makeDocument = function makeDocument(groupKeyIndex){
|
|
|
+ proto.makeDocument = function makeDocument(groupKeyIndex) {
|
|
|
var groupKey = this.groupsKeys[groupKeyIndex],
|
|
|
group = this.groups[groupKey],
|
|
|
doc = {};
|
|
|
|
|
|
doc[Document.ID_PROPERTY_NAME] = JSON.parse(groupKey);
|
|
|
|
|
|
- for(var i = 0; i < this.fieldNames.length; ++i){
|
|
|
+ for (var i = 0; i < this.fieldNames.length; ++i) {
|
|
|
var fieldName = this.fieldNames[i],
|
|
|
item = group[i];
|
|
|
- if((item !== "null") && (typeof item !== "undefined")){
|
|
|
+ if (item !== "null" && item !== undefined) {
|
|
|
doc[fieldName] = item.getValue();
|
|
|
}
|
|
|
}
|