Explorar el Código

Fixes #988. AddToSubsetAccumulator completed and tested.

http://source.rd.rcg.local/trac/eagle6/changeset/1317/Eagle6_SVN
Jared Hall hace 12 años
padre
commit
25acf8e7ad

+ 15 - 0
lib/pipeline/Value.js

@@ -1,4 +1,7 @@
 var Value = module.exports = Value = (function(){
+	//DEPENDENCIES
+	var SHA3 = require('sha3');
+
 	// CONSTRUCTOR
 	var klass = function Value(){
 		if(this.constructor == Value) throw new Error("Never create instances of this! Use the static helpers only.");
@@ -127,6 +130,18 @@ var Value = module.exports = Value = (function(){
 		default:
 			throw new Error("unhandled left hand type:" + lt);
 		}
+
+	};
+
+	//Takes a "value" object and returns a hashcode
+	klass.hashCombine = function hashCombine(val){
+		/*
+		SHA3.update(val)
+		return SHA3.digest('hex');
+		*/
+		var sha3 = new SHA3.SHA3Hash(224);
+		sha3.update(JSON.stringify(val));
+		return sha3.digest('hex');
 	};
 
 //TODO:	klass.hashCombine = ...?

+ 30 - 32
lib/pipeline/accumulators/AddToSetAccumulator.js

@@ -1,49 +1,47 @@
-var sha3 = require('sha3'),
-	AddToSetAccumulator = module.exports = (function(){
-
-	// Constructor
-	var klass = module.exports = function AddToSetAccumulator(){
+var AddToSetAccumulator = module.exports = (function(){
+	// CONSTRUCTOR
+	/** Create an expression that finds the sum of n operands. **/
+	var klass = module.exports = function AddToSetAccumulator(/* pCtx */){
+		if(arguments.length !== 0) throw new Error("zero args expected");
 		this.set = {};
+		//this.itr = undefined; /* Shoudln't need an iterator for the set */
+		//this.pCtx = undefined; /* Not using the context object currently as it is related to sharding */
 		base.call(this);
 	}, Accumulator = require("./Accumulator"), base = Accumulator, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 
+	// DEPENDENCIES
+	var Value = require("../Value");
 
-	proto.evaluate = function evaluate(doc){
-		if(this.operands.length != 1) throw new Error("this should never happen");
-		var v = this.operands[0].evaluate(doc);
-		if(v)
-			this.set[this._getHashForObject(v)] = v; 
-	
-		return null;
+	// PROTOTYPE MEMBERS
+	proto.getOpName = function getOpName(){
+		return "$addToSet";
 	};
 
-	proto._getHashForObject = function _getHashForObject(obj){ 
-		// TODO: This uses JSON.stringify which is going to be really slow for any signficant amount of data
-		// Need to figure out a better way to do hasing in Javascript for objects
-		var d = new sha3.SHA3Hash();
-		d.update(JSON.stringify(obj));
-		return d.digest('hex');
+	proto.getFactory = function getFactory(){
+		return klass;	// using the ctor rather than a separate .create() method
 	};
 
-	proto.getValue = function getValue(){
-		var values = [], n = 0;
-		for(var key in this.set){
-			if(this.set.hasOwnProperty(key)) values[n++] = this.set[key];
+	proto.evaluate = function evaluate(doc) {
+		if(arguments.length !== 1) throw new Error("One and only one arg expected");
+		var rhs = this.operands[0].evaluate(doc);
+		if ('undefined' != typeof rhs) {
+			Value.verifyArray(rhs);
+			for(var i=0; i<rhs.length; i++) {
+				var key = Value.hashCombine(rhs[i]);
+				this.set[key] = rhs[i];
+			}
 		}
-		return values;
+		return undefined;
 	};
 
-	proto.getOpName = function getOpName(){
-		return "$addToSet";
+	proto.getValue = function getValue() {
+		var arr = [], n = 0;
+		for(var key in this.set){
+			if(this.set.hasOwnProperty(key)) arr[n++] = this.set[key];
+		}
+		return arr;
 	};
 
 	return klass;
-
 })();
 
-
-
-
-
-
-

+ 41 - 96
test/lib/pipeline/accumulators/AddToSetAccumulator.js

@@ -1,125 +1,70 @@
-
 var assert = require("assert"),
 	AddToSetAccumulator = require("../../../../lib/pipeline/accumulators/AddToSetAccumulator"),
+	ConstantExpression = require("../../../../lib/pipeline/expressions/ConstantExpression"),
 	FieldPathExpression = require("../../../../lib/pipeline/expressions/FieldPathExpression");
 
+var createAccumulator = function createAccumulator() {
+	var myAccumulator = new AddToSetAccumulator();
+	myAccumulator.addOperand(new FieldPathExpression("b") );
+	return myAccumulator;
+};
 
-function createAccumulator(){
-	var accumulator = new AddToSetAccumulator();
-	accumulator.addOperand(new FieldPathExpression("b") );
-	return accumulator;
-}
-
-
+//TODO: refactor these test cases using Expression.parseOperand() or something because these could be a whole lot cleaner...
 module.exports = {
 
 	"AddToSetAccumulator": {
 
 		"constructor()": {
 
-			"should not throw Error when constructing without args": function testConstructor(){
-				assert.doesNotThrow(function(){
-					new AddToSetAccumulator();
-				});
-			}
-
-		},
-
-		"#getOpName()": {
-
-			"should return the correct op name; $addToSet": function testOpName(){
-				assert.strictEqual(new AddToSetAccumulator().getOpName(), "$addToSet");
-			}
-
 		},
 
-		"#evaluate()": {
-
-			"should evaluate no documents": function testStuff(){
-				var accumulator = createAccumulator();
-				accumulator.evaluate();
-				assert.deepEqual(accumulator.getValue(), []);
-			},
-
+		"#getValue()": {
 
-			"should evaluate one document and return an set of 1: [1]": function testStuff(){
-				var accumulator = createAccumulator();
-				accumulator.evaluate({b:1});
-				assert.deepEqual(accumulator.getValue(), [1]);
+			"should return empty array": function testEmptySet() {
+				var acc = new createAccumulator();
+				var value = acc.getValue();
+				assert.equal((value instanceof Array), true);
+				assert.equal(value.length, 0);
 			},
 
-			"should evaluate two documents with the same value and return a set of 1: [1]": function testStuff(){
-
-				var accumulator = createAccumulator();
-				accumulator.evaluate({b:1});
-				accumulator.evaluate({b:1});
-				assert.deepEqual(accumulator.getValue(), [1]);
+			"should return array with one element that equals 5": function test5InSet() {
+				var acc = createAccumulator();
+				acc.evaluate({b:[5]});
+				var value = acc.getValue();
+				assert.equal((value instanceof Array), true);
+				assert.equal(value.length, 1);
+				assert.equal(value[0], 5);
 			},
 
-			"should evaluate two documents with the same objects as values and return a set of 1: [{foo:'bar'}]": function testStuff(){
-
-				var accumulator = createAccumulator();
-				accumulator.evaluate({b:{foo:"bar"}});
-				accumulator.evaluate({b:{foo:"bar"}});
-				assert.deepEqual(accumulator.getValue(), [{foo:"bar"}]);
+			"should produce value that is an array of multiple elements": function testMultipleItems() {
+				var acc = createAccumulator();
+				acc.evaluate({b:[5, {key: "value"}]});
+				var value = acc.getValue();
+				assert.equal((value instanceof Array), true);
+				assert.equal(value.length, 2);
+				assert.equal((value[0] instanceof Object || value[1] instanceof Object) && (typeof value[0] == 'number' || typeof value[1] == 'number'), true);
+				//assert.equal(value[0], 5);
 			},
 
-
-			"should evaluate two documents with the same arrays as values and return a set of 1: [[1,2,3]]": function testStuff(){
-
-				var accumulator = createAccumulator();
-				accumulator.evaluate({b:[1,2,3]});
-				accumulator.evaluate({b:[1,2,3]});
-				assert.deepEqual(accumulator.getValue(), [[1,2,3]]);
-			},
-
-
-			"should evaluate two documents with different values and return a set of 2: [1,2]": function testStuff(){
-
-				var accumulator = createAccumulator();
-				accumulator.evaluate({b:1});
-				accumulator.evaluate({b:2});
-				assert.deepEqual(accumulator.getValue(), [1,2]);
-			},
-
-			"should evaluate two documents with the different objects as values and return a set of 2: [{foo:'bar'},{foo:'baz'}]": function testStuff(){
-
-				var accumulator = createAccumulator();
-				accumulator.evaluate({b:{foo:"bar"}});
-				accumulator.evaluate({b:{foo:"baz"}});
-				assert.deepEqual(accumulator.getValue(), [{foo:"bar"},{foo:"baz"}]);
-			},
-
-
-			"should evaluate two documents with the different arrays as values and return a set of 1: [[1,2,3], [1,2]]": function testStuff(){
-
-				var accumulator = createAccumulator();
-				accumulator.evaluate({b:[1,2,3]});
-				accumulator.evaluate({b:[1,2]});
-				assert.deepEqual(accumulator.getValue(), [[1,2,3], [1,2]]);
+			"should throw an error when given a non-array to evaluate": function testArrayValidity() {
+				assert.throws(function() {
+					var acc = createAccumulator();
+					acc.evaluate({b:5});
+					var value = acc.getValue();
+				});
 			},
 
-
-
-			"should evaluate one document with null and one document with a value and return an set of 1: [1]": function testStuff(){
-				var accumulator = createAccumulator();
-				accumulator.evaluate({b:1});
-				accumulator.evaluate({b:null});
-				assert.deepEqual(accumulator.getValue(), [1]);
+			"should return array with one element that is an object containing a key/value pair": function testKeyValue() {
+				var acc = createAccumulator();
+				acc.evaluate({b:[{key: "value"}]});
+				var value = acc.getValue();
+				assert.equal((value instanceof Object), true);
+				assert.equal(value.length, 1);
+				assert.equal(value[0].key == "value", true);
 			}
 
 		}
-
 	}
-
 };
 
 if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);
-
-
-
-
-
-
-
-