Browse Source

Merge branch 'feature/mongo_2.6.5_documentSource_SortDocumentSource' into feature/mongo_2.6.5_documentSource_Pipeline

Jason Walton 11 years ago
parent
commit
99642c37db

+ 28 - 35
lib/pipeline/documentSources/SortDocumentSource.js

@@ -2,7 +2,8 @@
 
 
 var async = require("async"),
 var async = require("async"),
 	DocumentSource = require("./DocumentSource"),
 	DocumentSource = require("./DocumentSource"),
-	LimitDocumentSource = require("./LimitDocumentSource");
+	LimitDocumentSource = require("./LimitDocumentSource"),
+	Document = require('../Document');
 
 
 /**
 /**
  * A document source sorter
  * A document source sorter
@@ -49,10 +50,6 @@ proto.getFactory = function getFactory(){
 	return klass;	// using the ctor rather than a separate .create() method
 	return klass;	// using the ctor rather than a separate .create() method
 };
 };
 
 
-klass.GetDepsReturn = {
-	SEE_NEXT: "SEE_NEXT" // Add the next Source's deps to the set
-};
-
 proto.dispose = function dispose() {
 proto.dispose = function dispose() {
 	this.docIterator = 0;
 	this.docIterator = 0;
 	this.documents = [];
 	this.documents = [];
@@ -68,7 +65,7 @@ proto.getDependencies = function getDependencies(deps) {
 	for(var i = 0; i < this.vSortKey.length; ++i) {
 	for(var i = 0; i < this.vSortKey.length; ++i) {
 		this.vSortKey[i].addDependencies(deps);
 		this.vSortKey[i].addDependencies(deps);
 	}
 	}
-	return klass.GetDepsReturn.SEE_NEXT;
+	return DocumentSource.GetDepsReturn.SEE_NEXT;
 };
 };
 
 
 proto.coalesce = function coalesce(nextSource) {
 proto.coalesce = function coalesce(nextSource) {
@@ -95,22 +92,24 @@ proto.getNext = function getNext(callback) {
 		[
 		[
 			function(next) {
 			function(next) {
 				if (!self.populated)
 				if (!self.populated)
+				{
 					self.populate(function(err) {
 					self.populate(function(err) {
 						return next(err);
 						return next(err);
 					});
 					});
-				else
-					next();
+				} else {
+					return next();
+				}
 			},
 			},
 			function(next) {
 			function(next) {
 				if (self.docIterator >= self.documents.length) {
 				if (self.docIterator >= self.documents.length) {
-					out = DocumentSource.EOF;
-					return next(null, DocumentSource.EOF);
+					out = null;
+					return next(null, null);
 				}
 				}
 
 
 				var output = self.documents[self.docIterator++];
 				var output = self.documents[self.docIterator++];
-				if (!output || output === DocumentSource.EOF) {
-					out = DocumentSource.EOF;
-					return next(null, DocumentSource.EOF);
+				if (!output || output === null) {
+					out = null;
+					return next(null, null);
 				}
 				}
 
 
 				out = output;
 				out = output;
@@ -139,10 +138,12 @@ proto.serializeToArray = function serializeToArray(array, explain) {
 		doc.limit = this.limitSrc ? this.limitSrc.getLimit() : undefined;
 		doc.limit = this.limitSrc ? this.limitSrc.getLimit() : undefined;
 		array.push(doc);
 		array.push(doc);
 	} else { // one Value for $sort and maybe a Value for $limit
 	} else { // one Value for $sort and maybe a Value for $limit
-		var inner = this.serializeSortKey(explain);
+		var inner = {};
+		inner = this.serializeSortKey(explain);
 		if (this._mergePresorted)
 		if (this._mergePresorted)
 			inner.$mergePresorted = true;
 			inner.$mergePresorted = true;
 		doc[this.getSourceName()] = inner;
 		doc[this.getSourceName()] = inner;
+
 		array.push(doc);
 		array.push(doc);
 
 
 		if (this.limitSrc)
 		if (this.limitSrc)
@@ -194,7 +195,7 @@ proto.makeSortOptions = function makeSortOptions(){
 proto.populate = function populate(callback) {
 proto.populate = function populate(callback) {
 	if ( this._mergePresorted ){
 	if ( this._mergePresorted ){
 		// Skipping stuff about mergeCursors and commandShards
 		// Skipping stuff about mergeCursors and commandShards
-
+console.log('never here');
 
 
 		if ( this.source instanceof MergeCursorDocumentSouce ){
 		if ( this.source instanceof MergeCursorDocumentSouce ){
 			populateFromCursors( this.source);
 			populateFromCursors( this.source);
@@ -215,19 +216,20 @@ proto.populate = function populate(callback) {
 					next = doc;
 					next = doc;
 
 
 					// Don't add EOF; it doesn't sort well.
 					// Don't add EOF; it doesn't sort well.
-					if (doc !== DocumentSource.EOF)
+					if (doc !== null)
 						self.documents.push(doc);
 						self.documents.push(doc);
+
 					return cb();
 					return cb();
 				});
 				});
 			},
 			},
 			function() {
 			function() {
-				return next !== DocumentSource.EOF;
+				return next !== null;
 			},
 			},
 			function(err) {
 			function(err) {
-		/* sort the list */
+				/* sort the list */
 				self.documents.sort(SortDocumentSource.prototype.compare.bind(self));
 				self.documents.sort(SortDocumentSource.prototype.compare.bind(self));
 
 
-		/* start the sort iterator */
+				/* start the sort iterator */
 				self.docIterator = 0;
 				self.docIterator = 0;
 
 
 				self.populated = true;
 				self.populated = true;
@@ -265,7 +267,7 @@ klass.IteratorFromCursor = (function(){
 })();
 })();
 
 
 proto.populateFromCursors = function populateFromCursors(cursors){
 proto.populateFromCursors = function populateFromCursors(cursors){
-	for (var i = 0; i < cursors.lenth; i++) {
+	for (var i = 0; i < cursors.length; i++) {
 		// TODO Create class
 		// TODO Create class
 		//this.iterators.push(boost::make_shared<IteratorFromBsonArray>(this, cursors[i]));
 		//this.iterators.push(boost::make_shared<IteratorFromBsonArray>(this, cursors[i]));
 	}
 	}
@@ -338,19 +340,11 @@ proto.compare = function compare(lhs,rhs) {
 	  However, the tricky part is what to do is none of the sort keys are
 	  However, the tricky part is what to do is none of the sort keys are
 	  present.  In this case, consider the document less.
 	  present.  In this case, consider the document less.
 	*/
 	*/
-	var n = this.vSortKey.length;
-	if ( n == 1) { // simple fast case
-		if ( this.vAscending[0] ) {
-			return Value.compare(lhs, rhs);
-		} else {
-			return -Value.compare(lhs, rhs);
-		}
-	}
 
 
-	// compound sort
-	for(var i = 0; i < n; ++i) {
+	for(var i = 0, n = this.vSortKey.length; i < n; ++i) {
+		var pathExpr = FieldPathExpression.create(this.vSortKey[i].getFieldPath(false).fieldNames.slice(1).join('.'));
+
 		/* evaluate the sort keys */
 		/* evaluate the sort keys */
-		var pathExpr = FieldPathExpression.create(this.vSortKey[i].getFieldPath(false));
 		var left = pathExpr.evaluate(lhs), right = pathExpr.evaluate(rhs);
 		var left = pathExpr.evaluate(lhs), right = pathExpr.evaluate(rhs);
 
 
 		/*
 		/*
@@ -384,13 +378,12 @@ proto.serializeSortKey = function serializeSortKey(explain) {
 	var n = this.vSortKey.length;
 	var n = this.vSortKey.length;
 	for (var i = 0; i < n; i++) {
 	for (var i = 0; i < n; i++) {
 		if ( this.vSortKey[i] instanceof FieldPathExpression ) {
 		if ( this.vSortKey[i] instanceof FieldPathExpression ) {
-			var fieldPath = this.vSortKey[i].getFieldPath();
-
+			var fieldPath = this.vSortKey[i].getFieldPath(false).fieldNames.slice(1).join('.');
 			// append a named integer based on the sort order
 			// append a named integer based on the sort order
 			keyObj[fieldPath] = this.vAscending[i] ? 1 : -1;
 			keyObj[fieldPath] = this.vAscending[i] ? 1 : -1;
 		} else {
 		} else {
 			// other expressions use a made-up field name
 			// other expressions use a made-up field name
-			keyObj[{"$computed":i}] = this.vSortKey[i].serializeToArray(explain);
+			keyObj[{"$computed":i}] = this.vSortKey[i].serialize(explain);
 		}
 		}
 	}
 	}
 	return keyObj;
 	return keyObj;
@@ -406,7 +399,6 @@ proto.serializeSortKey = function serializeSortKey(explain) {
 klass.createFromJson = function createFromJson(elem, expCtx) {
 klass.createFromJson = function createFromJson(elem, expCtx) {
 	if (typeof elem !== "object") throw new Error("code 15973; the " + klass.sortName + " key specification must be an object");
 	if (typeof elem !== "object") throw new Error("code 15973; the " + klass.sortName + " key specification must be an object");
 
 
-	
 	return this.create(expCtx, elem);
 	return this.create(expCtx, elem);
 };
 };
 
 
@@ -477,6 +469,7 @@ proto.getDependencies = function getDependencies(deps) {
 	for(var i = 0; i < this.vSortKey.length; i++) {
 	for(var i = 0; i < this.vSortKey.length; i++) {
 		this.vSortKey[i].addDependencies(deps);
 		this.vSortKey[i].addDependencies(deps);
 	}
 	}
+
 	return DocumentSource.GetDepsReturn.SEE_NEXT;
 	return DocumentSource.GetDepsReturn.SEE_NEXT;
 };
 };
 
 

+ 356 - 289
test/lib/pipeline/documentSources/SortDocumentSource.js

@@ -5,108 +5,192 @@ var assert = require("assert"),
 	SortDocumentSource = require("../../../../lib/pipeline/documentSources/SortDocumentSource"),
 	SortDocumentSource = require("../../../../lib/pipeline/documentSources/SortDocumentSource"),
 	LimitDocumentSource = require("../../../../lib/pipeline/documentSources/LimitDocumentSource"),
 	LimitDocumentSource = require("../../../../lib/pipeline/documentSources/LimitDocumentSource"),
 	CursorDocumentSource = require("../../../../lib/pipeline/documentSources/CursorDocumentSource"),
 	CursorDocumentSource = require("../../../../lib/pipeline/documentSources/CursorDocumentSource"),
-	Cursor = require("../../../../lib/Cursor"),
+	ArrayRunner = require("../../../../lib/query/ArrayRunner"),
 	FieldPathExpression = require("../../../../lib/pipeline/expressions/FieldPathExpression");
 	FieldPathExpression = require("../../../../lib/pipeline/expressions/FieldPathExpression");
 
 
+var getCursorDocumentSource = function(values) {
+	return new CursorDocumentSource(null, new ArrayRunner(values), null);
+};
+
+
+/// An assertion for `ObjectExpression` instances based on Mongo's `ExpectedResultBase` class
+function assertExpectedResult(args) {
+	{// check for required args
+		if (args === undefined) throw new TypeError("missing arg: `args` is required");
+		if (args.spec && args.throw === undefined) args.throw = true; // Assume that spec only tests expect an error to be thrown
+		//if (args.spec === undefined) throw new Error("missing arg: `args.spec` is required");
+		if (args.expected !== undefined && args.docs === undefined) throw new Error("must provide docs with expected value");
+	}// check for required args
+
+	// run implementation
+	if(args.expected && args.docs){
+		var sds = SortDocumentSource.createFromJson(args.spec),
+			next,
+			results = [],
+			cds = new CursorDocumentSource(null, new ArrayRunner(args.docs), null);
+			debugger;
+		sds.setSource(cds);
+		async.whilst(
+			function() {
+				return next !== null;
+			},
+			function(done) {
+				sds.getNext(function(err, doc) {
+					if(err) return done(err);
+					next = doc;
+					if(next === null) {
+						return done();
+					} else {
+						results.push(next);
+						return done();
+					}
+				});
+			},
+			function(err) {
+				assert.equal(JSON.stringify(results), JSON.stringify(args.expected));
+				if(args.done) {
+					return args.done();
+				}
+			}
+		);
+	}else{
+		if(args.throw) {
+			assert.throws(function(){
+				SortDocumentSource.createFromJson(args.spec);
+			});
+		} else {
+			assert.doesNotThrow(function(){
+				var gds = SortDocumentSource.createFromJson(args.spec);
+			});
+		}
+	}
+}
 
 
 module.exports = {
 module.exports = {
 
 
 	"SortDocumentSource": {
 	"SortDocumentSource": {
 
 
-		// "constructor()": {
+		"constructor()": {
 
 
-		// 	"should not throw Error when constructing without args": function testConstructor(){
-		// 		assert.doesNotThrow(function(){
-		// 			new SortDocumentSource();
-		// 		});
-		// 	}
+			// $sort spec is not an object
+			"should throw Error when constructing without args": function testConstructor(){
+				assertExpectedResult({"throw":true});
+			},
 
 
-		// },
+			// $sort spec is not an object
+			"should throw Error when $sort spec is not an object": function testConstructor(){
+				assertExpectedResult({spec:"Foo"});
+			},
 
 
-		// "#getSourceName()": {
+			// $sort spec is an empty object
+			"should throw Error when $sort spec is an empty object": function testConstructor(){
+				assertExpectedResult({spec:{}});
+			},
 
 
-		// 	"should return the correct source name; $sort": function testSourceName(){
-		// 		var sds = new SortDocumentSource();
-		// 		assert.strictEqual(sds.getSourceName(), "$sort");
-		// 	}
 
 
-		// },
+			// $sort _id is specified as an invalid object expression
+			"should throw error when _id is an invalid object expression": function testConstructor(){
+				assertExpectedResult({
+					spec:{_id:{$add:1, $and:1}},
+				});
+			},
 
 
-		// "#getFactory()": {
+		},
 
 
-		// 	"should return the constructor for this class": function factoryIsConstructor(){
-		// 		assert.strictEqual(new SortDocumentSource().getFactory(), SortDocumentSource);
-		// 	}
+		"#getSourceName()": {
+
+			"should return the correct source name; $sort": function testSourceName(){
+				var sds = new SortDocumentSource();
+				assert.strictEqual(sds.getSourceName(), "$sort");
+			}
+
+		},
 
 
-		// },
+		"#getFactory()": {
+
+			"should return the constructor for this class": function factoryIsConstructor(){
+				assert.strictEqual(new SortDocumentSource().getFactory(), SortDocumentSource);
+			}
+
+		},
 
 
 		"#getNext()": {
 		"#getNext()": {
 			/** Assert that iterator state accessors consistently report the source is exhausted. */
 			/** Assert that iterator state accessors consistently report the source is exhausted. */
 			"should return EOF if there are no more sources": function noSources(next){
 			"should return EOF if there are no more sources": function noSources(next){
-				var cwc = new CursorDocumentSource.CursorWithContext();
-				cwc._cursor = new Cursor( [{"a": 1}] );
-				var cds = new CursorDocumentSource(cwc);
+				var cds = getCursorDocumentSource([{"a": 1}]);
 				var sds = SortDocumentSource.createFromJson({"sort":1});
 				var sds = SortDocumentSource.createFromJson({"sort":1});
 				sds.setSource(cds);
 				sds.setSource(cds);
 				sds.getNext(function(err, val) {
 				sds.getNext(function(err, val) {
 					assert.deepEqual(val, {a:1});
 					assert.deepEqual(val, {a:1});
 					sds.getNext(function(err, val) {
 					sds.getNext(function(err, val) {
-						assert.equal(val, DocumentSource.EOF);
+						if (err) throw err;
+						assert.equal(val, null);
 						return next();
 						return next();
 					});
 					});
 				});
 				});
+
 			},
 			},
 
 
-			"should return EOF if there are more documents": function hitSort(next){
-				var cwc = new CursorDocumentSource.CursorWithContext();
-				cwc._cursor = new Cursor( [{a: 1}] );
-				var cds = new CursorDocumentSource(cwc);
+			"should not return EOF if there are documents": function hitSort(next){
+				var cds = getCursorDocumentSource([{a: 1}]);
 				var sds = SortDocumentSource.createFromJson({"sort":1});
 				var sds = SortDocumentSource.createFromJson({"sort":1});
 				sds.setSource(cds);
 				sds.setSource(cds);
-				sds.getNext(function(err, doc) {
-					assert.notEqual(doc, DocumentSource.EOF);
-					next();
-				});
+				async.series([
+						cds.getNext.bind(cds),
+					],
+					function(err,res) {
+						if (err) throw err;
+						assert.notEqual(res, null);
+						return next();
+					}
+				);
 			},
 			},
 
 
 			"should return the current document source": function currSource(next){
 			"should return the current document source": function currSource(next){
-				var cwc = new CursorDocumentSource.CursorWithContext();
-				cwc._cursor = new Cursor( [{a: 1}] );
-				var cds = new CursorDocumentSource(cwc);
+				var cds = getCursorDocumentSource([{a: 1}]);
 				var sds = SortDocumentSource.createFromJson({"sort":1});
 				var sds = SortDocumentSource.createFromJson({"sort":1});
 				sds.setSource(cds);
 				sds.setSource(cds);
-				sds.getNext(function(err, doc) {
-					assert.deepEqual(doc, { a:1 });
-					next();
-				});
+				async.series([
+						cds.getNext.bind(cds),
+					],
+					function(err,res) {
+						if (err) throw err;
+						assert.deepEqual(res, [ { a: 1 } ]);
+						return next();
+					}
+				);
 			},
 			},
 
 
 			"should return next document when moving to the next source sorted descending": function nextSource(next){
 			"should return next document when moving to the next source sorted descending": function nextSource(next){
-				var cwc = new CursorDocumentSource.CursorWithContext();
-				cwc._cursor = new Cursor( [{a: 1}, {b:2}] );
-				var cds = new CursorDocumentSource(cwc);
-				var sds = SortDocumentSource.createFromJson({"sort":-1});
+				var cds = getCursorDocumentSource([{a: 1}, {b:2}]);
+				var sds = SortDocumentSource.createFromJson({"sort":1});
 				sds.setSource(cds);
 				sds.setSource(cds);
-				sds.getNext(function(err, doc) {
-					assert.deepEqual(doc, {b:2});
-					return next();
-				});
+				async.series([
+						cds.getNext.bind(cds),
+					],
+					function(err,res) {
+						if (err) throw err;
+						assert.deepEqual(res, [ { a: 1 } ]);
+						return next();
+					}
+				);
 			},
 			},
 
 
 			"should return false for no sources remaining sorted descending": function noMoar(next){
 			"should return false for no sources remaining sorted descending": function noMoar(next){
-				var cwc = new CursorDocumentSource.CursorWithContext();
-				cwc._cursor = new Cursor( [{a: 1}, {b:2}] );
-				var cds = new CursorDocumentSource(cwc);
-				var sds = SortDocumentSource.createFromJson({"sort":-1});
+				var cds = getCursorDocumentSource([{a: 1}, {b:2}]);
+				var sds = SortDocumentSource.createFromJson({"sort":1});
 				sds.setSource(cds);
 				sds.setSource(cds);
-				sds.getNext(function(err, doc) {
-					sds.getNext(function(err, doc) {
-						assert.deepEqual(doc, {a:1});
-						next();
-					});
-				});
+				async.series([
+						cds.getNext.bind(cds),
+						cds.getNext.bind(cds),
+					],
+					function(err,res) {
+						if (err) throw err;
+						assert.deepEqual(res,  [ { a: 1 }, { b: 2 } ]);
+						return next();
+					}
+				);
 			}
 			}
-
 		},
 		},
 
 
 		"#serialize()": {
 		"#serialize()": {
@@ -115,7 +199,6 @@ module.exports = {
 				var sds = new SortDocumentSource();
 				var sds = new SortDocumentSource();
 				assert.throws(sds.serialize.bind(sds));
 				assert.throws(sds.serialize.bind(sds));
 			}
 			}
-
 		},
 		},
 
 
 		"#serializeToArray()": {
 		"#serializeToArray()": {
@@ -124,98 +207,100 @@ module.exports = {
             * Check that the BSON representation generated by the souce matches the BSON it was
             * Check that the BSON representation generated by the souce matches the BSON it was
             * created with.
             * created with.
             */
             */
-            "should have equal json representation": function serializeToArrayCheck(){
+            "should have equal json representation": function serializeToArrayCheck(next){
 				var sds = SortDocumentSource.createFromJson({"sort":1}, {});
 				var sds = SortDocumentSource.createFromJson({"sort":1}, {});
-
 				var array = [];
 				var array = [];
 				sds.serializeToArray(array, false);
 				sds.serializeToArray(array, false);
-
-				assert.deepEqual(array, [ { '$sort': { '[object Object]': 1 } } ]);
+				assert.deepEqual(array, [{"$sort":{"sort":1}}]);
+				return next();
 			},
 			},
 
 
-			"should create an object representation of the SortDocumentSource": function serializeToArrayTest(){
+			"should create an object representation of the SortDocumentSource": function serializeToArrayTest(next){
 				var sds = new SortDocumentSource();
 				var sds = new SortDocumentSource();
 				var fieldPathVar;
 				var fieldPathVar;
 				sds.vSortKey.push(new FieldPathExpression("b", fieldPathVar) );
 				sds.vSortKey.push(new FieldPathExpression("b", fieldPathVar) );
 				var array = [];
 				var array = [];
 				sds.serializeToArray(array, false);
 				sds.serializeToArray(array, false);
-				assert.deepEqual(array,  [{"$sort":{"[object Object]":-1}}] );
+				assert.deepEqual(array, [{"$sort":{"":-1}}] );
+				return next();
 			}
 			}
 
 
 		},
 		},
 
 
 		"#createFromJson()": {
 		"#createFromJson()": {
 
 
-			"should return a new SortDocumentSource object from an input JSON object": function createTest(){
+			"should return a new SortDocumentSource object from an input JSON object": function createTest(next){
 				var sds = SortDocumentSource.createFromJson({a:1});
 				var sds = SortDocumentSource.createFromJson({a:1});
 				assert.strictEqual(sds.constructor, SortDocumentSource);
 				assert.strictEqual(sds.constructor, SortDocumentSource);
 				var t = [];
 				var t = [];
 				sds.serializeToArray(t, false);
 				sds.serializeToArray(t, false);
-				assert.deepEqual(t, [{"$sort":{"[object Object]":1}}]);
+				assert.deepEqual(t, [{"$sort":{"a":1}}] );
+				return next();
 			},
 			},
 
 
-			"should return a new SortDocumentSource object from an input JSON object with a descending field": function createTest(){
+			"should return a new SortDocumentSource object from an input JSON object with a descending field": function createTest(next){
 				var sds = SortDocumentSource.createFromJson({a:-1});
 				var sds = SortDocumentSource.createFromJson({a:-1});
 				assert.strictEqual(sds.constructor, SortDocumentSource);
 				assert.strictEqual(sds.constructor, SortDocumentSource);
 				var t = [];
 				var t = [];
 				sds.serializeToArray(t, false);
 				sds.serializeToArray(t, false);
-				assert.deepEqual(t, [{ "$sort": { "[object Object]": -1 } }]);
+				assert.deepEqual(t,  [{"$sort":{"a":-1}}]);
+				return next();
 			},
 			},
 
 
-			"should return a new SortDocumentSource object from an input JSON object with dotted paths": function createTest(){
+			"should return a new SortDocumentSource object from an input JSON object with dotted paths": function createTest(next){
 				var sds = SortDocumentSource.createFromJson({ "a.b":1 });
 				var sds = SortDocumentSource.createFromJson({ "a.b":1 });
 				assert.strictEqual(sds.constructor, SortDocumentSource);
 				assert.strictEqual(sds.constructor, SortDocumentSource);
 				var t = [];
 				var t = [];
 				sds.serializeToArray(t, false);
 				sds.serializeToArray(t, false);
-				assert.deepEqual(t, [{ "$sort": { "[object Object]" : 1  } }]);
+				assert.deepEqual(t, [{"$sort":{"a.b":1}}]);
+				return next();
 			},
 			},
 
 
-			"should throw an exception when not passed an object": function createTest(){
+			"should throw an exception when not passed an object": function createTest(next){
 				assert.throws(function() {
 				assert.throws(function() {
 					var sds = SortDocumentSource.createFromJson(7);
 					var sds = SortDocumentSource.createFromJson(7);
 				});
 				});
+				return next();
 			},
 			},
 
 
-			"should throw an exception when passed an empty object": function createTest(){
+			"should throw an exception when passed an empty object": function createTest(next){
 				assert.throws(function() {
 				assert.throws(function() {
 					var sds = SortDocumentSource.createFromJson({});
 					var sds = SortDocumentSource.createFromJson({});
 				});
 				});
+				return next();
 			},
 			},
 
 
-			"should throw an exception when passed an object with a non number value": function createTest(){
+			"should throw an exception when passed an object with a non number value": function createTest(next){
 				assert.throws(function() {
 				assert.throws(function() {
 					var sds = SortDocumentSource.createFromJson({a:"b"});
 					var sds = SortDocumentSource.createFromJson({a:"b"});
 				});
 				});
+				return next();
 			},
 			},
 
 
-			"should throw an exception when passed an object with a non valid number value": function createTest(){
+			"should throw an exception when passed an object with a non valid number value": function createTest(next){
 				assert.throws(function() {
 				assert.throws(function() {
 					var sds = SortDocumentSource.createFromJson({a:14});
 					var sds = SortDocumentSource.createFromJson({a:14});
 				});
 				});
+				next();
 			}
 			}
-
 		},
 		},
 
 
 		"#sort": {
 		"#sort": {
 
 
 			"should sort a single document": function singleValue(next) {
 			"should sort a single document": function singleValue(next) {
-				var cwc = new CursorDocumentSource.CursorWithContext();
-				cwc._cursor = new Cursor( [{_id:0, a: 1}] );
-				var cds = new CursorDocumentSource(cwc);
+				var cds = getCursorDocumentSource([{_id:0, a: 1}]);
 				var sds = new SortDocumentSource();
 				var sds = new SortDocumentSource();
 				sds.addKey("_id", false);
 				sds.addKey("_id", false);
 				sds.setSource(cds);
 				sds.setSource(cds);
 				sds.getNext(function(err, actual) {
 				sds.getNext(function(err, actual) {
+					if (err) throw err;
 					assert.deepEqual(actual, {_id:0, a:1});
 					assert.deepEqual(actual, {_id:0, a:1});
-					next();
+					return next();
 				});
 				});
 			},
 			},
 
 
 			"should sort two documents": function twoValue(next) {
 			"should sort two documents": function twoValue(next) {
-				var cwc = new CursorDocumentSource.CursorWithContext();
-				var l = [{_id:0, a: 1}, {_id:1, a:0}];
-				cwc._cursor = new Cursor( l );
-				var cds = new CursorDocumentSource(cwc);
+				var cds = getCursorDocumentSource([{_id:0, a: 1}, {_id:1, a:0}]);
 				var sds = new SortDocumentSource();
 				var sds = new SortDocumentSource();
 				sds.addKey("_id", false);
 				sds.addKey("_id", false);
 				sds.setSource(cds);
 				sds.setSource(cds);
@@ -225,17 +310,15 @@ module.exports = {
 						sds.getNext.bind(sds),
 						sds.getNext.bind(sds),
 					],
 					],
 					function(err,res) {
 					function(err,res) {
-						assert.deepEqual([{_id:1, a: 0}, {_id:0, a:1}], res);
-						next();
+						if (err) throw err;
+						assert.deepEqual([ { _id: 1, a: 0 }, { _id: 0, a: 1 } ], res);
+						return next();
 					}
 					}
 				);
 				);
 			},
 			},
 
 
 			"should sort two documents in ascending order": function ascendingValue(next) {
 			"should sort two documents in ascending order": function ascendingValue(next) {
-				var cwc = new CursorDocumentSource.CursorWithContext();
-				var l = [{_id:0, a: 1}, {_id:5, a:12}, {_id:1, a:0}];
-				cwc._cursor = new Cursor( l );
-				var cds = new CursorDocumentSource(cwc);
+				var cds = getCursorDocumentSource([{_id:0, a: 1}, {_id:5, a:12}, {_id:1, a:0}]);
 				var sds = new SortDocumentSource();
 				var sds = new SortDocumentSource();
 				sds.addKey("_id", true);
 				sds.addKey("_id", true);
 				sds.setSource(cds);
 				sds.setSource(cds);
@@ -249,20 +332,18 @@ module.exports = {
 						});
 						});
 					},
 					},
 					function() {
 					function() {
-						return docs[i++] !== DocumentSource.EOF;
+						return docs[i++] !== null;
 					},
 					},
 					function(err) {
 					function(err) {
-						assert.deepEqual([{_id:0, a: 1}, {_id:1, a:0}, {_id:5, a:12}, DocumentSource.EOF], docs);
-						next();
+						if (err) throw err;
+						assert.deepEqual([{_id:0, a: 1}, {_id:1, a:0}, {_id:5, a:12}, null], docs);
+						return next();
 					}
 					}
 				);
 				);
 			},
 			},
 
 
 			"should sort documents with a compound key": function compoundKeySort(next) {
 			"should sort documents with a compound key": function compoundKeySort(next) {
-				var cwc = new CursorDocumentSource.CursorWithContext();
-				var l = [{_id:0, a: 1, b:3}, {_id:5, a:12, b:7}, {_id:1, a:0, b:2}];
-				cwc._cursor = new Cursor( l );
-				var cds = new CursorDocumentSource(cwc);
+				var cds = getCursorDocumentSource([{_id:0, a: 1, b:3}, {_id:5, a:12, b:7}, {_id:1, a:0, b:2}]);
 				var sds = SortDocumentSource.createFromJson({"sort":1});
 				var sds = SortDocumentSource.createFromJson({"sort":1});
 
 
 				sds.addKey("a", false);
 				sds.addKey("a", false);
@@ -278,140 +359,128 @@ module.exports = {
 						});
 						});
 					},
 					},
 					function() {
 					function() {
-						return docs[i++] !== DocumentSource.EOF;
+						return docs[i++] !== null;
+					},
+					function(err) {
+						if (err) throw err;
+						assert.deepEqual([{_id:5, a:12, b:7}, {_id:0, a:1, b:3}, {_id:1, a:0, b:2}, null], docs);
+						return next();
+					}
+				);
+			},
+
+			"should sort documents with a compound key in ascending order": function compoundAscendingKeySort(next) {
+				var cds = getCursorDocumentSource([{_id:0, a: 1, b:3}, {_id:5, a:12, b:7}, {_id:1, a:0, b:2}]);
+				var sds = new SortDocumentSource();
+				sds.addKey("a", true);
+				sds.addKey("b", true);
+				sds.setSource(cds);
+
+				var docs = [], i = 0;
+				async.doWhilst(
+					function(cb) {
+						sds.getNext(function(err, val) {
+							docs[i] = val;
+							return cb(err);
+						});
+					},
+					function() {
+						return docs[i++] !== null;
+					},
+					function(err) {
+						if (err) throw err;
+						assert.deepEqual([{_id:1, a:0, b:2}, {_id:0, a:1, b:3}, {_id:5, a:12, b:7}, null], docs);
+						return next();
+					}
+				);
+			},
+
+			"should sort documents with a compound key in mixed order": function compoundMixedKeySort(next) {
+				var cds = getCursorDocumentSource([{_id:0, a: 1, b:3}, {_id:5, a:12, b:7}, {_id:1, a:0, b:2}, {_id:8, a:7, b:42}]);
+				var sds = new SortDocumentSource();
+				sds.addKey("a", true);
+				sds.addKey("b", false);
+				sds.setSource(cds);
+
+				var docs = [], i = 0;
+				async.doWhilst(
+					function(cb) {
+						sds.getNext(function(err, val) {
+							docs[i] = val;
+							return cb(err);
+						});
+					},
+					function() {
+						return docs[i++] !== null;
 					},
 					},
 					function(err) {
 					function(err) {
-						assert.deepEqual([{_id:5, a:12, b:7}, {_id:0, a:1, b:3}, {_id:1, a:0, b:2}, DocumentSource.EOF], docs);
-						next();
+						if (err) throw err;
+						assert.deepEqual([{_id:1, a:0, b:2}, {_id:0, a:1, b:3}, {_id:8, a:7, b:42}, {_id:5, a:12, b:7}, null], docs);
+						return next();
 					}
 					}
 				);
 				);
 			},
 			},
 
 
-		// 	"should sort documents with a compound key in ascending order": function compoundAscendingKeySort(next) {
-		// 		var cwc = new CursorDocumentSource.CursorWithContext();
-		// 		var l = [{_id:0, a: 1, b:3}, {_id:5, a:12, b:7}, {_id:1, a:0, b:2}];
-		// 		cwc._cursor = new Cursor( l );
-		// 		var cds = new CursorDocumentSource(cwc);
-		// 		var sds = new SortDocumentSource();
-		// 		sds.addKey("a", true);
-		// 		sds.addKey("b", true);
-		// 		sds.setSource(cds);
-
-		// 		var docs = [], i = 0;
-		// 		async.doWhilst(
-		// 			function(cb) {
-		// 				sds.getNext(function(err, val) {
-		// 					docs[i] = val;
-		// 					return cb(err);
-		// 				});
-		// 			},
-		// 			function() {
-		// 				return docs[i++] !== DocumentSource.EOF;
-		// 			},
-		// 			function(err) {
-		// 				assert.deepEqual([{_id:1, a:0, b:2}, {_id:0, a:1, b:3}, {_id:5, a:12, b:7}, DocumentSource.EOF], docs);
-		// 				next();
-		// 			}
-		// 		);
-		// 	},
-
-		// 	"should sort documents with a compound key in mixed order": function compoundMixedKeySort(next) {
-		// 		var cwc = new CursorDocumentSource.CursorWithContext();
-		// 		var l = [{_id:0, a: 1, b:3}, {_id:5, a:12, b:7}, {_id:1, a:0, b:2}, {_id:8, a:7, b:42}];
-		// 		cwc._cursor = new Cursor( l );
-		// 		var cds = new CursorDocumentSource(cwc);
-		// 		var sds = new SortDocumentSource();
-		// 		sds.addKey("a", true);
-		// 		sds.addKey("b", false);
-		// 		sds.setSource(cds);
-
-		// 		var docs = [], i = 0;
-		// 		async.doWhilst(
-		// 			function(cb) {
-		// 				sds.getNext(function(err, val) {
-		// 					docs[i] = val;
-		// 					return cb(err);
-		// 				});
-		// 			},
-		// 			function() {
-		// 				return docs[i++] !== DocumentSource.EOF;
-		// 			},
-		// 			function(err) {
-		// 				assert.deepEqual([{_id:1, a:0, b:2}, {_id:0, a:1, b:3}, {_id:8, a:7, b:42}, {_id:5, a:12, b:7}, DocumentSource.EOF], docs);
-		// 				next();
-		// 			}
-		// 		);
-		// 	},
-
-			"should not sort different types": function diffTypesSort() {
-				var cwc = new CursorDocumentSource.CursorWithContext();
-				var l = [{_id:0, a: 1}, {_id:1, a:"foo"}];
-				cwc._cursor = new Cursor( l );
-				var cds = new CursorDocumentSource(cwc);
+			"should not sort different types": function diffTypesSort(next) {
+				var cds = getCursorDocumentSource([{_id:0, a: 1}, {_id:1, a:"foo"}]);
 				var sds = new SortDocumentSource();
 				var sds = new SortDocumentSource();
 				sds.addKey("a", false);
 				sds.addKey("a", false);
 				assert.throws(sds.setSource(cds));
 				assert.throws(sds.setSource(cds));
+				return next();
+			},
+
+			"should sort docs with missing fields": function missingFields(next) {
+				var cds = getCursorDocumentSource([{_id:0, a: 1}, {_id:1}]);
+				var sds = new SortDocumentSource();
+				sds.addKey("a", true);
+				sds.setSource(cds);
+
+				var docs = [], i = 0;
+				async.doWhilst(
+					function(cb) {
+						sds.getNext(function(err, val) {
+							docs[i] = val;
+							return cb(err);
+						});
+					},
+					function() {
+						return docs[i++] !== null;
+					},
+					function(err) {
+						if (err) throw err;
+						assert.deepEqual([{_id:1}, {_id:0, a:1}, null], docs);
+						return next();
+					}
+				);
+			},
+
+			"should sort docs with null fields": function nullFields(next) {
+				var cds = getCursorDocumentSource([{_id:0, a: 1}, {_id:1, a: null}]);
+				var sds = new SortDocumentSource();
+				sds.addKey("a", true);
+				sds.setSource(cds);
+
+				var docs = [], i = 0;
+				async.doWhilst(
+					function(cb) {
+						sds.getNext(function(err, val) {
+							docs[i] = val;
+							return cb(err);
+						});
+					},
+					function() {
+						return docs[i++] !== null;
+					},
+					function(err) {
+						if (err) throw err;
+						assert.deepEqual([{_id:1, a:null}, {_id:0, a:1}, null], docs);
+						return next();
+					}
+				);
 			},
 			},
 
 
-			// "should sort docs with missing fields": function missingFields(next) {
-			// 	var cwc = new CursorDocumentSource.CursorWithContext();
-			// 	var l = [{_id:0, a: 1}, {_id:1}];
-			// 	cwc._cursor = new Cursor( l );
-			// 	var cds = new CursorDocumentSource(cwc);
-			// 	var sds = new SortDocumentSource();
-			// 	sds.addKey("a", true);
-			// 	sds.setSource(cds);
-
-			// 	var docs = [], i = 0;
-			// 	async.doWhilst(
-			// 		function(cb) {
-			// 			sds.getNext(function(err, val) {
-			// 				docs[i] = val;
-			// 				return cb(err);
-			// 			});
-			// 		},
-			// 		function() {
-			// 			return docs[i++] !== DocumentSource.EOF;
-			// 		},
-			// 		function(err) {
-			// 			assert.deepEqual([{_id:1}, {_id:0, a:1}, DocumentSource.EOF], docs);
-			// 			next();
-			// 		}
-			// 	);
-			// },
-
-		// 	"should sort docs with null fields": function nullFields(next) {
-		// 		var cwc = new CursorDocumentSource.CursorWithContext();
-		// 		var l = [{_id:0, a: 1}, {_id:1, a: null}];
-		// 		cwc._cursor = new Cursor( l );
-		// 		var cds = new CursorDocumentSource(cwc);
-		// 		var sds = new SortDocumentSource();
-		// 		sds.addKey("a", true);
-		// 		sds.setSource(cds);
-
-		// 		var docs = [], i = 0;
-		// 		async.doWhilst(
-		// 			function(cb) {
-		// 				sds.getNext(function(err, val) {
-		// 					docs[i] = val;
-		// 					return cb(err);
-		// 				});
-		// 			},
-		// 			function() {
-		// 				return docs[i++] !== DocumentSource.EOF;
-		// 			},
-		// 			function(err) {
-		// 				assert.deepEqual([{_id:1, a:null}, {_id:0, a:1}, DocumentSource.EOF], docs);
-		// 				next();
-		// 			}
-		// 		);
-		// 	},
-
-			"should not support a missing object nested in an array": function missingObjectWithinArray() {
-				var cwc = new CursorDocumentSource.CursorWithContext();
-				var l = [{_id:0, a: [1]}, {_id:1, a:[0]}];
-				cwc._cursor = new Cursor( l );
-				var cds = new CursorDocumentSource(cwc);
+			"should not support a missing object nested in an array": function missingObjectWithinArray(next) {
+				var cds = getCursorDocumentSource([{_id:0, a: [1]}, {_id:1, a:[0]}]);
 				var sds = new SortDocumentSource();
 				var sds = new SortDocumentSource();
 				assert.throws(function() {
 				assert.throws(function() {
 					sds.addKey("a.b", false);
 					sds.addKey("a.b", false);
@@ -422,98 +491,96 @@ module.exports = {
 						sds.advance();
 						sds.advance();
 					}
 					}
 				});
 				});
+				return next();
 			},
 			},
 
 
-		// 	"should compare nested values from within an array": function extractArrayValues(next) {
-		// 		var cwc = new CursorDocumentSource.CursorWithContext();
-		// 		var l = [{_id:0,a:[{b:1},{b:2}]}, {_id:1,a:[{b:1},{b:1}]} ];
-		// 		cwc._cursor = new Cursor( l );
-		// 		var cds = new CursorDocumentSource(cwc);
-		// 		var sds = new SortDocumentSource();
-		// 		sds.addKey("a.b", true);
-		// 		sds.setSource(cds);
-
-		// 		var docs = [], i = 0;
-		// 		async.doWhilst(
-		// 			function(cb) {
-		// 				sds.getNext(function(err, val) {
-		// 					docs[i] = val;
-		// 					return cb(err);
-		// 				});
-		// 			},
-		// 			function() {
-		// 				return docs[i++] !== DocumentSource.EOF;
-		// 			},
-		// 			function(err) {
-		// 				assert.deepEqual([{_id:1,a:[{b:1},{b:1}]},{_id:0,a:[{b:1},{b:2}]}, DocumentSource.EOF], docs);
-		// 				next();
-		// 			}
-		// 		);
-		// 	}
+			"should compare nested values from within an array": function extractArrayValues(next) {
+				var cds = getCursorDocumentSource([{_id:0,a:[{b:1},{b:2}]}, {_id:1,a:[{b:1},{b:1}]}]);
+				var sds = new SortDocumentSource();
+				sds.addKey("a.b", true);
+				sds.setSource(cds);
 
 
+				var docs = [], i = 0;
+				async.doWhilst(
+					function(cb) {
+						sds.getNext(function(err, val) {
+							docs[i] = val;
+							return cb(err);
+						});
+					},
+					function() {
+						return docs[i++] !== null;
+					},
+					function(err) {
+						if (err) throw err;
+						assert.deepEqual([{_id:1,a:[{b:1},{b:1}]},{_id:0,a:[{b:1},{b:2}]}, null], docs);
+						return next();
+					}
+				);
+			}
 		},
 		},
 
 
 		"#coalesce()": {
 		"#coalesce()": {
-			"should return false when coalescing a non-limit source": function nonLimitSource() {
-				var cwc = new CursorDocumentSource.CursorWithContext();
-				var l = [{_id:0,a:[{b:1},{b:2}]}, {_id:1,a:[{b:1},{b:1}]} ];
-				cwc._cursor = new Cursor( l );
-				var cds = new CursorDocumentSource(cwc),
-					sds = SortDocumentSource.createFromJson({a:1});
+			"should return false when coalescing a non-limit source": function nonLimitSource(next) {
+				var cds = getCursorDocumentSource([{_id:0,a:[{b:1},{b:2}]}, {_id:1,a:[{b:1},{b:1}]} ]);
+				var	sds = SortDocumentSource.createFromJson({a:1});
 
 
 				var newSrc = sds.coalesce(cds);
 				var newSrc = sds.coalesce(cds);
 				assert.equal(newSrc, false);
 				assert.equal(newSrc, false);
+				return next();
 			},
 			},
 
 
 
 
-		// 	"should return limit source when coalescing a limit source": function limitSource() {
-		// 		var sds = SortDocumentSource.createFromJson({a:1}),
-		// 			lds = LimitDocumentSource.createFromJson(1);
-
-		// 		// TODO: add missing test cases.
-		// 		// array json getLimit
-		// 		// getShardSource
-		// 		// getMergeSource
+			"should return limit source when coalescing a limit source": function limitSource(next) {
+				var sds = SortDocumentSource.createFromJson({a:1}),
+					lds = LimitDocumentSource.createFromJson(1);
 
 
+				// TODO: add missing test cases.
+				// array json getLimit
+				// getShardSource
+				// getMergeSource
 
 
-		// 		var newSrc = sds.coalesce(LimitDocumentSource.createFromJson(10));
-		// 		assert.ok(newSrc instanceof LimitDocumentSource);
-		// 		assert.equal(sds.getLimit(), 10);
-		// 		assert.equal(newSrc.limit, 10);
+				var newSrc = sds.coalesce(LimitDocumentSource.createFromJson(10));
+				assert.ok(newSrc instanceof LimitDocumentSource);
+				assert.equal(sds.getLimit(), 10);
+				assert.equal(newSrc.limit, 10);
 
 
-		// 		sds.coalesce(LimitDocumentSource.createFromJson(5));
-		// 		assert.equal(sds.getLimit(), 5);
+				sds.coalesce(LimitDocumentSource.createFromJson(5));
+				assert.equal(sds.getLimit(), 5);
 
 
-		// 		var arr = [];
-		// 		sds.serializeToArray(arr);
-		// 		assert.deepEqual(arr, [{$sort: {a:1}}, {$limit: 5}]);
+				var arr = [];
+				sds.serializeToArray(arr);
+				assert.deepEqual(arr, [{$sort: {a:1}}, {$limit: 5}]);
 
 
-		// 		// TODO: add missing test cases
-		// 		// doc array get limit
-		// 		// getShardSource
-		// 		// get MergeSource
-
-		// 	},
+				// TODO: add missing test cases
+				// doc array get limit
+				// getShardSource
+				// get MergeSource
+				return next();
+			},
 		},
 		},
 
 
 		"#dependencies": {
 		"#dependencies": {
-			// /** Dependant field paths. */
-			// "should have Dependant field paths": function dependencies() {
-			// 	var sds = new SortDocumentSource.createFromJson({"sort":1});
-			// 	sds.addKey("a", true);
-			// 	sds.addKey("b.c", false);
-			// 	var deps = {};
-			// 	assert.equal("SEE_NEXT", sds.getDependencies(deps));
-			// 	assert.equal(2, Object.keys(deps).length);
-			// 	assert.ok(deps.a);
-			// 	assert.ok(deps["b.c"]);
-			// 	// TODO: Add needWholeDocument
-			// 	// TODO: Add needTextScore
-			// }
+			/** Dependant field paths. */
+			"should have Dependant field paths": function dependencies(next) {
+			 	var sds = SortDocumentSource.createFromJson({sort: 1});
+
+				sds.addKey('a', true);
+			 	sds.addKey('b.c', false);
+
+				var deps = {fields: {}, needWholeDocument: false, needTextScore: false};
+
+				assert.equal('SEE_NEXT', sds.getDependencies(deps));
+				// Sort keys are now part of deps fields.
+				assert.equal(3, Object.keys(deps.fields).length);
+			 	assert.equal(1, deps.fields.a);
+				assert.equal(1, deps.fields['b.c']);
+				assert.equal(false, deps.needWholeDocument);
+				assert.equal(false, deps.needTextScore);
+				return next();
+			}
 		}
 		}
-
 	}
 	}
-
 };
 };
 
 
 if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).grep(process.env.MOCHA_GREP || '').run(process.exit);
 if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).grep(process.env.MOCHA_GREP || '').run(process.exit);