Explorar o código

EAGLESIX-3083: Updating code changes

David Aebersold %!s(int64=11) %!d(string=hai) anos
pai
achega
e405cf372d
Modificáronse 1 ficheiros con 145 adicións e 43 borrados
  1. 145 43
      lib/pipeline/documentSources/SortDocumentSource.js

+ 145 - 43
lib/pipeline/documentSources/SortDocumentSource.js

@@ -34,6 +34,8 @@ var SortDocumentSource = module.exports = function SortDocumentSource(ctx){
 
 // DEPENDENCIES
 var FieldPathExpression = require("../expressions/FieldPathExpression"),
+	VariablesIdGenerator = require("../../../../lib/pipeline/expressions/VariablesIdGenerator"),
+	VariablesParseState = require("../../../../lib/pipeline/expressions/VariablesParseState"),
 	Value = require("../Value");
 
 klass.sortName = "$sort";
@@ -82,6 +84,9 @@ proto.coalesce = function coalesce(nextSource) {
 proto.getNext = function getNext(callback) {
 	if (!callback) throw new Error(this.getSourceName() + ' #getNext() requires callback');
 
+	if (this.expCtx instanceof Object && this.expCtx.checkForInterrupt && this.expCtx.checkForInterrupt() === false)
+		return callback(new Error("Interrupted"));
+
 	var self = this,
 		out;
 	async.series(
@@ -118,15 +123,21 @@ proto.getNext = function getNext(callback) {
 	return out;
 };
 
+/**
+* Serialize to Array.
+*
+* @param {Array} array
+* @param {bool} explain
+**/
 proto.serializeToArray = function serializeToArray(array, explain) {
 	var doc = {};
-	if (explain) {
-		doc.sortKey = this.serializeSortKey();
+	if (explain) { // always one obj for combined $sort + $limit
+		doc.sortKey = this.serializeSortKey(explain);
 		doc.mergePresorted = this._mergePresorted;
 		doc.limit = this.limitSrc ? this.limitSrc.getLimit() : undefined;
 		array.push(doc);
-	} else {
-		var inner = this.serializeSortKey();
+	} else { // one Value for $sort and maybe a Value for $limit
+		var inner = this.serializeSortKey(explain);
 		if (this._mergePresorted)
 			inner.$mergePresorted = true;
 		doc[this.getSourceName()] = inner;
@@ -151,7 +162,10 @@ proto.serialize = function serialize(explain) {
 * @param {bool} ascending if true, use the key for an ascending sort, otherwise, use it for descending
 **/
 proto.addKey = function addKey(fieldPath, ascending) {
-	var pathExpr = new FieldPathExpression(fieldPath);
+	var idGenerator = new VariablesIdGenerator(),
+		vps = new VariablesParseState(idGenerator);
+
+	var pathExpr = new FieldPathExpression("$$ROOT." + fieldPath, vps);
 	this.vSortKey.push(pathExpr);
 	if (ascending === true || ascending === false) {
 		this.vAscending.push(ascending);
@@ -161,40 +175,66 @@ proto.addKey = function addKey(fieldPath, ascending) {
 	}
 };
 
-proto.populate = function populate(callback) {
+proto.makeSortOptions = function makeSortOptions(){
 	/* make sure we've got a sort key */
 	if (!this.vSortKey.length) throw new Error("no sort key for " + this.getSourceName());
 
-	// Skipping stuff about mergeCursors and commandShards
+	// Skipping memory checks
+
+	var opts;
+	if ( this.limitSrc)
+		opts.limit = limitSrc.getLimt();
+
+	return opts;
+}
+// TODO: Add makeSortOptions
+
+proto.populate = function populate(callback) {
+	if ( this._mergePresorted ){
+		// Skipping stuff about mergeCursors and commandShards
+
+
+		if ( this.source instanceof MergeCursorDocumentSouce ){
+			populateFromCursors( this.source);
+		} else if ( this.source instanceof CommandShardsDocumentSource){
+			populateFromJsonArrays(this.source);
+		} else {
+			throw new Error("code 17196; the " + klass.sortName + "can only mergePresorted from MergeCursors and CommandShards");
+		}
+	} else {
+
+		/* pull everything from the underlying source */
+		var self = this,
+			next;
+		async.doWhilst(
+			function (cb) {
+				self.source.getNext(function(err, doc) {
+					next = doc;
+
+					// Don't add EOF; it doesn't sort well.
+					if (doc !== DocumentSource.EOF)
+						self.documents.push(doc);
+					return cb();
+				});
+			},
+			function() {
+				return next !== DocumentSource.EOF;
+			},
+			function(err) {
+		/* sort the list */
+				self.documents.sort(SortDocumentSource.prototype.compare.bind(self));
+
+		/* start the sort iterator */
+				self.docIterator = 0;
+
+				self.populated = true;
+				return callback();
+		}
+		);
+
 
-	/* pull everything from the underlying source */
-	var self = this,
-		next;
-	async.doWhilst(
-		function (cb) {
-			self.source.getNext(function(err, doc) {
-				next = doc;
-
-				// Don't add EOF; it doesn't sort well.
-				if (doc !== DocumentSource.EOF)
-					self.documents.push(doc);
-				return cb();
-			});
-		},
-		function() {
-			return next !== DocumentSource.EOF;
-		},
-		function(err) {
-	/* sort the list */
-			self.documents.sort(SortDocumentSource.prototype.compare.bind(self));
-
-	/* start the sort iterator */
-			self.docIterator = 0;
-
-			self.populated = true;
-			return callback();
 	}
-	);
+	this.populated = true;
 };
 
 /**
@@ -240,13 +280,21 @@ proto.compare = function compare(pL,pR) {
 /**
 * Write out an object whose contents are the sort key.
 **/
-proto.serializeSortKey = function sortKeyToJson() {
+proto.serializeSortKey = function serializeSortKey(explain) {
 	var keyObj = {};
 
 	var n = this.vSortKey.length;
 	for (var i = 0; i < n; i++) {
-		var fieldPath = this.vSortKey[i].getFieldPath();
-		keyObj[fieldPath] = this.vAscending[i] ? 1 : -1;
+		if ( this.vSortKey[i] instanceof FieldPathExpression ) {
+			var fieldPath = this.vSortKey[i].getFieldPath();
+
+			// append a named integer based on the sort order
+			keyObj[fieldPath] = this.vAscending[i] ? 1 : -1;
+		} else {
+			// other expressions use a made-up field name
+			keyObj[{"$computed":i}] = this.vSortKey[i].serializeToArray(explain);
+		}
+
 	}
 	return keyObj;
 };
@@ -254,6 +302,8 @@ proto.serializeSortKey = function sortKeyToJson() {
 /**
  * Creates a new SortDocumentSource
  * @param {Object} jsonElement
+ * @ctx {object} context
+ *
 **/
 klass.createFromJson = function createFromJson(jsonElement, ctx) {
 	if (typeof jsonElement !== "object") throw new Error("code 15973; the " + klass.sortName + " key specification must be an object");
@@ -263,18 +313,70 @@ klass.createFromJson = function createFromJson(jsonElement, ctx) {
 
 	/* check for then iterate over the sort object */
 	var sortKeys = 0;
-	for(var key in jsonElement) {
-		var sortOrder = 0;
+	for(var keyField in jsonElement) {
+		var fieldName = keyField.fieldName;
 
-		if (typeof jsonElement[key] !== "number") throw new Error("code 15974; " + klass.sortName + " key ordering must be specified using a number");
+		if ( fieldName === "$mergePresorted" ){
+			Sort._mergePresorted = true;
+			continue;
+		}
+
+		if ( keyField instanceof object) {
+			// this restriction is due to needing to figure out sort direction
+			throw new Error("code 17312; " + klass.sortName + "the only expression supported by $sort right now is {$meta: 'textScore'}");
+
+			nextSort.vSortKey.push(new ExpressionMeta());
+			nextSort.vAscending.push(false); // best scoring documents first
+			continue;
+		}
 
-		sortOrder = jsonElement[key];
-		if ((sortOrder != 1) && (sortOrder !== -1)) throw new Error("code 15975; " + klass.sortName + " key ordering must be 1 (for ascending) or 0 (for descending)");
+		if (typeof jsonElement[keyField] !== "number") throw new Error("code 15974; " + klass.sortName + "$sort key ordering must be specified using a number or {$meta: 'text'}");
 
-		nextSort.addKey(key, (sortOrder > 0));
+		var sortOrder = 0;
+		sortOrder = jsonElement[keyField];
+		if ((sortOrder != 1) && (sortOrder !== -1)) throw new Error("code 15975; " + klass.sortName + "$sort key ordering must be 1 (for ascending) or -1 (for descending)");
+
+		nextSort.addKey(keyField, (sortOrder > 0));
 		++sortKeys;
 	}
 
 	if (sortKeys <= 0) throw new Error("code 15976; " + klass.sortName + " must have at least one sort key");
+	
+	if ( limit > 0) {
+		var coalesced = nextSort.coalesce( create(ctx, limit));
+		// should always coalesce
+	}
+
 	return nextSort;
 };
+
+// SplittableDocumentSource implementation.
+klass.isSplittableDocumentSource = true;
+
+/**
+ * Get dependencies.
+ *
+ * @param deps
+ * @returns {number}
+ */
+proto.getDependencies = function getDependencies(deps) {
+	return DocumentSource.GetDepsReturn.SEE_NEXT;
+};
+
+/**
+ * Get shard source.
+ *
+ * @returns {null}
+ */
+proto.getShardSource = function getShardSource() {
+	return null;
+};
+
+/**
+ * Get router source.
+ *
+ * @returns {SkipDocumentSource}
+ */
+proto.getRouterSource = function getRouterSource() {
+	return this;
+};