Browse Source

REFS EAG-MUN2 Refactor for klass static usage for register expression

David Aebersold 11 years ago
parent
commit
1e145d218e
37 changed files with 510 additions and 406 deletions
  1. 1 1
      lib/pipeline/expressions/AddExpression.js
  2. 1 1
      lib/pipeline/expressions/AllElementsTrueExpression.js
  3. 43 37
      lib/pipeline/expressions/AndExpression.js
  4. 2 2
      lib/pipeline/expressions/AnyElementTrueExpression.js
  5. 0 4
      lib/pipeline/expressions/CoerceToBoolExpression.js
  6. 72 38
      lib/pipeline/expressions/CompareExpression.js
  7. 6 2
      lib/pipeline/expressions/ConcatExpression.js
  8. 2 12
      lib/pipeline/expressions/CondExpression.js
  9. 3 3
      lib/pipeline/expressions/ConstantExpression.js
  10. 3 2
      lib/pipeline/expressions/DayOfMonthExpression.js
  11. 1 1
      lib/pipeline/expressions/DayOfWeekExpression.js
  12. 1 1
      lib/pipeline/expressions/DayOfYearExpression.js
  13. 1 1
      lib/pipeline/expressions/DivideExpression.js
  14. 1 1
      lib/pipeline/expressions/HourExpression.js
  15. 1 2
      lib/pipeline/expressions/IfNullExpression.js
  16. 1 1
      lib/pipeline/expressions/MillisecondExpression.js
  17. 1 1
      lib/pipeline/expressions/MinuteExpression.js
  18. 1 1
      lib/pipeline/expressions/MonthExpression.js
  19. 1 1
      lib/pipeline/expressions/MultiplyExpression.js
  20. 1 1
      lib/pipeline/expressions/NotExpression.js
  21. 1 1
      lib/pipeline/expressions/OrExpression.js
  22. 1 1
      lib/pipeline/expressions/SecondExpression.js
  23. 1 1
      lib/pipeline/expressions/SetDifferenceExpression.js
  24. 1 1
      lib/pipeline/expressions/SetEqualsExpression.js
  25. 2 2
      lib/pipeline/expressions/SetIntersectionExpression.js
  26. 1 1
      lib/pipeline/expressions/SetIsSubsetExpression.js
  27. 2 2
      lib/pipeline/expressions/SetUnionExpression.js
  28. 1 2
      lib/pipeline/expressions/SizeExpression.js
  29. 2 3
      lib/pipeline/expressions/StrcasecmpExpression.js
  30. 2 2
      lib/pipeline/expressions/SubstrExpression.js
  31. 2 3
      lib/pipeline/expressions/SubtractExpression.js
  32. 2 3
      lib/pipeline/expressions/ToLowerExpression.js
  33. 2 3
      lib/pipeline/expressions/ToUpperExpression.js
  34. 2 3
      lib/pipeline/expressions/WeekExpression.js
  35. 15 17
      lib/pipeline/expressions/YearExpression.js
  36. 297 238
      test/lib/pipeline/expressions/CompareExpression.js
  37. 33 10
      test/lib/pipeline/expressions/DayOfMonthExpression.js

+ 1 - 1
lib/pipeline/expressions/AddExpression.js

@@ -39,4 +39,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 
 
 /** Register Expression */
-Expression.registerExpression("$add",AddExpression.parse);
+Expression.registerExpression("$add",klass.parse(AddExpression));

+ 1 - 1
lib/pipeline/expressions/AllElementsTrueExpression.js

@@ -9,7 +9,7 @@
  **/
 var AllElementsTrueExpression = module.exports = function AllElementsTrueExpression() {
 		this.fixedArity(1);
-		if (arguments.length !== 0) throw new Error("zero args expected");
+		if (arguments.length !== 1) throw new Error("one arg expected");
 		base.call(this);
 }, klass = AllElementsTrueExpression,
 		NaryExpression = require("./NaryExpression"),

+ 43 - 37
lib/pipeline/expressions/AndExpression.js

@@ -11,20 +11,26 @@
  * @module mungedb-aggregate
  * @constructor
  **/
-var AndExpression = module.exports = function AndExpression(){
-	if (arguments.length !== 0) throw new Error("zero args expected");
-	base.call(this);
-}, klass = AndExpression, base = require("./NaryExpression"), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
+var AndExpression = module.exports = function AndExpression() {
+		if (arguments.length !== 0) throw new Error("zero args expected");
+		base.call(this);
+}, klass = AndExpression,
+		base = require("./NaryExpression"),
+		proto = klass.prototype = Object.create(base.prototype, {
+				constructor: {
+						value: klass
+				}
+		});
 
 // DEPENDENCIES
 var Value = require("../Value"),
-	ConstantExpression = require("./ConstantExpression"),
-	CoerceToBoolExpression = require("./CoerceToBoolExpression"),
-	Expression = require("./Expression");
+		ConstantExpression = require("./ConstantExpression"),
+		CoerceToBoolExpression = require("./CoerceToBoolExpression"),
+		Expression = require("./Expression");
 
 // PROTOTYPE MEMBERS
-proto.getOpName = function getOpName(){
-	return "$and";
+proto.getOpName = function getOpName() {
+		return "$and";
 };
 
 /**
@@ -32,43 +38,43 @@ proto.getOpName = function getOpName(){
  * @method evaluate
  **/
 proto.evaluateInternal = function evaluateInternal(vars) {
-	for (var i = 0, n = this.operands.length; i < n; ++i) {
-		var value = this.operands[i].evaluateInternal(vars);
-		if (!Value.coerceToBool()) return false;
-	}
-	return true;
+		for (var i = 0, n = this.operands.length; i < n; ++i) {
+				var value = this.operands[i].evaluateInternal(vars);
+				if (!Value.coerceToBool()) return false;
+		}
+		return true;
 };
 
 proto.optimize = function optimize() {
-	var expr = base.prototype.optimize.call(this); //optimize the conjunction as much as possible
+		var expr = base.prototype.optimize.call(this); //optimize the conjunction as much as possible
 
-	// if the result isn't a conjunction, we can't do anything
-	if (!(expr instanceof AndExpression)) return expr;
-	var andExpr = expr;
+		// if the result isn't a conjunction, we can't do anything
+		if (!(expr instanceof AndExpression)) return expr;
+		var andExpr = expr;
 
-	// Check the last argument on the result; if it's not constant (as promised by ExpressionNary::optimize(),) then there's nothing we can do.
-	var n = andExpr.operands.length;
-	// ExpressionNary::optimize() generates an ExpressionConstant for {$and:[]}.
-	if (!n) throw new Error("requires operands!");
-	var lastExpr = andExpr.operands[n - 1];
-	if (!(lastExpr instanceof ConstantExpression)) return expr;
+		// Check the last argument on the result; if it's not constant (as promised by ExpressionNary::optimize(),) then there's nothing we can do.
+		var n = andExpr.operands.length;
+		// ExpressionNary::optimize() generates an ExpressionConstant for {$and:[]}.
+		if (!n) throw new Error("requires operands!");
+		var lastExpr = andExpr.operands[n - 1];
+		if (!(lastExpr instanceof ConstantExpression)) return expr;
 
-	// Evaluate and coerce the last argument to a boolean.  If it's false, then we can replace this entire expression.
-	var last = Value.coerceToBool(lastExpr.evaluate());
-	if (!last) return new ConstantExpression(false);
+		// Evaluate and coerce the last argument to a boolean.  If it's false, then we can replace this entire expression.
+		var last = Value.coerceToBool(lastExpr.evaluate());
+		if (!last) return new ConstantExpression(false);
 
-	// If we got here, the final operand was true, so we don't need it anymore.
-	// If there was only one other operand, we don't need the conjunction either.
-	// Note we still need to keep the promise that the result will be a boolean.
-	if (n == 2) return new CoerceToBoolExpression(andExpr.operands[0]);
+		// If we got here, the final operand was true, so we don't need it anymore.
+		// If there was only one other operand, we don't need the conjunction either.
+		// Note we still need to keep the promise that the result will be a boolean.
+		if (n == 2) return new CoerceToBoolExpression(andExpr.operands[0]);
 
-	//Remove the final "true" value, and return the new expression.
-	//CW TODO: Note that because of any implicit conversions, we may need to apply an implicit boolean conversion.
-	andExpr.operands.length = n - 1; //truncate the array
-	return expr;
+		//Remove the final "true" value, and return the new expression.
+		//CW TODO: Note that because of any implicit conversions, we may need to apply an implicit boolean conversion.
+		andExpr.operands.length = n - 1; //truncate the array
+		return expr;
 };
 
 /** Register Expression */
-Expression.registerExpression("$and",AndExpression.parse);
+Expression.registerExpression("$and", klass.parse(AndExpression));
 
-//TODO:	proto.toMatcherBson
+//TODO:	proto.toMatcherBson

+ 2 - 2
lib/pipeline/expressions/AnyElementTrueExpression.js

@@ -9,7 +9,7 @@
  **/
 var AnyElementTrueExpression = module.exports = function AnyElementTrueExpression(){
 	this.fixedArity(1);
-	if (arguments.length !== 0) throw new Error("zero args expected");
+	if (arguments.length !== 1) throw new Error("one args expected");
 	base.call(this);
 }, klass = AnyElementTrueExpression, NaryExpression = require("./NaryExpression"), base = NaryExpression, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 
@@ -40,4 +40,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$anyElementTrue",AnyElementTrueExpression.evaluateInternal);
+Expression.registerExpression("$anyElementTrue",klass.parse(AnyElementTrueExpression));

+ 0 - 4
lib/pipeline/expressions/CoerceToBoolExpression.js

@@ -58,9 +58,5 @@ proto.toJSON = function toJSON() {
 	return {$and:[this.expression.toJSON()]};
 };
 
-
-/** Register Expression */
-Expression.registerExpression("$coerceToBool",CoerceToBoolExpression.parse);
-
 //TODO:	proto.addToBsonObj   --- may be required for $project to work
 //TODO:	proto.addToBsonArray

+ 72 - 38
lib/pipeline/expressions/CompareExpression.js

@@ -1,24 +1,32 @@
 "use strict";
 
 /**
- * Generic comparison expression that gets used for $eq, $ne, $lt, $lte, $gt, $gte, and $cmp. 
+ * Generic comparison expression that gets used for $eq, $ne, $lt, $lte, $gt, $gte, and $cmp.
  * @class CompareExpression
  * @namespace mungedb-aggregate.pipeline.expressions
  * @module mungedb-aggregate
  * @constructor
  **/
 var CompareExpression = module.exports = function CompareExpression(cmpOp) {
-	if (arguments.length !== 1) throw new Error("args expected: cmpOp");
-	this.cmpOp = cmpOp;
-	base.call(this);
-}, klass = CompareExpression, base = require("./NaryExpression"), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
+		this.fixedVarity(2);
+		if (arguments.length !== 2) throw new Error("two args expected: cmpOp");
+		this.cmpOp = cmpOp;
+		base.call(this);
+}, klass = CompareExpression,
+		base = require("./NaryExpression"),
+		proto = klass.prototype = Object.create(base.prototype, {
+				constructor: {
+						value: klass
+				}
+		});
 
 // DEPENDENCIES
-var Value = require("../Value"),
-	Expression = require("./Expression"),
-	ConstantExpression = require("./ConstantExpression"),
-	FieldPathExpression = require("./FieldPathExpression"),
-	FieldRangeExpression = require("./FieldRangeExpression");
+var Value = require("../Value");
+var Expression = require("./Expression");
+var ConstantExpression = require("./ConstantExpression");
+var FieldPathExpression = require("./FieldPathExpression");
+var FieldRangeExpression = require("./FieldRangeExpression");
+var NaryExpression = require("./NaryExpression");
 
 // NESTED CLASSES
 /**
@@ -28,15 +36,20 @@ var Value = require("../Value"),
  * @param reverse		reverse comparison operator
  * @param name			string name
  **/
-var CmpLookup = (function(){	// emulating a struct
-	// CONSTRUCTOR
-	var klass = function CmpLookup(truthValues, reverse, name) {
-		if(arguments.length !== 3) throw new Error("args expected: truthValues, reverse, name");
-		this.truthValues = truthValues;
-		this.reverse = reverse;
-		this.name = name;
-	}, base = Object, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
-	return klass;
+var CmpLookup = (function() { // emulating a struct
+		// CONSTRUCTOR
+		var klass = function CmpLookup(truthValues, reverse, name) {
+				if (arguments.length !== 3) throw new Error("args expected: truthValues, reverse, name");
+				this.truthValues = truthValues;
+				this.reverse = reverse;
+				this.name = name;
+		}, base = Object,
+				proto = klass.prototype = Object.create(base.prototype, {
+						constructor: {
+								value: klass
+						}
+				});
+		return klass;
 })();
 
 // verify we need this below
@@ -45,26 +58,38 @@ var CmpLookup = (function(){	// emulating a struct
  * a table of cmp type lookups to truth values
  * @private
  **/
-var cmpLookupMap = [	//NOTE: converted from this Array to a Dict/Object below using CmpLookup#name as the key
-	//              -1      0      1      reverse             name     (taking advantage of the fact that our 'enums' are strings below)
-	new CmpLookup([false, true, false], CompareExpression.EQ, CompareExpression.EQ),
-	new CmpLookup([true, false, true], CompareExpression.NE, CompareExpression.NE),
-	new CmpLookup([false, false, true], CompareExpression, CompareExpression.GT),
-	new CmpLookup([false, true, true], CompareExpression.LTE, CompareExpression.GTE),
-	new CmpLookup([true, false, false], CompareExpression.GT, CompareExpression.LT),
-	new CmpLookup([true, true, false], CompareExpression.GTE, CompareExpression.LTE),
-	new CmpLookup([false, false, false], CompareExpression.CMP, CompareExpression.CMP)
-].reduce(function(r,o){r[o.name]=o;return r;},{});
+var cmpLookupMap = [ //NOTE: converted from this Array to a Dict/Object below using CmpLookup#name as the key
+		//              -1      0      1      reverse             name     (taking advantage of the fact that our 'enums' are strings below)
+		new CmpLookup([false, true, false], CompareExpression.EQ, CompareExpression.EQ),
+		new CmpLookup([true, false, true], CompareExpression.NE, CompareExpression.NE),
+		new CmpLookup([false, false, true], CompareExpression.LT, CompareExpression.GT),
+		new CmpLookup([false, true, true], CompareExpression.LTE, CompareExpression.GTE),
+		new CmpLookup([true, false, false], CompareExpression.GT, CompareExpression.LT),
+		new CmpLookup([true, true, false], CompareExpression.GTE, CompareExpression.LTE),
+		new CmpLookup([false, false, false], CompareExpression.CMP, CompareExpression.CMP)
+].reduce(function(r, o) {
+		r[o.name] = o;
+		return r;
+}, {});
 
 
+klass.parse = function parse(bsonExpr, vps, op) {
+		var expr = new CompareExpression(op);
+		var args = NaryExpression.parseArguments(bsonExpr, vps);
+		expr.validateArguments(args);
+		expr.vpOperand = args;
+		return expr;
+
+};
+
 // PROTOTYPE MEMBERS
-proto.evaluateInternal = function evaluateInternal(doc) {
-	this.checkArgCount(2);
-	var left = this.operands[0].evaluateInternal(doc),
-		right = this.operands[1].evaluateInternal(doc),
-		cmp = Expression.signum(Value.compare(left, right));
-	if (this.cmpOp == Expression.CmpOp.CMP) return cmp;
-	return cmpLookupMap[this.cmpOp].truthValues[cmp + 1] || false;
+proto.evaluateInternal = function evaluateInternal(vars) {
+		//debugger;
+		var left = this.operands[0].evaluateInternal(vars),
+				right = this.operands[1].evaluateInternal(vars),
+				cmp = Expression.signum(Value.compare(left, right));
+		if (this.cmpOp == Expression.CmpOp.CMP) return cmp;
+		return cmpLookupMap[this.cmpOp].truthValues[cmp + 1] || false;
 };
 
 klass.EQ = "$eq";
@@ -75,6 +100,15 @@ klass.LT = "$lt";
 klass.LTE = "$lte";
 klass.CMP = "$cmp";
 
-proto.getOpName = function getOpName(){
-	return this.cmpOp;
+proto.getOpName = function getOpName() {
+		return this.cmpOp;
 };
+
+/** Register Expression */
+Expression.registerExpression("$eq", klass.parse(CompareExpression));
+Expression.registerExpression("$ne", klass.parse(CompareExpression));
+Expression.registerExpression("$gt", klass.parse(CompareExpression));
+Expression.registerExpression("$gte", klass.parse(CompareExpression));
+Expression.registerExpression("$lt", klass.parse(CompareExpression));
+Expression.registerExpression("$lte", klass.parse(CompareExpression));
+Expression.registerExpression("$cmp", klass.parse(CompareExpression));

+ 6 - 2
lib/pipeline/expressions/ConcatExpression.js

@@ -14,6 +14,7 @@ var ConcatExpression = module.exports = function ConcatExpression(){
 
 // DEPENDENCIES
 var Value = require("../Value");
+var Expression = require("./Expression");
 
 // PROTOTYPE MEMBERS
 proto.getOpName = function getOpName(){
@@ -28,13 +29,16 @@ proto.getFactory = function getFactory(){
  * Concats a string of values together.
  * @method evaluate
  **/
-proto.evaluate = function evaluate(doc) {
+proto.evaluateInternal = function evaluateInternal(vars) {
 	var result = "";
 	for (var i = 0, n = this.operands.length; i < n; ++i) {
-		var val = this.operands[i].evaluate(doc);
+		var val = this.operands[i].evaluateInternal(vars);
 		if (val === null) return null; // if any operand is null, return null for all
 		if (typeof(val) != "string") throw new Error("$concat only supports strings, not " + typeof(val) + "; code 16702");
 		result = result + Value.coerceToString(val);
 	}
 	return result;
 };
+
+/** Register Expression */
+Expression.registerExpression("$concat", klass.parse(ConcatExpression));

+ 2 - 12
lib/pipeline/expressions/CondExpression.js

@@ -11,9 +11,8 @@ var CondExpression = module.exports = function CondExpression(vars) {
 		this.fixedArity(3);
 		this.pCond = this.evaluateInternal(vars);
 		this.idx = this.pCond.coerceToBool() ? 1 : 2;
-		//return vpOperand[this.idx].evaluateInternal(vars);
 
-		if (arguments.length !== 3) throw new Error("zero args expected");
+		if (arguments.length !== 3) throw new Error("three args expected");
 		base.call(this);
 }, klass = CondExpression,
 		base = require("./NaryExpression"),
@@ -75,14 +74,5 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 		return this.operands[this.idx].evaluateInternal(vars);
 };
 
-proto.evaluateInternal = function evaluateInternal(vars) {
-		this.checkArgCount(1);
-		var date = this.operands[0].evaluateInternal(vars);
-
-
-		return date.getUTCDate();
-};
-
-
 /** Register Expression */
-Expression.registerExpression("$cond", CondExpression.parse);
+Expression.registerExpression("$cond", klass.parse(CondExpression));

+ 3 - 3
lib/pipeline/expressions/ConstantExpression.js

@@ -36,7 +36,7 @@ proto.addDependencies = function addDependencies(deps, path) {
 	// nothing to do
 };
 
-proto.parse = function parse(){
+klass.parse = function parse(){
 	return;
 };
 
@@ -60,5 +60,5 @@ proto.serialize = function(rawValue){
 //TODO:	proto.addToBsonArray
 
 /** Register Expression */
-Expression.registerExpression("$const",proto.parse);
-Expression.registerExpression("$literal", proto.parse); // alias
+Expression.registerExpression("$const",klass.parse(ConstantExpression));
+Expression.registerExpression("$literal", klass.parse(ConstantExpression)); // alias

+ 3 - 2
lib/pipeline/expressions/DayOfMonthExpression.js

@@ -12,7 +12,7 @@ var DayOfMonthExpression = module.exports = function DayOfMonthExpression() {
 		if (arguments.length !== 1) throw new Error("one arg expected");
 		base.call(this);
 }, klass = DayOfMonthExpression,
-		base = require("./NaryExpression"),
+		base = require("../Value"),
 		proto = klass.prototype = Object.create(base.prototype, {
 				constructor: {
 						value: klass
@@ -22,6 +22,7 @@ var DayOfMonthExpression = module.exports = function DayOfMonthExpression() {
 // DEPENDENCIES
 var Expression = require("./Expression");
 
+
 // PROTOTYPE MEMBERS
 proto.getOpName = function getOpName() {
 		return "$dayOfMonth";
@@ -37,4 +38,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$dayOfMonth", DayOfMonthExpression.parse);
+Expression.registerExpression("$dayOfMonth", klass.parse(DayOfMonthExpression));

+ 1 - 1
lib/pipeline/expressions/DayOfWeekExpression.js

@@ -31,4 +31,4 @@ proto.evaluateInternal = function evaluateInternal(vars){
 };
 
 /** Register Expression */
-Expression.registerExpression("$dayOfWeek",DayOfWeekExpression.parse);
+Expression.registerExpression("$dayOfWeek",klass.parse(DayOfWeekExpression));

+ 1 - 1
lib/pipeline/expressions/DayOfYearExpression.js

@@ -39,5 +39,5 @@ klass.getDateDayOfYear = function getDateDayOfYear(d){
 };
 
 /** Register Expression */
-Expression.registerExpression("$dayOfYear",DayOfYearExpression.parse);
+Expression.registerExpression("$dayOfYear",klass.parse(DayOfYearExpression));
 

+ 1 - 1
lib/pipeline/expressions/DivideExpression.js

@@ -38,4 +38,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$divide",DivideExpression.parse);
+Expression.registerExpression("$divide",klass.parse(DivideExpression));

+ 1 - 1
lib/pipeline/expressions/HourExpression.js

@@ -33,4 +33,4 @@ proto.evaluateInternal = function evaluateInternal(vars){
 
 
 /** Register Expression */
-Expression.registerExpression("$hour",HourExpression.parse);
+Expression.registerExpression("$hour",klass.parse(HourExpression));

+ 1 - 2
lib/pipeline/expressions/IfNullExpression.js

@@ -35,7 +35,6 @@ proto.getOpName = function getOpName() {
  * @method evaluate
  **/
 proto.evaluateInternal = function evaluateInternal(vars) {
-		this.checkArgCount(2);
 		var left = this.operands[0].evaluateInternal(vars);
 		if (left !== undefined && left !== null) return left;
 		var right = this.operands[1].evaluateInternal(vars);
@@ -43,4 +42,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$ifNull", IfNullExpression.parse);
+Expression.registerExpression("$ifNull", klass.parse(IfNullExpression));

+ 1 - 1
lib/pipeline/expressions/MillisecondExpression.js

@@ -40,4 +40,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$millisecond", MillisecondExpression.parse);
+Expression.registerExpression("$millisecond", klass.parse(MillisecondExpression));

+ 1 - 1
lib/pipeline/expressions/MinuteExpression.js

@@ -38,4 +38,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$minute", MinuteExpression.parse);
+Expression.registerExpression("$minute", klass.parse(MinuteExpression));

+ 1 - 1
lib/pipeline/expressions/MonthExpression.js

@@ -38,4 +38,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$month", MonthExpression.parse);
+Expression.registerExpression("$month", klass.parse(MonthExpression));

+ 1 - 1
lib/pipeline/expressions/MultiplyExpression.js

@@ -38,4 +38,4 @@ proto.evaluateInternal = function evaluateInternal(vars){
 };
 
 /** Register Expression */
-Expression.registerExpression("$multiply", MultiplyExpression.parse);
+Expression.registerExpression("$multiply", klass.parse(MultiplyExpression));

+ 1 - 1
lib/pipeline/expressions/NotExpression.js

@@ -39,4 +39,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$not", NotExpression.parse);
+Expression.registerExpression("$not", klass.parse(NotExpression));

+ 1 - 1
lib/pipeline/expressions/OrExpression.js

@@ -64,4 +64,4 @@ proto.optimize = function optimize() {
 };
 
 /** Register Expression */
-Expression.registerExpression("$or", OrExpression.parse);
+Expression.registerExpression("$or", klass.parse(OrExpression));

+ 1 - 1
lib/pipeline/expressions/SecondExpression.js

@@ -40,4 +40,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$second", SecondExpression.parse);
+Expression.registerExpression("$second", klass.parse(SecondExpression));

+ 1 - 1
lib/pipeline/expressions/SetDifferenceExpression.js

@@ -50,4 +50,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$setdifference", SetDifferenceExpression.parse);
+Expression.registerExpression("$setdifference", klass.parse(SetDifferenceExpression));

+ 1 - 1
lib/pipeline/expressions/SetEqualsExpression.js

@@ -43,4 +43,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$setequals", SetEqualsExpression.parse);
+Expression.registerExpression("$setequals", klass.parse(SetEqualsExpression));

+ 2 - 2
lib/pipeline/expressions/SetIntersectionExpression.js

@@ -26,7 +26,7 @@ var Value = require("../Value"),
 
 // PROTOTYPE MEMBERS
 proto.getOpName = function getOpName() {
-		return "$setintersection";
+		return "$setIntersection";
 };
 
 /**
@@ -45,4 +45,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$setintersection", SetIntersectionExpression.parse);
+Expression.registerExpression("$setIntersection", klass.parse(SetIntersectionExpression));

+ 1 - 1
lib/pipeline/expressions/SetIsSubsetExpression.js

@@ -85,4 +85,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$setissubset", SetIsSubsetExpression.parse);
+Expression.registerExpression("$setissubset", klass.parse(SetIsSubsetExpression));

+ 2 - 2
lib/pipeline/expressions/SetUnionExpression.js

@@ -26,7 +26,7 @@ var Value = require("../Value"),
 
 // PROTOTYPE MEMBERS
 proto.getOpName = function getOpName() {
-		return "$setunion";
+		return "$setUnion";
 };
 
 /**
@@ -51,4 +51,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$setunion", SetUnionExpression.parse);
+Expression.registerExpression("$setUnion", klass.parse(SetUnionExpression));

+ 1 - 2
lib/pipeline/expressions/SizeExpression.js

@@ -33,11 +33,10 @@ proto.getOpName = function getOpName() {
  * Takes an array and return the size.
  **/
 proto.evaluateInternal = function evaluateInternal(vars) {
-		this.checkArgCount(1);
 		var array = this.operands[0].evaluateInternal(vars);
 		if (array instanceof Date) throw new Error("$size does not support dates; code 16376");
 		return array.length;
 };
 
 /** Register Expression */
-Expression.registerExpression("$size", SizeExpression.parse);
+Expression.registerExpression("$size", klass.parse(SizeExpression));

+ 2 - 3
lib/pipeline/expressions/StrcasecmpExpression.js

@@ -10,7 +10,7 @@
  **/
 var StrcasecmpExpression = module.exports = function StrcasecmpExpression() {
 		this.fixedArity(2);
-		if (arguments.length !== 0) throw new Error("zero args expected");
+		if (arguments.length !== 2) throw new Error("two args expected");
 		base.call(this);
 }, klass = StrcasecmpExpression,
 		base = require("./NaryExpression"),
@@ -35,7 +35,6 @@ proto.getOpName = function getOpName() {
  * @method evaluate
  **/
 proto.evaluateInternal = function evaluateInternal(vars) {
-		this.checkArgCount(2);
 		var val1 = this.operands[0].evaluateInternal(vars),
 				val2 = this.operands[1].evaluateInternal(vars),
 				str1 = Value.coerceToString(val1).toUpperCase(),
@@ -45,4 +44,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$strcasecmp", StrcasecmpExpression.parse);
+Expression.registerExpression("$strcasecmp", klass.parse(StrcasecmpExpression));

+ 2 - 2
lib/pipeline/expressions/SubstrExpression.js

@@ -10,7 +10,7 @@
  **/
 var SubstrExpression = module.exports = function SubstrExpression() {
 		this.fixedArity(3);
-		if (arguments.length !== 0) throw new Error("zero args expected");
+		if (arguments.length !== 3) throw new Error("three args expected");
 		base.call(this);
 }, klass = SubstrExpression,
 		base = require("./NaryExpression"),
@@ -47,4 +47,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$substr", SubstrExpression.parse);
+Expression.registerExpression("$substr", klass.parse(SubstrExpression));

+ 2 - 3
lib/pipeline/expressions/SubtractExpression.js

@@ -10,7 +10,7 @@
  **/
 var SubtractExpression = module.exports = function SubtractExpression() {
 		this.fixedArity(2);
-		if (arguments.length !== 0) throw new Error("zero args expected");
+		if (arguments.length !== 2) throw new Error("two args expected");
 		base.call(this);
 }, klass = SubtractExpression,
 		base = require("./NaryExpression"),
@@ -33,7 +33,6 @@ proto.getOpName = function getOpName() {
  * Takes an array that contains a pair of numbers and subtracts the second from the first, returning their difference.
  **/
 proto.evaluateInternal = function evaluateInternal(vars) {
-		this.checkArgCount(2);
 		var left = this.operands[0].evaluateInternal(vars),
 				right = this.operands[1].evaluateInternal(vars);
 		if (left instanceof Date || right instanceof Date) throw new Error("$subtract does not support dates; code 16376");
@@ -41,4 +40,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$subtract", SubtractExpression.parse);
+Expression.registerExpression("$subtract", klass.parse(SubtractExpression));

+ 2 - 3
lib/pipeline/expressions/ToLowerExpression.js

@@ -10,7 +10,7 @@
  **/
 var ToLowerExpression = module.exports = function ToLowerExpression() {
 		this.fixedArity(1);
-		if (arguments.length !== 0) throw new Error("zero args expected");
+		if (arguments.length !== 1) throw new Error("one args expected");
 		base.call(this);
 }, klass = ToLowerExpression,
 		base = require("./NaryExpression"),
@@ -33,11 +33,10 @@ proto.getOpName = function getOpName() {
  * Takes a single string and converts that string to lowercase, returning the result. All uppercase letters become lowercase.
  **/
 proto.evaluateInternal = function evaluateInternal(vars) {
-		this.checkArgCount(1);
 		var val = this.operands[0].evaluateInternal(vars),
 				str = Value.coerceToString(val);
 		return str.toLowerCase();
 };
 
 /** Register Expression */
-Expression.registerExpression("$toLower", ToLowerExpression.parse);
+Expression.registerExpression("$toLower", klass.parse(ToLowerExpression));

+ 2 - 3
lib/pipeline/expressions/ToUpperExpression.js

@@ -10,7 +10,7 @@
  **/
 var ToUpperExpression = module.exports = function ToUpperExpression() {
 		this.fixedArity(1);
-		if (arguments.length !== 0) throw new Error("zero args expected");
+		if (arguments.length !== 1) throw new Error("one args expected");
 		base.call(this);
 }, klass = ToUpperExpression,
 		base = require("./NaryExpression"),
@@ -33,11 +33,10 @@ proto.getOpName = function getOpName() {
  * Takes a single string and converts that string to lowercase, returning the result. All uppercase letters become lowercase.
  **/
 proto.evaluateInternal = function evaluateInternal(vars) {
-		this.checkArgCount(1);
 		var val = this.operands[0].evaluateInternal(vars),
 				str = Value.coerceToString(val);
 		return str.toUpperCase();
 };
 
 /** Register Expression */
-Expression.registerExpression("$toUpper", ToUpperExpression.parse);
+Expression.registerExpression("$toUpper", klass.parse(ToUpperExpression));

+ 2 - 3
lib/pipeline/expressions/WeekExpression.js

@@ -10,7 +10,7 @@
  **/
 var WeekExpression = module.exports = function WeekExpression() {
 		this.fixedArity(1);
-		if (arguments.length !== 0) throw new Error("zero args expected");
+		if (arguments.length !== 1) throw new Error("one args expected");
 		base.call(this);
 }, klass = WeekExpression,
 		base = require("./NaryExpression"),
@@ -38,7 +38,6 @@ proto.getOpName = function getOpName() {
  * @method evaluateInternal
  **/
 proto.evaluateInternal = function evaluateInternal(vars) {
-		this.checkArgCount(1);
 		var date = this.operands[0].evaluateInternal(vars),
 				dayOfWeek = date.getUTCDay(),
 				dayOfYear = DayOfYearExpression.getDateDayOfYear(date),
@@ -49,4 +48,4 @@ proto.evaluateInternal = function evaluateInternal(vars) {
 };
 
 /** Register Expression */
-Expression.registerExpression("$week", WeekExpression.parse);
+Expression.registerExpression("$week", klass.parse(WeekExpression));

+ 15 - 17
lib/pipeline/expressions/YearExpression.js

@@ -1,6 +1,6 @@
 "use strict";
 
-/** 
+/**
  * A $year pipeline expression.
  * @see evaluateInternal
  * @class YearExpression
@@ -9,26 +9,25 @@
  * @constructor
  **/
 var YearExpression = module.exports = function YearExpression() {
-		this.fixedArity(1);
-		if (arguments.length !== 0) throw new Error("zero args expected");
-		base.call(this);
+    this.nargs = 1;
+    base.call(this);
 }, klass = YearExpression,
-		base = require("./NaryExpression"),
-		proto = klass.prototype = Object.create(base.prototype, {
-				constructor: {
-						value: klass
-				}
-		});
+    base = require("./NaryExpression"),
+    proto = klass.prototype = Object.create(base.prototype, {
+        constructor: {
+            value: klass
+        }
+    });
 
 // DEPENDENCIES
 var Value = require("../Value"),
-		DayOfYearExpression = require("./DayOfYearExpression"),
-		Expression = require("./Expression");
+    DayOfYearExpression = require("./DayOfYearExpression"),
+    Expression = require("./Expression");
 
 
 // PROTOTYPE MEMBERS
 proto.getOpName = function getOpName() {
-		return "$year";
+    return "$year";
 };
 
 /**
@@ -36,10 +35,9 @@ proto.getOpName = function getOpName() {
  * @method evaluateInternal
  **/
 proto.evaluateInternal = function evaluateInternal(vars) {
-		this.checkArgCount(1);
-		var date = this.operands[0].evaluateInternal(vars);
-		return date.getUTCFullYear();
+    var date = this.operands[0].evaluateInternal(vars);
+    return date.getUTCFullYear();
 };
 
 /** Register Expression */
-Expression.registerExpression("$year", YearExpression.parse);
+Expression.registerExpression("$year", base.parse(YearExpression));

+ 297 - 238
test/lib/pipeline/expressions/CompareExpression.js

@@ -1,308 +1,367 @@
 "use strict";
 var assert = require("assert"),
-	CompareExpression = require("../../../../lib/pipeline/expressions/CompareExpression"),
-	Expression = require("../../../../lib/pipeline/expressions/Expression"),
-	FieldRangeExpression = require("../../../../lib/pipeline/expressions/FieldRangeExpression");
-
+		Expression = require("../../../../lib/pipeline/expressions/Expression"),
+		CompareExpression = require("../../../../lib/pipeline/expressions/CompareExpression"),
+		FieldRangeExpression = require("../../../../lib/pipeline/expressions/FieldRangeExpression"),
+		VariablesParseState = require("../../../../Lib/pipeline/expressions/VariablesParseState"),
+		VariablesIdGenerator = require("../../../../Lib/pipeline/expressions/VariablesIdGenerator"),
+		ConstantExpression = require("../../../../Lib/pipeline/expressions/ConstantExpression");
 
 module.exports = {
 
-	"CompareExpression": {
-
-		"constructor()": {
-
-			"should throw Error if no args": function testConstructor(){
-				assert.throws(function(){
-					new CompareExpression();
-				});
-			}
-
-		},
-
-		"#getOpName()": {
-
-			"should return the correct op name; $eq, $ne, $gt, $gte, $lt, $lte, $cmp": function testOpName(){
-				assert.equal((new CompareExpression(CompareExpression.EQ)).getOpName(), "$eq");
-				assert.equal((new CompareExpression(CompareExpression.NE)).getOpName(), "$ne");
-				assert.equal((new CompareExpression(CompareExpression.GT)).getOpName(), "$gt");
-				assert.equal((new CompareExpression(CompareExpression.GTE)).getOpName(), "$gte");
-				assert.equal((new CompareExpression(CompareExpression.LT)).getOpName(), "$lt");
-				assert.equal((new CompareExpression(CompareExpression.LTE)).getOpName(), "$lte");
-				assert.equal((new CompareExpression(CompareExpression.CMP)).getOpName(), "$cmp");
-			}
-
-		},
-
-		"#evaluate()": {
-
-			"$eq": {
-
-				"should return false if first < second; {$eq:[1,2]}": function testEqLt(){
-						assert.equal(Expression.parseOperand({"$eq":[1,2]}), false);
-				},
-
-				"should return true if first == second; {$eq:[1,1]}": function testEqEq(){
-					assert.equal(Expression.parseOperand({$eq:[1,1]}).evaluate({}), true);
-				},
-
-				"should return false if first > second {$eq:[1,0]}": function testEqGt(){
-					assert.equal(Expression.parseOperand({$eq:[1,0]}).evaluate({}), false);
-				},
-				"should return false if first and second are different types {$eq:[null,0]}": function testEqGt(){
-					assert.equal(Expression.parseOperand({$eq:[null,0]}).evaluate({}), false);
-				},
-				"should return false if first and second are different types {$eq:[undefined,0]}": function testEqGt(){
-					assert.equal(Expression.parseOperand({$eq:[undefined,0]}).evaluate({}), false);
-				},
-				"should return false if first and second are different arrays {$eq:[[1],[null]]}": function testEqGt(){
-					assert.equal(Expression.parseOperand({$eq:[[1],[null]]}).evaluate({}), false);
-				},
-				"should return false if first and second are different arrays {$eq:[[1],[]]}": function testEqGt(){
-					assert.equal(Expression.parseOperand({$eq:[[1],[]]}).evaluate({}), false);
-				},
-				"should return true if first and second are the same arrays {$eq:[[1],[1]]}": function testEqGt(){
-					assert.equal(Expression.parseOperand({$eq:[[1],[1]]}).evaluate({}), true);
-				}
-			},
-
-			"$ne": {
+		"CompareExpression": {
 
-				"should return true if first < second; {$ne:[1,2]}": function testNeLt(){
-					assert.equal(Expression.parseOperand({$ne:[1,2]}).evaluate({}), true);
-				},
+				"constructor()": {
 
-				"should return false if first == second; {$ne:[1,1]}": function testNeLt(){
-					assert.equal(Expression.parseOperand({$ne:[1,1]}).evaluate({}), false);
-				},
+						"should throw Error if no args": function testConstructor() {
+								assert.throws(function() {
+										new CompareExpression();
+								});
+						}
 
-				"should return true if first > second; {$ne:[1,0]}": function testNeGt(){
-					assert.equal(Expression.parseOperand({$ne:[1,0]}).evaluate({}), true);
-				}
-
-			},
-
-			"$gt": {
-
-				"should return false if first < second; {$gt:[1,2]}": function testGtLt(){
-					assert.equal(Expression.parseOperand({$gt:[1,2]}).evaluate({}), false);
 				},
 
-				"should return false if first == second; {$gt:[1,1]}": function testGtLt(){
-					assert.equal(Expression.parseOperand({$gt:[1,1]}).evaluate({}), false);
-				},
-
-				"should return true if first > second; {$gt:[1,0]}": function testGtGt(){
-					assert.equal(Expression.parseOperand({$gt:[1,0]}).evaluate({}), true);
-				}
-
-			},
+				"#getOpName()": {
 
-			"$gte": {
+						"should return the correct op name; $eq, $ne, $gt, $gte, $lt, $lte, $cmp": function testOpName() {
+								assert.equal((new CompareExpression(CompareExpression.EQ)).getOpName(), "$eq");
+								assert.equal((new CompareExpression(CompareExpression.NE)).getOpName(), "$ne");
+								assert.equal((new CompareExpression(CompareExpression.GT)).getOpName(), "$gt");
+								assert.equal((new CompareExpression(CompareExpression.GTE)).getOpName(), "$gte");
+								assert.equal((new CompareExpression(CompareExpression.LT)).getOpName(), "$lt");
+								assert.equal((new CompareExpression(CompareExpression.LTE)).getOpName(), "$lte");
+								assert.equal((new CompareExpression(CompareExpression.CMP)).getOpName(), "$cmp");
+						}
 
-				"should return false if first < second; {$gte:[1,2]}": function testGteLt(){
-					assert.equal(Expression.parseOperand({$gte:[1,2]}).evaluate({}), false);
 				},
 
-				"should return true if first == second; {$gte:[1,1]}": function testGteLt(){
-					assert.equal(Expression.parseOperand({$gte:[1,1]}).evaluate({}), true);
-				},
+				"#evaluateInternal()": {
+
+						"$eq": {
+
+								"should return false if first < second; {$eq:[1,2]}": function testEqLt() {
+										//debugger;
+										var idGenerator = new VariablesIdGenerator();
+										var vps = new VariablesParseState(idGenerator);
+										var parseOp = Expression.parseOperand({
+												$eq: [{
+														$const: 1
+												}, {
+														$const: 2
+												}]
+										}, vps);
+										var result = parseOp.evaluateInternal({});
+
+										//assert.equal(new CompareExpression( CompareExpression.EQ).evaluateInternal({"$eq":[1,2]}), false);
+										assert.equal(result, false);
+
+								},
+
+								"should return true if first == second; {$eq:[1,1]}": function testEqEq() {
+										var idGenerator = new VariablesIdGenerator();
+										var vps = new VariablesParseState(idGenerator);
+
+										assert.equal(Expression.parseOperand({
+												$eq: [1, 1]
+										}, vps).evaluateInternal({}), true);
+								},
+
+								"should return false if first > second {$eq:[1,0]}": function testEqGt() {
+										var idGenerator = new VariablesIdGenerator();
+										var vps = new VariablesParseState(idGenerator);
+										assert.equal(Expression.parseOperand({
+												$eq: [1, 0]
+										}).evaluateInternal({}), false);
+								},
+
+								"should return false if first and second are different types {$eq:[null,0]}": function testEqGt() {
+										var idGenerator = new VariablesIdGenerator();
+										var vps = new VariablesParseState(idGenerator);
+										assert.equal(Expression.parseOperand({
+												$eq: [null, 0]
+										}, vps).evaluateInternal({}), false);
+								},
+
+								"should return false if first and second are different types {$eq:[undefined,0]}": function testEqGt() {
+										var idGenerator = new VariablesIdGenerator();
+										var vps = new VariablesParseState(idGenerator);
+										assert.equal(Expression.parseOperand({
+												$eq: [undefined, 0]
+										}, vps).evaluateInternal({}), false);
+								},
+
+								"should return false if first and second are different arrays {$eq:[[1],[null]]}": function testEqGt() {
+										var idGenerator = new VariablesIdGenerator();
+										var vps = new VariablesParseState(idGenerator);
+										assert.equal(Expression.parseOperand({
+												$eq: [
+														[1],
+														[null]
+												]
+										}, vps).evaluateInternal({}), false);
+								},
+
+								"should return false if first and second are different arrays {$eq:[[1],[]]}": function testEqGt() {
+										assert.equal(Expression.parseOperand({
+												$eq: [
+														[1],
+														[]
+												]
+										}, vps).evaluateInternal({}), false);
+										var idGenerator = new VariablesIdGenerator();
+										var vps = new VariablesParseState(idGenerator);
+								},
+
+								"should return true if first and second are the same arrays {$eq:[[1],[1]]}": function testEqGt() {
+										var idGenerator = new VariablesIdGenerator();
+										var vps = new VariablesParseState(idGenerator);
+										assert.equal(Expression.parseOperand({
+												$eq: [
+														[1],
+														[1]
+												]
+										}, vps).evaluateInternal({}), true);
+								}
+						},
+
+						// 	"$ne": {
+
+						// 		"should return true if first < second; {$ne:[1,2]}": function testNeLt(){
+						// 			assert.equal(Expression.parseOperand({$ne:[1,2]}).evaluateInternal({}), true);
+						// 		},
+
+						// 		"should return false if first == second; {$ne:[1,1]}": function testNeLt(){
+						// 			assert.equal(Expression.parseOperand({$ne:[1,1]}).evaluateInternal({}), false);
+						// 		},
+
+						// 		"should return true if first > second; {$ne:[1,0]}": function testNeGt(){
+						// 			assert.equal(Expression.parseOperand({$ne:[1,0]}).evaluateInternal({}), true);
+						// 		}
+
+						// 	},
+
+						// 	"$gt": {
+
+						// 		"should return false if first < second; {$gt:[1,2]}": function testGtLt(){
+						// 			assert.equal(Expression.parseOperand({$gt:[1,2]}).evaluateInternal({}), false);
+						// 		},
+
+						// 		"should return false if first == second; {$gt:[1,1]}": function testGtLt(){
+						// 			assert.equal(Expression.parseOperand({$gt:[1,1]}).evaluateInternal({}), false);
+						// 		},
+
+						// 		"should return true if first > second; {$gt:[1,0]}": function testGtGt(){
+						// 			assert.equal(Expression.parseOperand({$gt:[1,0]}).evaluateInternal({}), true);
+						// 		}
+
+						// 	},
+
+						// 	"$gte": {
+
+						// 		"should return false if first < second; {$gte:[1,2]}": function testGteLt(){
+						// 			assert.equal(Expression.parseOperand({$gte:[1,2]}).evaluateInternal({}), false);
+						// 		},
+
+						// 		"should return true if first == second; {$gte:[1,1]}": function testGteLt(){
+						// 			assert.equal(Expression.parseOperand({$gte:[1,1]}).evaluateInternal({}), true);
+						// 		},
+
+						// 		"should return true if first > second; {$gte:[1,0]}": function testGteGt(){
+						// 			assert.equal(Expression.parseOperand({$gte:[1,0]}).evaluateInternal({}), true);
+						// 		}
+
+						// 	},
+
+						// 	"$lt": {
+
+						// 		"should return true if first < second; {$lt:[1,2]}": function testLtLt(){
+						// 			assert.equal(Expression.parseOperand({$lt:[1,2]}).evaluateInternal({}), true);
+						// 		},
 
-				"should return true if first > second; {$gte:[1,0]}": function testGteGt(){
-					assert.equal(Expression.parseOperand({$gte:[1,0]}).evaluate({}), true);
-				}
+						// 		"should return false if first == second; {$lt:[1,1]}": function testLtLt(){
+						// 			assert.equal(Expression.parseOperand({$lt:[1,1]}).evaluateInternal({}), false);
+						// 		},
 
-			},
+						// 		"should return false if first > second; {$lt:[1,0]}": function testLtGt(){
+						// 			assert.equal(Expression.parseOperand({$lt:[1,0]}).evaluateInternal({}), false);
+						// 		}
 
-			"$lt": {
+						// 	},
 
-				"should return true if first < second; {$lt:[1,2]}": function testLtLt(){
-					assert.equal(Expression.parseOperand({$lt:[1,2]}).evaluate({}), true);
-				},
+						// 	"$lte": {
 
-				"should return false if first == second; {$lt:[1,1]}": function testLtLt(){
-					assert.equal(Expression.parseOperand({$lt:[1,1]}).evaluate({}), false);
-				},
+						// 		"should return true if first < second; {$lte:[1,2]}": function testLteLt(){
+						// 			assert.equal(Expression.parseOperand({$lte:[1,2]}).evaluateInternal({}), true);
+						// 		},
 
-				"should return false if first > second; {$lt:[1,0]}": function testLtGt(){
-					assert.equal(Expression.parseOperand({$lt:[1,0]}).evaluate({}), false);
-				}
+						// 		"should return true if first == second; {$lte:[1,1]}": function testLteLt(){
+						// 			assert.equal(Expression.parseOperand({$lte:[1,1]}).evaluateInternal({}), true);
+						// 		},
 
-			},
+						// 		"should return false if first > second; {$lte:[1,0]}": function testLteGt(){
+						// 			assert.equal(Expression.parseOperand({$lte:[1,0]}).evaluateInternal({}), false);
+						// 		}
 
-			"$lte": {
+						// 	},
 
-				"should return true if first < second; {$lte:[1,2]}": function testLteLt(){
-					assert.equal(Expression.parseOperand({$lte:[1,2]}).evaluate({}), true);
-				},
+						// 	"$cmp": {
 
-				"should return true if first == second; {$lte:[1,1]}": function testLteLt(){
-					assert.equal(Expression.parseOperand({$lte:[1,1]}).evaluate({}), true);
-				},
+						// 		"should return -1 if first < second; {$cmp:[1,2]}": function testCmpLt(){
+						// 			assert.equal(Expression.parseOperand({$cmp:[1,2]}).evaluateInternal({}), -1);
+						// 		},
 
-				"should return false if first > second; {$lte:[1,0]}": function testLteGt(){
-					assert.equal(Expression.parseOperand({$lte:[1,0]}).evaluate({}), false);
-				}
+						// 		"should return 0 if first < second; {$cmp:[1,1]}": function testCmpLt(){
+						// 			assert.equal(Expression.parseOperand({$cmp:[1,1]}).evaluateInternal({}), 0);
+						// 		},
 
-			},
+						// 		"should return 1 if first < second; {$cmp:[1,0]}": function testCmpLt(){
+						// 			assert.equal(Expression.parseOperand({$cmp:[1,0]}).evaluateInternal({}), 1);
+						// 		},
 
-			"$cmp": {
+						// 		"should return 1 even if comparison is larger; {$cmp:['z','a']}": function testCmpBracketed(){
+						// 			assert.equal(Expression.parseOperand({$cmp:['z','a']}).evaluateInternal({}), 1);
+						// 		}
 
-				"should return -1 if first < second; {$cmp:[1,2]}": function testCmpLt(){
-					assert.equal(Expression.parseOperand({$cmp:[1,2]}).evaluate({}), -1);
-				},
+						// 	},
 
-				"should return 0 if first < second; {$cmp:[1,1]}": function testCmpLt(){
-					assert.equal(Expression.parseOperand({$cmp:[1,1]}).evaluate({}), 0);
-				},
+						// 	"should throw Error": {
 
-				"should return 1 if first < second; {$cmp:[1,0]}": function testCmpLt(){
-					assert.equal(Expression.parseOperand({$cmp:[1,0]}).evaluate({}), 1);
-				},
+						// 		"if zero operands are provided; {$ne:[]}": function testZeroOperands(){
+						// 			assert.throws(function(){
+						// 				Expression.parseOperand({$ne:[]}).evaluateInternal({});
+						// 			});
+						// 		},
 
-				"should return 1 even if comparison is larger; {$cmp:['z','a']}": function testCmpBracketed(){
-					assert.equal(Expression.parseOperand({$cmp:['z','a']}).evaluate({}), 1);
-				}
+						// 		"if one operand is provided; {$eq:[1]}": function testOneOperand(){
+						// 			assert.throws(function(){
+						// 				Expression.parseOperand({$eq:[1]}).evaluateInternal({});
+						// 			});
+						// 		},
 
-			},
+						// 		"if three operands are provided; {$gt:[2,3,4]}": function testThreeOperands(){
+						// 			assert.throws(function(){
+						// 				Expression.parseOperand({$gt:[2,3,4]}).evaluateInternal({});
+						// 			});
+						// 		}
+						// 	}
 
-			"should throw Error": {
+						// },
 
-				"if zero operands are provided; {$ne:[]}": function testZeroOperands(){
-					assert.throws(function(){
-						Expression.parseOperand({$ne:[]}).evaluate({});
-					});
-				},
+						// "#optimize()": {
 
-				"if one operand is provided; {$eq:[1]}": function testOneOperand(){
-					assert.throws(function(){
-						Expression.parseOperand({$eq:[1]}).evaluate({});
-					});
-				},
+						// 	"should optimize constants; {$eq:[1,1]}": function testOptimizeConstants(){
+						// 		assert.deepEqual(Expression.parseOperand({$eq:[1,1]}).optimize().toJSON(true), {$const:true});
+						// 	},
 
-				"if three operands are provided; {$gt:[2,3,4]}": function testThreeOperands(){
-					assert.throws(function(){
-						Expression.parseOperand({$gt:[2,3,4]}).evaluate({});
-					});
-				}
-			}
-			
-		},
+						// 	"should not optimize if $cmp op; {$cmp:[1,'$a']}": function testNoOptimizeCmp(){
+						// 		assert.deepEqual(Expression.parseOperand({$cmp:[1,'$a']}).optimize().toJSON(), {$cmp:[1,'$a']});
+						// 	},
 
-		"#optimize()": {
+						// 	"should not optimize if $ne op; {$ne:[1,'$a']}": function testNoOptimizeNe(){
+						// 		assert.deepEqual(Expression.parseOperand({$ne:[1,'$a']}).optimize().toJSON(), {$ne:[1,'$a']});
+						// 	},
 
-			"should optimize constants; {$eq:[1,1]}": function testOptimizeConstants(){
-				assert.deepEqual(Expression.parseOperand({$eq:[1,1]}).optimize().toJSON(true), {$const:true});
-			},
+						// 	"should not optimize if no constants; {$ne:['$a','$b']}": function testNoOptimizeNoConstant(){
+						// 		assert.deepEqual(Expression.parseOperand({$ne:['$a','$b']}).optimize().toJSON(), {$ne:['$a','$b']});
+						// 	},
 
-			"should not optimize if $cmp op; {$cmp:[1,'$a']}": function testNoOptimizeCmp(){
-				assert.deepEqual(Expression.parseOperand({$cmp:[1,'$a']}).optimize().toJSON(), {$cmp:[1,'$a']});
-			},
+						// 	"should not optimize without an immediate field path;": {
 
-			"should not optimize if $ne op; {$ne:[1,'$a']}": function testNoOptimizeNe(){
-				assert.deepEqual(Expression.parseOperand({$ne:[1,'$a']}).optimize().toJSON(), {$ne:[1,'$a']});
-			},
+						// 		"{$eq:[{$and:['$a']},1]}": function testNoOptimizeWithoutFieldPath(){
+						// 			assert.deepEqual(Expression.parseOperand({$eq:[{$and:['$a']},1]}).optimize().toJSON(), {$eq:[{$and:['$a']},1]});
+						// 		},
 
-			"should not optimize if no constants; {$ne:['$a','$b']}": function testNoOptimizeNoConstant(){
-				assert.deepEqual(Expression.parseOperand({$ne:['$a','$b']}).optimize().toJSON(), {$ne:['$a','$b']});
-			},
+						// 		"(reversed); {$eq:[1,{$and:['$a']}]}": function testNoOptimizeWithoutFieldPathReverse(){
+						// 			assert.deepEqual(Expression.parseOperand({$eq:[1,{$and:['$a']}]}).optimize().toJSON(), {$eq:[1,{$and:['$a']}]});
+						// 		}
 
-			"should not optimize without an immediate field path;": {
+						// 	},
 
-				"{$eq:[{$and:['$a']},1]}": function testNoOptimizeWithoutFieldPath(){
-					assert.deepEqual(Expression.parseOperand({$eq:[{$and:['$a']},1]}).optimize().toJSON(), {$eq:[{$and:['$a']},1]});
-				},
+						// 	"should optimize $eq expressions;": {
 
-				"(reversed); {$eq:[1,{$and:['$a']}]}": function testNoOptimizeWithoutFieldPathReverse(){
-					assert.deepEqual(Expression.parseOperand({$eq:[1,{$and:['$a']}]}).optimize().toJSON(), {$eq:[1,{$and:['$a']}]});
-				}
+						// 		"{$eq:['$a',1]}": function testOptimizeEq(){
+						// 			var expr = Expression.parseOperand({$eq:['$a',1]}).optimize();
+						// 			assert(expr instanceof FieldRangeExpression, "not optimized");
+						// 			assert.deepEqual(expr.toJSON(), {$eq:['$a',1]});
+						// 		},
 
-			},
+						// 		"{$eq:[1,'$a']} (reversed)": function testOptimizeEqReverse(){
+						// 			var expr = Expression.parseOperand({$eq:[1,'$a']}).optimize();
+						// 			assert(expr instanceof FieldRangeExpression, "not optimized");
+						// 			assert.deepEqual(expr.toJSON(), {$eq:['$a',1]});
+						// 		}
 
-			"should optimize $eq expressions;": {
+						// 	},
 
-				"{$eq:['$a',1]}": function testOptimizeEq(){
-					var expr = Expression.parseOperand({$eq:['$a',1]}).optimize();
-					assert(expr instanceof FieldRangeExpression, "not optimized");
-					assert.deepEqual(expr.toJSON(), {$eq:['$a',1]});
-				},
+						// 	"should optimize $lt expressions;": {
 
-				"{$eq:[1,'$a']} (reversed)": function testOptimizeEqReverse(){
-					var expr = Expression.parseOperand({$eq:[1,'$a']}).optimize();
-					assert(expr instanceof FieldRangeExpression, "not optimized");
-					assert.deepEqual(expr.toJSON(), {$eq:['$a',1]});
-				}
+						// 		"{$lt:['$a',1]}": function testOptimizeLt(){
+						// 			var expr = Expression.parseOperand({$lt:['$a',1]}).optimize();
+						// 			assert(expr instanceof FieldRangeExpression, "not optimized");
+						// 			assert.deepEqual(expr.toJSON(), {$lt:['$a',1]});
+						// 		},
 
-			},
+						// 		"{$lt:[1,'$a']} (reversed)": function testOptimizeLtReverse(){
+						// 			var expr = Expression.parseOperand({$lt:[1,'$a']}).optimize();
+						// 			assert(expr instanceof FieldRangeExpression, "not optimized");
+						// 			assert.deepEqual(expr.toJSON(), {$gt:['$a',1]});
+						// 		}
 
-			"should optimize $lt expressions;": {
+						// 	},
 
-				"{$lt:['$a',1]}": function testOptimizeLt(){
-					var expr = Expression.parseOperand({$lt:['$a',1]}).optimize();
-					assert(expr instanceof FieldRangeExpression, "not optimized");
-					assert.deepEqual(expr.toJSON(), {$lt:['$a',1]});
-				},
+						// 	"should optimize $lte expressions;": {
 
-				"{$lt:[1,'$a']} (reversed)": function testOptimizeLtReverse(){
-					var expr = Expression.parseOperand({$lt:[1,'$a']}).optimize();
-					assert(expr instanceof FieldRangeExpression, "not optimized");
-					assert.deepEqual(expr.toJSON(), {$gt:['$a',1]});
-				}
+						// 		"{$lte:['$b',2]}": function testOptimizeLte(){
+						// 			var expr = Expression.parseOperand({$lte:['$b',2]}).optimize();
+						// 			assert(expr instanceof FieldRangeExpression, "not optimized");
+						// 			assert.deepEqual(expr.toJSON(), {$lte:['$b',2]});
+						// 		},
 
-			},
+						// 		"{$lte:[2,'$b']} (reversed)": function testOptimizeLteReverse(){
+						// 			var expr = Expression.parseOperand({$lte:[2,'$b']}).optimize();
+						// 			assert(expr instanceof FieldRangeExpression, "not optimized");
+						// 			assert.deepEqual(expr.toJSON(), {$gte:['$b',2]});
+						// 		}
 
-			"should optimize $lte expressions;": {
+						// 	},
 
-				"{$lte:['$b',2]}": function testOptimizeLte(){
-					var expr = Expression.parseOperand({$lte:['$b',2]}).optimize();
-					assert(expr instanceof FieldRangeExpression, "not optimized");
-					assert.deepEqual(expr.toJSON(), {$lte:['$b',2]});
-				},
+						// 	"should optimize $gt expressions;": {
 
-				"{$lte:[2,'$b']} (reversed)": function testOptimizeLteReverse(){
-					var expr = Expression.parseOperand({$lte:[2,'$b']}).optimize();
-					assert(expr instanceof FieldRangeExpression, "not optimized");
-					assert.deepEqual(expr.toJSON(), {$gte:['$b',2]});
-				}
+						// 		"{$gt:['$b',2]}": function testOptimizeGt(){
+						// 			var expr = Expression.parseOperand({$gt:['$b',2]}).optimize();
+						// 			assert(expr instanceof FieldRangeExpression, "not optimized");
+						// 			assert.deepEqual(expr.toJSON(), {$gt:['$b',2]});
+						// 		},
 
-			},
+						// 		"{$gt:[2,'$b']} (reversed)": function testOptimizeGtReverse(){
+						// 			var expr = Expression.parseOperand({$gt:[2,'$b']}).optimize();
+						// 			assert(expr instanceof FieldRangeExpression, "not optimized");
+						// 			assert.deepEqual(expr.toJSON(), {$lt:['$b',2]});
+						// 		}
 
-			"should optimize $gt expressions;": {
+						// 	},
 
-				"{$gt:['$b',2]}": function testOptimizeGt(){
-					var expr = Expression.parseOperand({$gt:['$b',2]}).optimize();
-					assert(expr instanceof FieldRangeExpression, "not optimized");
-					assert.deepEqual(expr.toJSON(), {$gt:['$b',2]});
-				},
+						// 	"should optimize $gte expressions;": {
 
-				"{$gt:[2,'$b']} (reversed)": function testOptimizeGtReverse(){
-					var expr = Expression.parseOperand({$gt:[2,'$b']}).optimize();
-					assert(expr instanceof FieldRangeExpression, "not optimized");
-					assert.deepEqual(expr.toJSON(), {$lt:['$b',2]});
-				}
+						// 		"{$gte:['$b',2]}": function testOptimizeGte(){
+						// 			var expr = Expression.parseOperand({$gte:['$b',2]}).optimize();
+						// 			assert(expr instanceof FieldRangeExpression, "not optimized");
+						// 			assert.deepEqual(expr.toJSON(), {$gte:['$b',2]});
+						// 		},
 
-			},
+						// 		"{$gte:[2,'$b']} (reversed)": function testOptimizeGteReverse(){
+						// 			var expr = Expression.parseOperand({$gte:[2,'$b']}).optimize();
+						// 			assert(expr instanceof FieldRangeExpression, "not optimized");
+						// 			assert.deepEqual(expr.toJSON(), {$lte:['$b',2]});
+						// 		}
 
-			"should optimize $gte expressions;": {
+						// 	},
 
-				"{$gte:['$b',2]}": function testOptimizeGte(){
-					var expr = Expression.parseOperand({$gte:['$b',2]}).optimize();
-					assert(expr instanceof FieldRangeExpression, "not optimized");
-					assert.deepEqual(expr.toJSON(), {$gte:['$b',2]});
-				},
 
-				"{$gte:[2,'$b']} (reversed)": function testOptimizeGteReverse(){
-					var expr = Expression.parseOperand({$gte:[2,'$b']}).optimize();
-					assert(expr instanceof FieldRangeExpression, "not optimized");
-					assert.deepEqual(expr.toJSON(), {$lte:['$b',2]});
 				}
 
-			},
-
-
 		}
 
-	}
-
 };
 
-if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);
+if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);

+ 33 - 10
test/lib/pipeline/expressions/DayOfMonthExpression.js

@@ -1,7 +1,9 @@
 "use strict";
 var assert = require("assert"),
 		DayOfMonthExpression = require("../../../../lib/pipeline/expressions/DayOfMonthExpression"),
-		Expression = require("../../../../lib/pipeline/expressions/Expression");
+		Expression = require("../../../../lib/pipeline/expressions/Expression"),
+		VariablesParseState = require("../../../../Lib/pipeline/expressions/VariablesParseState"),
+		VariablesIdGenerator = require("../../../../Lib/pipeline/expressions/VariablesIdGenerator");
 
 
 module.exports = {
@@ -32,20 +34,41 @@ module.exports = {
 
 				},
 
-				"#evaluateInternal()": {
+				"#evaulateInternal1()": {
+
+						"should return day of month; 10 for 2013-03-10": function testOpName() {
+								assert.equal(new DayOfMonthExpression("2013-03-10T00:00:00.000Z").evaluateInternal(), "10");
+						}
+
+				},
+
+				"#evaluateInternal2()": {
 
 						"should return day of month; 18 for 2013-02-18": function testStuff() {
-								assert.strictEqual(Expression.parseOperand({
+
+								var idGenerator = new VariablesIdGenerator();
+								var vps = new VariablesParseState(idGenerator);
+								var parseOp = Expression.parseOperand({
 										$dayOfMonth: "$someDate"
-								}).evaluateInternal({
-										someDate: new Date("2013-02-18T00:00:00.000Z")
-								}), 18);
+								}, vps);
+
+								var result = parseOp.evaluateInternal({
+										$someDate: new Date("2013-02-18T00:00:00.000Z")
+								});
+
+								assert.strictEqual(result, "2");
+
+										// assert.strictEqual(Expression.parseOperand({
+										// $dayOfMonth: "$someDate"
+										// }, vps).evaluate({
+										// someDate: new Date("2013-02-18T00:00:00.000Z")
+										// }), 18);
+								}
+
 						}
 
 				}
 
-		}
-
-};
+		};
 
-if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);
+		if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);