Browse Source

Refs #3045: Update AddToSetAccumulator js

Mike McCarty 11 years ago
parent
commit
0e8e0cda9b

+ 29 - 39
lib/pipeline/accumulators/Accumulator.js

@@ -10,26 +10,26 @@
  **/
 var Accumulator = module.exports = function Accumulator(){
 	if (arguments.length !== 0) throw new Error("zero args expected");
+	this._memUsageBytes = 0;
 	base.call(this);
-}, klass = Accumulator, base = require("../expressions/NaryExpression"), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
+}, klass = Accumulator, base = Object, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 
 // DEPENDENCIES
 // var Value = require("../Value"),
 
+proto.memUsageForSorter = function memUsageForSorter() {
+	return this._memUsageBytes;
+};
+
 proto.getFactory = function getFactory(){
 	return klass;	// using the ctor rather than a separate .create() method
 };
 
-/**
- * Adds the operand after checking the current limit
- * The equal is there because it checks *before* adding the requested argument.
- * Cannot use checkArgLimit because Accumulator must return a different error code.
- *
- * @param expr the operand to add
- **/
-proto.addOperand = function addOperand(expr) {
-	if (this.operands.length >= 1) throw new Error("code 15943; group accumulator " + this.getOpName() + " only accepts one operand");
-	base.prototype.addOperand.call(this, expr);
+/** Process input and update internal state.
+ *  merging should be true when processing outputs from getValue(true).
+ */
+proto.process = function process(input, merging){
+    this.processInternal(input, merging);
 };
 
 proto.toJSON = function toJSON(isExpressionRequired){
@@ -38,41 +38,31 @@ proto.toJSON = function toJSON(isExpressionRequired){
 	return rep;
 };
 
-/**
- * Convenience method for doing this for accumulators.  The pattern
- * is always the same, so a common implementation works, but requires
- * knowing the operator name.
+/** 
+ * If this function is not overridden in the sub classes,
+ * then throw an error
  *
- * @param {Object} pBuilder the builder to add to
- * @param {String} fieldName the projected name
- * @param {String} opName the operator name
- * @param {Boolean} requireExpression pass down if the expression is needed
  **/
-//	proto.opToBson = function opToBson(pBuilder, opName, fieldName, requireExpression) {
-//		if (this.operands.length == 1) throw new Error("this should never happen");
-//		var builder = new BSONObjBuilder();
-//		this.operands[0].addToBsonObj(builder, opName, requireExpression);
-//		pBuilder.append(fieldName, builder.done());
-//	};
+proto.getOpName = function getOpName() {
+	throw new Error("You need to define this function on your accumulator");
+};
 
-/**
- * Wrapper around opToBson
+/** 
+ * If this function is not overridden in the sub classes,
+ * then throw an error
  *
- * @param {Object} pBuilder the builder to add to
- * @param {String} fieldName the projected name
- * @param {Boolean} requireExpression pass down if the expression is needed
  **/
-//	proto.addToBsonObj = function addToBsonObj(pBuilder, fieldName, requireExpression) {
-//		this.opToBson(pBuilder, this.getOpName(), fieldName, requireExpression);
-//	};
+proto.getValue = function getValue(toBeMerged) {
+	throw new Error("You need to define this function on your accumulator");
+};
 
-/**
- * Make sure that nobody adds an accumulator to an array
+/** 
+ * If this function is not overridden in the sub classes,
+ * then throw an error
  *
- * @param {Object} pBuilder the builder to add to
  **/
-proto.addToBsonArray = function addToBsonArray(pBuilder) {
-	if (false) throw new Error("this should never happen"); // these can't appear in arrays
+proto.processInternal = function processInternal(input, merging) {
+	throw new Error("You need to define this function on your accumulator");
 };
 
 /** 
@@ -80,6 +70,6 @@ proto.addToBsonArray = function addToBsonArray(pBuilder) {
  * then throw an error
  *
  **/
-proto.getValue = function getValue() {
+proto.reset = function reset() {
 	throw new Error("You need to define this function on your accumulator");
 };

+ 15 - 16
lib/pipeline/accumulators/AddToSetAccumulator.js

@@ -15,26 +15,25 @@ var AddToSetAccumulator = module.exports = function AddToSetAccumulator(/* ctx *
 	base.call(this);
 }, klass = AddToSetAccumulator, Accumulator = require("./Accumulator"), base = Accumulator, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 
-// PROTOTYPE MEMBERS
-proto.getOpName = function getOpName(){
-	return "$addToSet";
+proto.processInternal = function processInternal(doc, merging) {
+	var set = this.set;
+	Object.keys(doc).map(function(d) {
+		set[JSON.stringify(doc[d])] = doc[d];
+	});
 };
 
-proto.getFactory = function getFactory(){
-	return klass;	// using the ctor rather than a separate .create() method
+proto.getValue = function getValue(toBeMerged) {
+	var set = this.set;
+	return Object.keys(set).map(function(k) {
+		return set[k];
+	});
 };
 
-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 (rhs === undefined) return;
-	this.set[JSON.stringify(rhs)] = rhs;
+proto.reset = function reset() {
+    this.set = {};
 };
 
-proto.getValue = function getValue() {
-	var setValues = [];
-	for (var setKey in this.set) {
-		setValues.push(this.set[setKey]);
-	}
-	return setValues;
+// PROTOTYPE MEMBERS
+proto.getOpName = function getOpName(){
+	return "$addToSet";
 };

+ 18 - 28
test/lib/pipeline/accumulators/AddToSetAccumulator.js

@@ -6,9 +6,7 @@ var assert = require("assert"),
 
 
 var createAccumulator = function createAccumulator() {
-	var myAccumulator = new AddToSetAccumulator();
-	myAccumulator.addOperand(new FieldPathExpression("b"));
-	return myAccumulator;
+	return new AddToSetAccumulator();
 };
 
 
@@ -33,20 +31,12 @@ module.exports = {
 
 		},
 
-		"#evaluate()" : {
-
-			"should error if evaluate is called with no args": function testNoArgs() {
-				assert.throws(function() {
-					var acc = new createAccumulator();
-					acc.evaluate();
-				});
-			},
-
-			"should error if evaluate is called with more than one arg": function testTooManyArgs() {
-				assert.throws(function() {
-					var acc = new createAccumulator();
-					acc.evaluate({}, {});
-				});
+		"#processInternal()" : {
+			"should add input to set": function testAddsToSet() {
+				var acc = createAccumulator();
+				acc.processInternal({b:5});
+				var value = acc.getValue();
+				assert.deepEqual(JSON.stringify(value), JSON.stringify([5]));
 			}
 
 		},
@@ -62,39 +52,39 @@ module.exports = {
 
 			"should return array with one element that equals 5": function test5InSet() {
 				var acc = createAccumulator();
-				acc.evaluate({b:5});
-				acc.evaluate({b:5});
+				acc.processInternal({b:5});
+				acc.processInternal({b:5});
 				var value = acc.getValue();
 				assert.deepEqual(JSON.stringify(value), JSON.stringify([5]));
 			},
 
 			"should produce value that is an array of multiple elements": function testMultipleItems() {
 				var acc = createAccumulator();
-				acc.evaluate({b:5});
-				acc.evaluate({b:{key: "value"}});
+				acc.processInternal({b:5});
+				acc.processInternal({b:{key: "value"}});
 				var value = acc.getValue();
 				assert.deepEqual(JSON.stringify(value), JSON.stringify([5, {key: "value"}]));
 			},
 
 			"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"}});
+				acc.processInternal({b:{key: "value"}});
 				var value = acc.getValue();
 				assert.deepEqual(JSON.stringify(value), JSON.stringify([{key: "value"}]));
 			},
 
-			"should ignore undefined values": function testKeyValue() {
+			"should not require defining values": function testKeyValue() {
 				var acc = createAccumulator();
-				acc.evaluate({b:{key: "value"}});
-				acc.evaluate({a:5});
+				acc.processInternal({b:{key: "value"}});
+				acc.processInternal({a:5});
 				var value = acc.getValue();
-				assert.deepEqual(JSON.stringify(value), JSON.stringify([{key: "value"}]));
+				assert.deepEqual(JSON.stringify(value), JSON.stringify([5, {key: "value"}]));
 			},
 
 			"should coalesce different instances of equivalent objects": function testGetValue_() {
 				var acc = createAccumulator();
-				acc.evaluate({b:{key: "value"}});
-				acc.evaluate({b:{key: "value"}});
+				acc.processInternal({b:{key: "value"}});
+				acc.processInternal({b:{key: "value"}});
 				var value = acc.getValue();
 				assert.deepEqual(JSON.stringify(value), JSON.stringify([{key: "value"}]));
 			}