Browse Source

Refs #5126: Update MatchDocumentSource

Chris Sexton 11 years ago
parent
commit
05f472ee91

+ 60 - 37
lib/pipeline/documentSources/MatchDocumentSource.js

@@ -1,8 +1,10 @@
 "use strict";
-var matcher = require("../matcher/Matcher2.js");
+var async = require("async"),
+	matcher = require("../matcher/Matcher2.js"),
+	DocumentSource = require("./DocumentSource");
 
 /**
- * A match document source built off of FilterBaseDocumentSource
+ * A match document source built off of DocumentSource
  *
  * NOTE: THIS IS A DEVIATION FROM THE MONGO IMPLEMENTATION.
  * TODO: internally uses `sift` to fake it, which has bugs, so we need to reimplement this by porting the MongoDB implementation
@@ -20,7 +22,7 @@ var MatchDocumentSource = module.exports = function MatchDocumentSource(query, c
 	base.call(this, ctx);
 	this.query = query; // save the query, so we can check it for deps later. THIS IS A DEVIATION FROM THE MONGO IMPLEMENTATION
 	this.matcher = new matcher(query);
-}, klass = MatchDocumentSource, base = require('./FilterBaseDocumentSource'), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
+}, klass = MatchDocumentSource, base = require('./DocumentSource'), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 
 klass.matchName = "$match";
 
@@ -28,40 +30,6 @@ proto.getSourceName = function getSourceName(){
 	return klass.matchName;
 };
 
-/**
- * Create an object that represents the document source.  The object
- * will have a single field whose name is the source's name.  This
- * will be used by the default implementation of addToJsonArray()
- * to add this object to a pipeline being represented in JSON.
- *
- * @method	sourceToJson
- * @param	{Object} builder	JSONObjBuilder: a blank object builder to write to
- * @param	{Boolean}	explain	create explain output
- **/
-proto.sourceToJson = function sourceToJson(builder, explain) {
-	builder[this.getSourceName()] = this.matcher._pattern;
-};
-
-/**
- *  Test the given document against the predicate and report if it should be accepted or not.
- * @param {object} document the document to test
- * @returns {bool} true if the document matches the filter, false otherwise
- **/
-proto.accept = function accept(document) {
-	/**
-	* The matcher only takes BSON documents, so we have to make one.
-	*
-	* LATER
-	* We could optimize this by making a document with only the
-	* fields referenced by the Matcher.  We could do this by looking inside
-	* the Matcher's BSON before it is created, and recording those.  The
-	* easiest implementation might be to hold onto an ExpressionDocument
-	* in here, and give that pDocument to create the created subset of
-	* fields, and then convert that instead.
-	**/
-	return this.matcher.matches(document);
-};
-
 /**
  * Create a JSONObj suitable for Matcher construction.
  *
@@ -72,6 +40,9 @@ proto.accept = function accept(document) {
  * find() Cursor mechanism.
  *
  * @param builder the builder to write to
+ *
+ *
+ * TO REMOVE
  **/
 proto.toMatcherJson = function toMatcherJson(builder) {
 	var q = this.matcher._pattern;
@@ -100,3 +71,55 @@ klass.createFromJson = function createFromJson(jsonElement, ctx) {
 	var matcher = new MatchDocumentSource(jsonElement, ctx);
 	return matcher;
 };
+
+//
+// TODO:RE-ORDER THIS FILE
+//
+
+proto.getNext = function getNext(callback) {
+	var self = this,
+		next;
+	async.doWhilst(
+		function(cb) {
+			self.source.getNext(function(err, val) {
+				next = val;
+
+				if (self.matcher.matches(next))
+					return cb(next);
+				return cb();
+			});
+		},
+		function() {
+			return next !== DocumentSource.EOF;
+		},
+		function(doc) {
+			if (!doc)
+				return callback(null, DocumentSource.EOF);
+			return callback(null, doc);
+		}
+
+	);
+};
+
+proto.coalesce = function coalesce(nextSource) {
+	if (!(nextSource instanceof MatchDocumentSource))
+		return false;
+
+	this.matcher.reset(new MatchDocumentSource({"$and": [this.getQuery(), nextSource.getQuery()]}));
+
+	return true;
+};
+
+proto.serialize = function(explain) {
+	var out = {};
+	out[this.getSourceName()] = this.getQuery();
+	return out;
+};
+
+proto.getQuery = function getQuery() {
+	return this.matcher.getQuery();
+};
+
+proto.redactSafePortion = function redactSafePortion() {
+	throw new Error("not implemented");
+};

+ 102 - 22
test/lib/pipeline/documentSources/MatchDocumentSource.js

@@ -1,5 +1,7 @@
 "use strict";
 var assert = require("assert"),
+	async = require("async"),
+	DocumentSource = require("../../../../lib/pipeline/documentSources/DocumentSource"),
 	MatchDocumentSource = require("../../../../lib/pipeline/documentSources/MatchDocumentSource");
 
 
@@ -26,44 +28,122 @@ module.exports = {
 
 		},
 
-		"#accept()": {
+		"#serialize()": {
 
-			"should return true on the input document": function acceptTest(){
-				var mds = new MatchDocumentSource({ location : { $in : ['Kentucky'] } }, {});
-				assert.strictEqual(mds.accept({ name: 'Adam', location: 'Kentucky'}), true);
+			"should append the match query to the input builder": function sourceToJsonTest(){
+				var mds = new MatchDocumentSource({ location : { $in : ['Kentucky'] } });
+				var t = mds.serialize(false);
+				assert.deepEqual(t, { "$match" : { location : { $in : ['Kentucky'] } }});
 			}
 
 		},
 
-		"#sourceToJson()": {
+		"#createFromJson()": {
 
-			"should append the match query to the input builder": function sourceToJsonTest(){
-				var mds = new MatchDocumentSource({ location : { $in : ['Kentucky'] } });
-				var t = {};
-				mds.sourceToJson(t, false);
-				assert.deepEqual(t, { "$match" : { location : { $in : ['Kentucky'] } }});
+			"should return a new MatchDocumentSource object from an input object": function createTest(){
+				var t = MatchDocumentSource.createFromJson({ someval:{$exists:true} });
+				assert.strictEqual(t instanceof MatchDocumentSource, true);
 			}
 
 		},
 
-		"#toMatcherJson()": {
+		"#getNext()": {
 
-			"should append the match query to an object suitable for creating a new matcher": function convertTest(){
-				var mds = new MatchDocumentSource({ location : { $in : ['Kentucky'] } });
-				var t = {};
-				mds.toMatcherJson(t);
-				assert.deepEqual(t, { location : { $in : ['Kentucky'] } });
-			}
+			"should return the current document source": function currSource(next){
+				var mds = new MatchDocumentSource({item: 1});
+				mds.source = {getNext:function(cb){cb(null,{ item:1 });}};
+				mds.getNext(function(err,val) {
+					assert.deepEqual(val, { item:1 });
+					next();
+				});
+			},
+
+			"should return matched sources remaining": function (next){
+				var mds = new MatchDocumentSource({ item: {$lt: 5} }),
+					items = [ 1,2,3,4,5,6,7,8,9 ];
+				mds.source = {
+					calls: 0,
+					getNext:function(cb) {
+						if (this.calls >= items.length)
+							return cb(null,DocumentSource.EOF);
+						return cb(null,{item: items[this.calls++]});
+					},
+					dispose:function() { return true; }
+				};
+
+				async.series([
+						mds.getNext.bind(mds),
+						mds.getNext.bind(mds),
+						mds.getNext.bind(mds),
+						mds.getNext.bind(mds),
+						mds.getNext.bind(mds),
+					],
+					function(err,res) {
+						assert.deepEqual([{item:1},{item:2},{item:3},{item:4},DocumentSource.EOF], res);
+						next();
+					}
+				);
+			},
+
+			"should not return matched out documents for sources remaining": function (next){
+				var mds = new MatchDocumentSource({ item: {$gt: 5} }),
+					items = [ 1,2,3,4,5,6,7,8,9 ];
+				mds.source = {
+					calls: 0,
+					getNext:function(cb) {
+						if (this.calls >= items.length)
+							return cb(null,DocumentSource.EOF);
+						return cb(null,{item: items[this.calls++]});
+					},
+					dispose:function() { return true; }
+				};
+
+				async.series([
+						mds.getNext.bind(mds),
+						mds.getNext.bind(mds),
+						mds.getNext.bind(mds),
+						mds.getNext.bind(mds),
+						mds.getNext.bind(mds),
+					],
+					function(err,res) {
+						assert.deepEqual([{item:6},{item:7},{item:8},{item:9},DocumentSource.EOF], res);
+						next();
+					}
+				);
+			},
+
+			"should return EOF for no sources remaining": function (next){
+				var mds = new MatchDocumentSource({ item: {$gt: 5} }),
+					items = [ ];
+				mds.source = {
+					calls: 0,
+					getNext:function(cb) {
+						if (this.calls >= items.length)
+							return cb(null,DocumentSource.EOF);
+						return cb(null,{item: items[this.calls++]});
+					},
+					dispose:function() { return true; }
+				};
+
+				async.series([
+						mds.getNext.bind(mds),
+					],
+					function(err,res) {
+						assert.deepEqual([DocumentSource.EOF], res);
+						next();
+					}
+				);
+			},
 
 		},
 
-		"#createFromJson()": {
+		"#coalesce()": {
+		},
 
-			"should return a new MatchDocumentSource object from an input object": function createTest(){
-				var t = MatchDocumentSource.createFromJson({ someval:{$exists:true} });
-				assert.strictEqual(t instanceof MatchDocumentSource, true);
-			}
+		"#getQuery()": {
+		},
 
+		"#redactSafePortion()": {
 		}
 
 	}