Przeglądaj źródła

Merge branch 'feature/mongo_2.6.5_matcher' into feature/mongo_2.6.5_matcher_RegexMatch

Phil Murray 11 lat temu
rodzic
commit
2d45659757

+ 23 - 40
lib/pipeline/matcher/ComparisonMatchExpression.js

@@ -2,15 +2,19 @@
 var LeafMatchExpression = require('./LeafMatchExpression.js');
 var Value = require('../Value');
 
-
-// Autogenerated by cport.py on 2013-09-17 14:37
-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}});
 
 
-// File: expression_leaf.h lines: 88-88
 proto._rhs = undefined;
 
 /**
@@ -21,9 +25,8 @@ proto._rhs = undefined;
  *
  */
 proto.debugString = function debugString(level) {
-	// File: expression_leaf.cpp lines: 135-154
-	var retStr = this._debugAddSpace( level ) + this.path() + " ";
-	switch (this._matchType){
+	var retStr = this._debugAddSpace(level) + this.path() + " ";
+	switch (this._matchType) {
 		case 'LT':
 			retStr += '$lt';
 			break;
@@ -45,7 +48,7 @@ proto.debugString = function debugString(level) {
 	}
 
 	retStr += (this._rhs ? this._rhs.toString() : '?');
-	if ( this.getTag() ) {
+	if (this.getTag()) {
 		retStr += this.getTag().debugString();
 	}
 	return retStr + '\n';
@@ -59,7 +62,6 @@ proto.debugString = function debugString(level) {
  *
  */
 proto.equivalent = function equivalent(other) {
-	// File: expression_leaf.cpp lines: 53-61
 	if (other._matchType != this._matchType)  return false;
 	return this.path() === other.path() && Value.compare(this._rhs,other._rhs) === 0;
 };
@@ -70,8 +72,7 @@ proto.equivalent = function equivalent(other) {
  * @method getData
  *
  */
-proto.getData = function getData(){
-	// File: expression_leaf.h lines: 85-84
+proto.getData = function getData() {
 	return this._rhs;
 };
 
@@ -81,8 +82,7 @@ proto.getData = function getData(){
  * @method getRHS
  *
  */
-proto.getRHS = function getRHS(){
-	// File: expression_leaf.h lines: 79-78
+proto.getRHS = function getRHS() {
 	return this._rhs;
 };
 
@@ -95,15 +95,14 @@ proto.getRHS = function getRHS(){
  *
  */
 proto.init = function init(path,rhs) {
-	// File: expression_leaf.cpp lines: 65-87
 	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 this.initPath( path );
+	return this.initPath(path);
 };
 
 /**
@@ -113,36 +112,21 @@ proto.init = function init(path,rhs) {
  * @param e
  *
  */
-proto.matchesSingleElement = function matchesSingleElement(e){
-	// File: expression_leaf.cpp lines: 91-132
-	if( typeof(e) != typeof(this._rhs) ){
-		if( this._rhs === null) {
-			if(this._matchType in {'EQ':1, 'LTE':1, 'GTE':1}){
-				if(e === undefined || e === null || (e instanceof Object && Object.keys(e).length === 0)) {
-					return true;
-				}
-			}
-			return false;
-		}
-		if((e === null || e === undefined) && (this._rhs ===null || this._rhs === undefined)) {
+proto.matchesSingleElement = function matchesSingleElement(e) {
+	if (Value.canonicalize(e) !== Value.canonicalize(this._rhs)) {
+		if (Value.canonicalize(e) + Value.canonicalize(this._rhs) === 5) {
 			return ["EQ","LTE","GTE"].indexOf(this._matchType) != -1;
 		}
 
-		if (this._rhs.constructor.name in {'MaxKey':1,'MinKey':1} ) {
-			return this._matchType != "EQ";
+		if (['MaxKey','MinKey'].indexOf(Value.getType(this._rhs)) != -1) {
+			return this._matchType !== "EQ";
 		}
 		return false;
 	}
 
-	if( this._rhs instanceof Array) {
-		if( 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 == -1;
 		case "LTE":
@@ -158,4 +142,3 @@ proto.matchesSingleElement = function matchesSingleElement(e){
 	}
 	return false;
 };
-

+ 0 - 73
lib/pipeline/matcher/Context.js

@@ -1,73 +0,0 @@
-"use strict";
-
-// Autogenerated by cport.py on 2013-09-17 14:37
-var Context = module.exports = function (){
-
-}, klass = Context, base = Object, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
-
-// File: path.h lines: 62-62
-proto._arrayOffset = undefined;
-
-// File: path.h lines: 61-61
-proto._element = undefined;
-
-// File: path.h lines: 63-63
-proto._outerArray = undefined;
-
-/**
- *
- * Return the _arrayOffset property
- * @method arrayOffset
- *
- */
-proto.arrayOffset = function arrayOffset( ){
-	// File: path.h lines: 57-56
-	return this._arrayOffset;
-};
-
-/**
- *
- * Return the _element property
- * @method element
- *
- */
-proto.element = function element( ){
-	// File: path.h lines: 56-55
-	return this._element;
-};
-
-/**
- *
- * Return the _outerArray property
- * @method outerArray
- *
- */
-proto.outerArray = function outerArray( ){
-	// File: path.h lines: 58-57
-	return this._outerArray;
-};
-
-/**
- *
- * Set _element to a new empty object
- * @method reset
- *
- */
-proto.reset = function reset(){
-	// File: path.cpp lines: 37-38
-	this._element = {};
-};
-
-/**
- *
- * Set _arrayOffset property to the input element
- * @method setArrayOffset
- * @param e
- *
- */
-proto.setArrayOffset = function setArrayOffset(e){
-	// File: path.h lines: 54-53
-	this._arrayOffset = e;
-};
-
-

+ 7 - 3
lib/pipeline/matcher/ElementPath.js

@@ -158,8 +158,7 @@ proto._matches = function _matches(doc, details, checker) {
  * @param function checker this function is used to check for a valid item at the end of the path
  *
  */
-klass._matches = function _matches(doc, path, shouldTraverseLeafArray, details, checker) {
-	// File: expression_array.cpp lines: 34-53
+klass._matches = function _matches(doc, path, shouldTraverseLeafArray, details, checker){
 	var k, result, ii, il,
 		curr = doc,
 		item = doc;
@@ -205,7 +204,7 @@ klass._matches = function _matches(doc, path, shouldTraverseLeafArray, details,
 			// otherwise, check each item in the array against the rest of the path
 			for (ii = 0, il = item.length; ii < il; ii++) {
 				var subitem = item[ii];
-				if (subitem.constructor !== Object) continue; // can't look for a subfield in a non-object value.
+				if (!subitem || subitem.constructor !== Object) continue;	// can't look for a subfield in a non-object value.
 				if (this._matches(subitem, path.slice(k), shouldTraverseLeafArray, null, checker)) { // check the item against the rest of the path
 					if (details && details.needRecord())
 						details.setElemMatchKey(ii.toString());
@@ -213,6 +212,11 @@ klass._matches = function _matches(doc, path, shouldTraverseLeafArray, details,
 				}
 			}
 			return false; // checked all items in the array and found no matches
+		} else {
+
+			if ( details === undefined && item !== null && curr[path[k+1]] !== undefined) {
+				curr = curr[path[k+1]];
+			}
 		}
 	}
 	return checker(item);

+ 5 - 4
lib/pipeline/matcher/EqualityMatchExpression.js

@@ -4,8 +4,7 @@ var ComparisonMatchExpression = require('./ComparisonMatchExpression');
 
 // Autogenerated by cport.py on 2013-09-17 14:37
 var EqualityMatchExpression = module.exports = function EqualityMatchExpression(){
-	base.call(this);
-	this._matchType = 'EQ';
+	base.call(this,'EQ');
 }, klass = EqualityMatchExpression, base =  ComparisonMatchExpression, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 
 /**
@@ -16,9 +15,11 @@ var EqualityMatchExpression = module.exports = function EqualityMatchExpression(
  *
  */
 proto.shallowClone = function shallowClone( /*  */ ){
-// File: expression_leaf.h lines: 98-101
 	var e = new EqualityMatchExpression();
 	e.init ( this.path(), this._rhs );
+	
+	if ( this.getTag() ) {
+		e.setTag(this.getTag().clone());
+	}
 	return e;
 };
-

+ 0 - 58
lib/pipeline/matcher/IndexKeyMatchableDocument.js

@@ -1,58 +0,0 @@
-"use strict";
-
-// Autogenerated by cport.py on 2013-09-17 14:37
-var IndexKeyMatchableDocument = module.exports = function IndexKeyMatchableDocument(pattern, doc){
-	this._pattern = pattern;
-	this._doc = doc;
-}, klass = IndexKeyMatchableDocument, base =  Object  , proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
-
-// File: matcher.cpp lines: 52-52
-proto._doc = undefined;
-
-// File: matcher.cpp lines: 51-51
-proto._pattern = undefined;
-
-// File: matcher.cpp lines: 52-52
-proto._doc = undefined;
-
-// File: matcher.cpp lines: 51-51
-proto._pattern = undefined;
-
-/**
- *
- * get the element at the input path
- * @method _getElement
- * @param path
- *
- */
-proto._getElement = function _getElement(path){
-	// File: matcher.cpp lines: 63-77
-	var patternElement, docElement;
-
-	for (var i in this._pattern) {
-		patternElement = this._pattern[i];
-		//verify( docIterator.more() );
-		if(i >= this._doc.length) throw new Error("Ran out of docs in IndexKeyMatchableDocument:35");
-		docElement = this._doc[i];
-
-		if (path.equalsDottedField(patternElement.fieldName())) {
-			return docElement;
-		}
-	}
-
-	return {};
-};
-
-/**
- *
- * This method returns a JSON representation of the Document
- * @method toBSON
- * @param
- *
- */
-proto.toJSON = function toJSON(){
-	// File: matcher.cpp lines: 39-42
-	// TODO: this isn't quite correct because of dots
-	// don't think it'll ever be called though
-	return this._doc.replaceFieldNames(this._pattern);
-};

+ 0 - 55
lib/pipeline/matcher/TagData.js

@@ -1,55 +0,0 @@
-"use strict";
-
-
-
-// Autogenerated by cport.py on 2013-09-17 14:37
-var TagData = module.exports = function TagData (){
-
-}, klass = TagData, base =  Object  , proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
-
-
-
-
-
-/**
- * debugString pure virtual.
- *
- * @method debugString
- *
- */
-proto.debugString = function debugString() {
-	throw new Error('Virtual function called.');
-};
-
-// Below this line is manually generated based on inferred info in the source.
-
-/**
- * resets the tag data.
- *
- *@method reset
- */
-proto.reset = function reset( data ) {
-	this._data = data;
-};
-
-/**
- * Sets the data tag
- * @method set
- * @param data
- */
-proto.set = function set( data ) {
-	this._data = data;
-};
-
-
-
-/**
- * Gets the data tag
- *
- * @method get
- *
- */
-proto.get = function get(){
-	return this._data;
-};
-

+ 48 - 24
test/lib/pipeline/matcher/EqualityMatchExpression.js

@@ -1,31 +1,16 @@
 "use strict";
 var assert = require("assert"),
 	MatchDetails = require('../../../../lib/pipeline/matcher/MatchDetails'),
-	EqualityMatchExpression = require("../../../../lib/pipeline/matcher/EqualityMatchExpression");
+	EqualityMatchExpression = require("../../../../lib/pipeline/matcher/EqualityMatchExpression"),
+	// TODO: replace the following with a real BSONTypes at some point
+	MinKey = new (function MinKey(){/*matcher does weird stuff with empty objects*/this.foo = 'bar';})(), // jshint ignore:line
+	MaxKey = new (function MaxKey(){/*matcher does weird stuff with empty objects*/this.foo = 'bar';})(); // jshint ignore:line
 
 
 module.exports = {
 
 	"EqualityMatchExpression": {
 
-		"should initialize equality and match numbers or numbers in arrays": function (){
-			var e = new EqualityMatchExpression();
-			var s = e.init('x', 5);
-			assert.strictEqual(s.code, 'OK');
-
-			assert.ok(e.matches({x:5}));
-			assert.ok(e.matches({x:[5]}));
-			assert.ok(e.matches({x:[1,5]}));
-			assert.ok(e.matches({x:[1,5,2]}));
-			assert.ok(e.matches({x:[5,2]}));
-
-			assert.ok(!e.matches({x:null}));
-			assert.ok(!e.matches({x:6}));
-			assert.ok(!e.matches({x:[4,2]}));
-			assert.ok(!e.matches({x:[[5]]}));
-		},
-
-//NOTE: from expression_leaf_test.cpp
 		"should match elements": function testMatchesElement(){
 			var operand = {a:5},
 				match = {a:5.0},
@@ -44,7 +29,7 @@ module.exports = {
 			var e = new EqualityMatchExpression();
 			var s = e.init('',{});
 
-			assert.strictEqual(s.code, 'BAD_VALUE');
+			assert.ok(s.code !== 'OK');
 		},
 		"should match a pathed number":function() {
 			var e = new EqualityMatchExpression();
@@ -82,12 +67,53 @@ module.exports = {
 		"should match null" : function() {
 			var e = new EqualityMatchExpression();
 			var s = e.init('a',null);
-		
+
 			assert.strictEqual(s.code, 'OK');
 			assert.ok( e.matches({}) );
 			assert.ok( e.matches({'a':null}) );
 			assert.ok( ! e.matches({'a':4}) );
+			assert.ok( e.matches({'b':4}) );
+		},
+    //// This test documents how the matcher currently works,
+    //// not necessarily how it should work ideally.
+		"should match nested nulls" : function(){
+			var e = new EqualityMatchExpression();
+			var s = e.init('a.b',null);
+
+			assert.strictEqual(s.code, 'OK');
+			// // null matches any empty object that is on a subpath of a.b
+			assert.ok( e.matches({}) );
+			assert.ok( e.matches({'a':{}}) );
+			assert.ok( e.matches({'a':[{}]}) );
+
+			assert.ok( e.matches({'a':{'b':null}} ) );
+			// b does not exist as an element in array under a.
+			assert.ok( !e.matches({'a':[]}) );
+			assert.ok( !e.matches({'a':[null]}) );
+			assert.ok( !e.matches({'a':[1,2]}) );
+			// a.b exists but is not null.
+			assert.ok( !e.matches({'a':{'b':4}} ) );
+			assert.ok( !e.matches({'a':{'b':{}}} ) );
+			// A non-existent field is treated same way as an empty bson object
+			assert.ok( e.matches({'b':4} ) );
+
+			assert.ok( e.matches({"b":"stuff"}) );
+		},
+		"should match MinKey" : function(){
+			var e = new EqualityMatchExpression();
+			var s = e.init('a',MinKey);
+			assert.ok( e.matches({'a': MinKey}) );
+			assert.ok( !e.matches({'a':MaxKey}) );
+			assert.ok( !e.matches({'a':4}) );
+		},
+		"should match MaxKey" : function(){
+			var e = new EqualityMatchExpression();
+			var s = e.init('a',MaxKey);
+			assert.ok( e.matches({'a':MaxKey}) );
+			assert.ok( !e.matches({'a': MinKey}) );
+			assert.ok( !e.matches({'a':4}) );
 		},
+
 		"should match full array" : function() {
 			var e = new EqualityMatchExpression();
 			var s = e.init('a',[1,2]);
@@ -128,7 +154,7 @@ module.exports = {
 			var a = new EqualityMatchExpression();
 			var b = new EqualityMatchExpression();
 			var c = new EqualityMatchExpression();
-			
+
 
 			assert.strictEqual( a.init('a',5).code, 'OK' );
 			assert.strictEqual( b.init('a',5).code, 'OK' );
@@ -139,10 +165,8 @@ module.exports = {
 			assert.ok( ! a.equivalent(c) );
 		}
 
-
 	}
 
 };
 
 if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);
-

+ 1 - 1
test/lib/pipeline/matcher/GTEMatchExpression.js

@@ -43,7 +43,7 @@ module.exports = {
 			var s = e.init('a',[5]);
 
 			assert.strictEqual(s.code, 'OK');
-			assert.ok( ! e.matches({'a':[6]}) );
+			assert.ok( e.matches({'a':[6]}) );
 		},
 		"should not match null" : function() {
 			var e = new GTEMatchExpression();

+ 1 - 1
test/lib/pipeline/matcher/GTMatchExpression.js

@@ -43,7 +43,7 @@ module.exports = {
 			var s = e.init('a',[5]);
 
 			assert.strictEqual(s.code, 'OK');
-			assert.ok( ! e.matches({'a':[6]}) );
+			assert.ok( e.matches({'a':[6]}) );
 		},
 		"should not match null" : function() {
 			var e = new GTMatchExpression();

+ 1 - 1
test/lib/pipeline/matcher/LTEMatchExpression.js

@@ -43,7 +43,7 @@ module.exports = {
 			var s = e.init('a',[5]);
 
 			assert.strictEqual(s.code, 'OK');
-			assert.ok( ! e.matches({'a':[4]}) );
+			assert.ok(e.matches({'a':[4]}) );
 		},
 		"should not match null" : function() {
 			var e = new LTEMatchExpression();

+ 1 - 1
test/lib/pipeline/matcher/LTMatchExpression.js

@@ -51,7 +51,7 @@ module.exports = {
 			var s = e.init('a',[5]);
 
 			assert.strictEqual(s.code, 'OK');
-			assert.ok( ! e.matches({'a':[4]}) );
+			assert.ok( e.matches({'a':[4]}) );
 		},
 		"should not match null" : function() {
 			var e = new LTMatchExpression();