浏览代码

Merge branch 'feature/mongo_2.6.5_matcher_ComparisonMatch' into feature/mongo_2.6.5_matcher_GTE_GT_LTE_LT

Conflicts:
	lib/pipeline/matcher/ComparisonMatchExpression.js
Jared Hall 11 年之前
父节点
当前提交
c6264ce192

+ 37 - 37
lib/pipeline/matcher/ComparisonMatchExpression.js

@@ -1,10 +1,15 @@
 "use strict";
-var LeafMatchExpression = require('./LeafMatchExpression.js');
-var Value = require('../Value');
+var LeafMatchExpression = require("./LeafMatchExpression.js");
+var Value = require("../Value");
 
-
-// File: expression_leaf.h
-var ComparisonMatchExpression = module.exports = function ComparisonMatchExpression( type ){
+/**
+ * ComparisonMatchExpression
+ * @class ComparisonMatchExpression
+ * @namespace mungedb-aggregate.pipeline.matcher
+ * @module mungedb-aggregate
+ * @constructor
+ */
+var ComparisonMatchExpression = module.exports = function ComparisonMatchExpression(type){
 	base.call(this);
 	this._matchType = type;
 }, klass = ComparisonMatchExpression, base =  LeafMatchExpression, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
@@ -20,33 +25,33 @@ proto._rhs = undefined;
  *
  */
 proto.debugString = function debugString(level) {
-	var retStr = this._debugAddSpace( level ) + this.path() + " ";
-	switch (this._matchType){
-		case 'LT':
-			retStr += '$lt';
+	var retStr = this._debugAddSpace(level) + this.path() + " ";
+	switch (this._matchType) {
+		case "LT":
+			retStr += "$lt";
 			break;
-		case 'LTE':
-			retStr += '$lte';
+		case "LTE":
+			retStr += "$lte";
 			break;
-		case 'EQ':
-			retStr += '==';
+		case "EQ":
+			retStr += "==";
 			break;
-		case 'GT':
-			retStr += '$gt';
+		case "GT":
+			retStr += "$gt";
 			break;
-		case 'GTE':
-			retStr += '$gte';
+		case "GTE":
+			retStr += "$gte";
 			break;
 		default:
 			retStr += "Unknown comparison!";
 			break;
 	}
 
-	retStr += (this._rhs ? this._rhs.toString() : '?');
-	if ( this.getTag() ) {
+	retStr += (this._rhs ? this._rhs.toString() : "?");
+	if (this.getTag()) {
 		retStr += this.getTag().debugString();
 	}
-	return retStr + '\n';
+	return retStr + "\n";
 };
 
 /**
@@ -67,7 +72,7 @@ proto.equivalent = function equivalent(other) {
  * @method getData
  *
  */
-proto.getData = function getData(){
+proto.getData = function getData() {
 	return this._rhs;
 };
 
@@ -77,7 +82,7 @@ proto.getData = function getData(){
  * @method getRHS
  *
  */
-proto.getRHS = function getRHS(){
+proto.getRHS = function getRHS() {
 	return this._rhs;
 };
 
@@ -91,13 +96,13 @@ proto.getRHS = function getRHS(){
  */
 proto.init = function init(path,rhs) {
 	this._rhs = rhs;
-	if ( (rhs instanceof Object && Object.keys(rhs).length === 0)) { return {'code':'BAD_VALUE', 'description':'Need a real operand'};}
+	if ((rhs instanceof Object && Object.keys(rhs).length === 0)) return {"code":"BAD_VALUE", "description":"Need a real operand"};
 
-	if ( rhs === undefined ) { return {'code':'BAD_VALUE', 'desc':'Cannot compare to undefined'};}
+	if (rhs === undefined) return {"code":"BAD_VALUE", "desc":"Cannot compare to undefined"};
 	if (!(this._matchType in {"LT":1, "LTE":1, "EQ":1, "GT":1, "GTE":1})) {
-		return {'code':'BAD_VALUE', 'description':'Bad match type for ComparisonMatchExpression'};
+		return {"code":"BAD_VALUE", "description":"Bad match type for ComparisonMatchExpression"};
 	}
-	return this.initPath( path );
+	return this.initPath(path);
 };
 
 /**
@@ -107,24 +112,20 @@ proto.init = function init(path,rhs) {
  * @param e
  *
  */
-proto.matchesSingleElement = function matchesSingleElement(e){
+proto.matchesSingleElement = function matchesSingleElement(e) {
 	if (Value.canonicalize(e) !== Value.canonicalize(this._rhs)) {
-		// some special cases
-		// // jsonNULL and undefined are treated the same
 		if (Value.canonicalize(e) + Value.canonicalize(this._rhs) === 5) {
-			return this._matchType === 'EQ' || this._matchType === 'LTE' || this._matchType === 'GTE';
+			return ["EQ","LTE","GTE"].indexOf(this._matchType) != -1;
 		}
 
-		if (Value.canonicalize(e) === -1 || Value.canonicalize(e) === 127) {
-			return this._matchType !== 'EQ';
+		if (["MaxKey","MinKey"].indexOf(Value.getType(this._rhs)) != -1) {
+			return this._matchType !== "EQ";
 		}
-
-		return false;
 	}
 
-	var x = Value.compare( e, this._rhs );
+	var x = Value.compare(e, this._rhs);
 
-	switch( this._matchType ) {
+	switch(this._matchType) {
 		case "LT":
 			return x < 0;
 		case "LTE":
@@ -140,4 +141,3 @@ proto.matchesSingleElement = function matchesSingleElement(e){
 	}
 	return false;
 };
-

+ 0 - 50
test/lib/pipeline/matcher/ComparisonMatchExpression.js

@@ -1,50 +0,0 @@
-"use strict";
-var assert = require("assert"),
-	ComparisonMatchExpression = require("../../../../lib/pipeline/matcher/ComparisonMatchExpression");
-
-
-module.exports = {
-	"ComparisonMatchExpression": {
-
-		"Should properly initialize with an empty path and a number": function (){
-			var e = new ComparisonMatchExpression();
-			e._matchType = 'LT';
-			assert.strictEqual(e.init('', 5 ).code,'OK');
-		},
-		"Should not initialize when given an undefined rhs": function() {
-			var e = new ComparisonMatchExpression();
-			assert.strictEqual(e.init('',5).code,'BAD_VALUE');
-			e._matchType = 'LT';
-			assert.strictEqual(e.init('',{}).code,'BAD_VALUE');	
-			assert.strictEqual(e.init('',undefined).code,'BAD_VALUE');
-			assert.strictEqual(e.init('',{}).code,'BAD_VALUE');
-		},
-		"Should match numbers with GTE": function (){
-			var e = new ComparisonMatchExpression();
-			e._matchType = 'GTE';
-			assert.strictEqual(e.init('',5).code,'OK');
-			assert.ok(e.matchesSingleElement(6), "6 ≥ 5");
-			assert.ok(e.matchesSingleElement(5), "5 ≥ 5");
-			assert.ok(!e.matchesSingleElement(4), "4 ≥ 5");
-			assert.ok(!e.matchesSingleElement('foo'), "5 ≥ 'foo'");
-		},
-		"Should match with simple paths and GTE": function(){
-			var e = new ComparisonMatchExpression();
-			e._matchType = 'GTE';
-			assert.strictEqual(e.init('a', 5).code,'OK');
-			assert.ok(e.matches({'a':6}));
-		},
-		"Should match arrays with GTE": function (){
-			var e = new ComparisonMatchExpression();
-			e._matchType = 'GTE';
-			assert.strictEqual(e.init('a',5).code,'OK');
-			assert.ok(e.matches({'a':[6,10]}),'[6,10] ≥ 5');
-			assert.ok(e.matches({'a':[4,5.5]}), '[4,5.5] ≥ 5');
-			assert.ok(!e.matches({'a':[1,2]}),'[1,2] ≥ 5');
-			assert.ok(e.matches({'a':[1,10]}),'[1,10] ≥ 5');
-		}
-	}
-};
-
-if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);
-

+ 110 - 0
test/lib/pipeline/matcher/ComparisonMatchExpression_test.js

@@ -0,0 +1,110 @@
+"use strict";
+var assert = require("assert"),
+	bson = require("bson"),
+	MinKey = bson.BSONPure.MinKey,
+	MaxKey = bson.BSONPure.MaxKey,
+	MatchDetails = require("../../../../lib/pipeline/matcher/MatchDetails"),
+	ComparisonMatchExpression = require("../../../../lib/pipeline/matcher/ComparisonMatchExpression");
+
+// 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));
+
+exports.ComparisonMatchExpression = {
+
+	"should properly initialize with an empty path and a number": function () {
+		var e = new ComparisonMatchExpression('LT');
+		assert.strictEqual(e.init('',5).code,'OK');
+	},
+	"should not initialize when given an invalid operand": function() {
+		var e = new ComparisonMatchExpression('');
+		assert.strictEqual(e.init('',5).code, 'BAD_VALUE');
+	},
+	"should not initialize when given an undefined rhs": function() {
+		var e = new ComparisonMatchExpression();
+		assert.strictEqual(e.init('',5).code,'BAD_VALUE');
+		e._matchType = 'LT';
+		assert.strictEqual(e.init('',{}).code,'BAD_VALUE');
+		assert.strictEqual(e.init('',undefined).code,'BAD_VALUE');
+		assert.strictEqual(e.init('',{}).code,'BAD_VALUE');
+	},
+	"should match numbers with GTE": function () {
+		var e = new ComparisonMatchExpression('GTE');
+		assert.strictEqual(e.init('',5).code,'OK');
+		assert.ok(e.matchesSingleElement(6),'6 ≥ 5');
+		assert.ok(e.matchesSingleElement(5),'5 ≥ 5');
+		assert.ok(!e.matchesSingleElement(4),'4 !≥ 5');
+		assert.ok(!e.matchesSingleElement('foo'),"'foo' !≥ 5");
+	},
+	"should match with simple paths and GTE": function() {
+		var e = new ComparisonMatchExpression('GTE');
+		assert.strictEqual(e.init('a',5).code,'OK');
+		assert.ok(e.matches({'a':6}));
+	},
+	"should match array values with GTE": function () {
+		var e = new ComparisonMatchExpression('GTE');
+		assert.strictEqual(e.init('a',5).code,'OK');
+		assert.ok(e.matches({'a':[6,10]}),'[6,10] ≥ 5');
+		assert.ok(e.matches({'a':[4,5.5]}),'[4,5.5] ≥ 5');
+		assert.ok(!e.matches({'a':[1,2]}),'[1,2] !≥ 5');
+		assert.ok(e.matches({'a':[1,10]}),'[1,10] ≥ 5');
+	},
+	"should match entire arrays with GTE": function() {
+		var e = new ComparisonMatchExpression('GTE');
+		assert.strictEqual(e.init('a',[5]).code,'OK');
+		assert.ok(!e.matches({'a':[4]}),'[4] !≥ [5]');
+		assert.ok(e.matches({'a':[5]}),'[5] !≥ [5]');
+		assert.ok(e.matches({'a':[6]}),'[6] !≥ [5]');
+		// documents current behavior
+		assert.ok(e.matches({'a':[[6]]}),'[[4]] ≥ [5]');
+		assert.ok(e.matches({'a':[[6]]}),'[[5]] ≥ [5]');
+		assert.ok(e.matches({'a':[[6]]}),'[[6]] ≥ [5]');
+	},
+	"should match null with GTE": function() {
+		var e = new ComparisonMatchExpression('GTE');
+		e._matchType = 'GTE';
+		assert.strictEqual(e.init('a',null).code,'OK');
+		assert.ok(e.matches({}),'{} ≥ null');
+		assert.ok(e.matches({'a':null}),'null ≥ null');
+		assert.ok(!e.matches({'a':4}),'4 !≥ null');
+		assert.ok(e.matches({'b':null}),'non-existent field ≥ null');
+	},
+	"should match null in dotted paths with GTE": function() {
+		var e = new ComparisonMatchExpression('GTE');
+		assert.strictEqual(e.init('a.b',null).code,'OK');
+		assert.ok(e.matches({}),'{} ≥ null');
+		assert.ok(e.matches({'a':null}),'{a:null} ≥ {a.b:null}');
+		assert.ok(e.matches({'a':4}),'{a:4} ≥ {a.b:null}');
+		assert.ok(e.matches({'a':{}}),'{a:{}} ≥ {a.b:null}');
+		assert.ok(e.matches({'a':[{'b':null}]}),'{a:[{b:null}]} ≥ {a.b:null}');
+		assert.ok(e.matches({'a':[{'a':4},{'b':4}]}),'{a:[{a:4},{b:4}]} ≥ {a.b:null}');
+		assert.ok(!e.matches({'a':[4]}),'{a:[4]} !≥ {a.b:null}');
+		assert.ok(!e.matches({'a':[{'b':4}]}),'{a:[{b:4}]} !≥ {a.b:null}');
+	},
+	"should match MinKeys": function() {
+		var e = new ComparisonMatchExpression('GTE');
+		assert.strictEqual(e.init('a',new MinKey()).code,'OK');
+		assert.ok(e.matches({'a':new MinKey()}),'minKey ≥ minKey');
+		assert.ok(e.matches({'a':new MaxKey()}),'maxKey ≥ minKey');
+		assert.ok(e.matches({'a':4}),'4 ≥ minKey');
+	},
+	"should match MaxKeys": function() {
+		var e = new ComparisonMatchExpression('GTE');
+		assert.strictEqual(e.init('a',new MaxKey()).code,'OK');
+		assert.ok(e.matches({'a':new MaxKey()}),'maxKey ≥ maxKey');
+		assert.ok(!e.matches({'a':new MinKey()}),'minKey !≥ maxKey');
+		assert.ok(!e.matches({'a':4},null),'4 !≥ maxKey');
+	},
+	"should properly set match keys": function() {
+		var e = new ComparisonMatchExpression('GTE'),
+			d = new MatchDetails();
+		d.requestElemMatchKey();
+		assert.strictEqual(e.init('a',5).code,'OK');
+		assert.ok(!e.matchesJSON({'a':4},d),'4 !≥ 5');
+		assert(!d.hasElemMatchKey());
+		assert.ok(e.matchesJSON({'a':6},d),'6 ≥ 5');
+		assert(!d.hasElemMatchKey());
+		assert.ok(e.matchesJSON({'a':[2,6,5]},d),'[2,6,5] ≥ 5');
+		assert(d.hasElemMatchKey());
+		assert.strictEqual('1',d.elemMatchKey());
+	}
+};