浏览代码

Merge pull request #72 from RiveraGroup/feature/mongo_2.6.5_expressions_Concat

Feature/mongo 2.6.5 expressions concat
Kyle P Davis 11 年之前
父节点
当前提交
5e93e74d8f
共有 2 个文件被更改,包括 88 次插入67 次删除
  1. 25 24
      lib/pipeline/expressions/ConcatExpression.js
  2. 63 43
      test/lib/pipeline/expressions/ConcatExpression_test.js

+ 25 - 24
lib/pipeline/expressions/ConcatExpression.js

@@ -1,41 +1,42 @@
 "use strict";
 "use strict";
 
 
-var Expression = require("./Expression");
-
 /**
 /**
  * Creates an expression that concatenates a set of string operands.
  * Creates an expression that concatenates a set of string operands.
  * @class ConcatExpression
  * @class ConcatExpression
  * @namespace mungedb-aggregate.pipeline.expressions
  * @namespace mungedb-aggregate.pipeline.expressions
  * @module mungedb-aggregate
  * @module mungedb-aggregate
  * @constructor
  * @constructor
- **/
+ */
 var ConcatExpression = module.exports = function ConcatExpression(){
 var ConcatExpression = module.exports = function ConcatExpression(){
 	if (arguments.length !== 0) throw new Error("zero args expected");
 	if (arguments.length !== 0) throw new Error("zero args expected");
 	base.call(this);
 	base.call(this);
 }, klass = ConcatExpression, base = require("./VariadicExpressionT")(ConcatExpression), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 }, klass = ConcatExpression, base = require("./VariadicExpressionT")(ConcatExpression), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 
 
-// DEPENDENCIES
-var Value = require("../Value");
-var Expression = require("./Expression");
-
-// PROTOTYPE MEMBERS
-klass.opName = "$concat";
-proto.getOpName = function getOpName(){
-	return klass.opName;
-};
+var Value = require("../Value"),
+	Expression = require("./Expression");
 
 
-/**
- * Concats a string of values together.
- * @method evaluate
- **/
 proto.evaluateInternal = function evaluateInternal(vars) {
 proto.evaluateInternal = function evaluateInternal(vars) {
-    return this.operands.map(function(x) {
-		var y = x.evaluateInternal(vars);
-		if(typeof(y) !== "string") {
-	    	throw new Error("$concat only supports strings - 16702");
-		}
-	return y;
-    }).join("");
+	var n = this.operands.length;
+
+	var result = "";
+	for (var i = 0; i < n; ++i) {
+		var val = this.operands[i].evaluateInternal(vars);
+
+		if (val === undefined || val === null)
+			return null;
+
+		if (typeof val !== "string")
+			throw new Error(this.getOpName() + " only supports strings, not " +
+				Value.getType(val) + "; uassert code 16702");
+
+		result += val;
+	}
+
+	return result;
 };
 };
 
 
-Expression.registerExpression(klass.opName, base.parse);
+Expression.registerExpression("$concat", base.parse);
+
+proto.getOpName = function getOpName(){
+	return "$concat";
+};

+ 63 - 43
test/lib/pipeline/expressions/ConcatExpression_test.js

@@ -1,71 +1,91 @@
 "use strict";
 "use strict";
 var assert = require("assert"),
 var assert = require("assert"),
 	ConcatExpression = require("../../../../lib/pipeline/expressions/ConcatExpression"),
 	ConcatExpression = require("../../../../lib/pipeline/expressions/ConcatExpression"),
+	VariablesParseState = require("../../../../lib/pipeline/expressions/VariablesParseState"),
+	VariablesIdGenerator = require("../../../../lib/pipeline/expressions/VariablesIdGenerator"),
 	Expression = require("../../../../lib/pipeline/expressions/Expression");
 	Expression = require("../../../../lib/pipeline/expressions/Expression");
 
 
+// Mocha one-liner to make these tests self-hosted
+if(!module.parent)return(require.cache[__filename]=null,(new(require("mocha"))({ui:"exports",reporter:"spec",grep:process.env.TEST_GREP})).addFile(__filename).run(process.exit));
 
 
-module.exports = {
+exports.ConcatExpression = {
 
 
-	"ConcatExpression": {
+	beforeEach: function() {
+		this.vps = new VariablesParseState(new VariablesIdGenerator());
+	},
 
 
-		"constructor()": {
+	"constructor()": {
 
 
-			"should not throw Error when constructing without args": function testConstructor(){
-				assert.doesNotThrow(function(){
-					new ConcatExpression();
-				});
-			},
-			"should throw Error when constructing with args": function testConstructor(){
-				assert.throws(function(){
-					new ConcatExpression("should die");
-				});
-			}
+		"should not throw Error when constructing without args": function() {
+			assert.doesNotThrow(function() {
+				new ConcatExpression();
+			});
 		},
 		},
 
 
-		"#getOpName()": {
+		"should throw Error when constructing with args": function() {
+			assert.throws(function() {
+				new ConcatExpression("should die");
+			});
+		},
+
+	},
 
 
-			"should return the correct op name; $concat": function testOpName(){
-				assert.equal(new ConcatExpression().getOpName(), "$concat");
-			}
+	"#getOpName()": {
 
 
+		"should return the correct op name; $concat": function() {
+			assert.equal(new ConcatExpression().getOpName(), "$concat");
 		},
 		},
 
 
-		"#getFactory()": {
+	},
 
 
-			"should return the constructor for this class": function factoryIsConstructor(){
-				assert.equal(new ConcatExpression().getFactory(), ConcatExpression);
-			}
+	"#evaluate()": {
 
 
+		"should return empty string if no operands were given; {$concat:[]}": function() {
+			var expr = Expression.parseOperand({$concat:[]}, this.vps);
+			assert.equal(expr.evaluate(), "");
 		},
 		},
 
 
-		"#evaluate()": {
+		"should return mystring if operands are my string; {$concat:[my, string]}": function() {
+			var expr = Expression.parseOperand({$concat:["my", "string"]}, this.vps);
+			assert.equal(expr.evaluate(), "mystring");
+		},
 
 
-			"should return empty string if no operands were given; {$concat:[]}": function testEmpty(){
-				assert.equal(Expression.parseOperand({$concat:[]}).evaluate(), "");
-			},
+		"should return mystring if operands are my and $a; {$concat:[my,$a]}": function() {
+			var expr = Expression.parseOperand({$concat:["my","$a"]}, this.vps);
+			assert.equal(expr.evaluate({a:"string"}), "mystring");
+		},
 
 
-			"should return mystring if operands are my string; {$concat:[my, string]}": function testConcat(){
-				assert.equal(Expression.parseOperand({$concat:["my", "string"]}).evaluate(), "mystring");
-			},
+		"should return null if an operand evaluates to null; {$concat:[my,$a]}": function() {
+			var expr = Expression.parseOperand({$concat:["my","$a"]}, this.vps);
+			assert.equal(expr.evaluate({a:null}), null);
+		},
 
 
-			"should return mystring if operands are my and $a; {$concat:[my,$a]}": function testFieldPath(){
-				assert.equal(Expression.parseOperand({$concat:["my","$a"]}).evaluate({a:"string"}), "mystring");
-			},
+		"should return null if an operand evaluates to undefined; {$concat:[my,$a]}": function() {
+			var expr = Expression.parseOperand({$concat:["my","$a"]}, this.vps);
+			assert.equal(expr.evaluate({a:undefined}), null);
+		},
 
 
-			"should return null if an operand evaluates to null; {$concat:[my,$a]}": function testNull(){
-				assert.equal(Expression.parseOperand({$concat:["my","$a"]}).evaluate({a:null}), null);
-			},
+		"should throw if an operand is a number": function() {
+			var expr = Expression.parseOperand({$concat:["my","$a"]}, this.vps);
+			assert.throws(function() {
+				expr.evaluate({a:100});
+			});
+		},
 
 
-			"should throw if a non-string is passed in: {$concat:[my,$a]}": function testNull(){
-				assert.throws(function(){
-					Expression.parseOperand({$concat:["my","$a"]}).evaluate({a:100});
-				});
+		"should throw if an operand is a date": function() {
+			var expr = Expression.parseOperand({$concat:["my","$a"]}, this.vps);
+			assert.throws(function() {
+				expr.evaluate({a:new Date()});
+			});
+		},
 
 
-			}
-		}
+		"should throw if an operand is a boolean": function() {
+			var expr = Expression.parseOperand({$concat:["my","$a"]}, this.vps)
+			assert.throws(function() {
+				expr.evaluate({a:true});
+			});
+		},
 
 
-	}
+	},
 
 
 };
 };
-
-if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);