Browse Source

EAGLESIX-2892 broke out and expanded DepsTracker into own class

Austin Meagher 11 years ago
parent
commit
ff2d843391
1 changed files with 105 additions and 0 deletions
  1. 105 0
      lib/pipeline/DepsTracker.js

+ 105 - 0
lib/pipeline/DepsTracker.js

@@ -0,0 +1,105 @@
+"use strict";
+
+/**
+ * Allows components in an aggregation pipeline to report what they need from their input.
+ *
+ * @class DepsTracker
+ * @namespace mungedb-aggregate.pipeline
+ * @module mungedb-aggregate
+ * @constructor
+ */
+var DepsTracker = module.exports = function DepsTracker() {
+    this.fields = {};
+    this.needWholeDocument = false;
+    this.needTextScore = false;
+}, klass = DepsTracker, base = Object, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
+
+var ParsedDeps = require("./ParsedDeps.js");
+
+/**
+ * Returns a projection object covering the dependencies tracked by this class.
+ *
+ * @method toProjection
+ * @return {Object} projection of caller's dependencies
+ */
+proto.toProjection = function toProjection() {
+    var proj = {};
+
+    // if(this.needTextScore) {
+        // bb.append(Document::metaFieldTextScore, BSON("$meta" << "textScore"));
+    // }
+
+    if (this.needWholeDocument) {
+        return proj;
+    }
+
+    if (Object.keys(this.fields).length === 0) {
+        // Projection language lacks good a way to say no fields needed. This fakes it.
+        proj._id = 0;
+        proj.$noFieldsNeeded = 1;
+        return proj;
+    }
+
+    var last = "",
+        needId = false;
+
+    Object.keys(this.fields).sort().forEach(function (it) {
+        if (it.slice(0,3) == "_id" && (it.length == 3 || it.charAt(3) == ".")) {
+            // _id and subfields are handled specially due in part to SERVER-7502
+            needId = true;
+            return;
+        }
+
+        if (last !== "" && it.slice(0, last.length) === last) {
+            // we are including a parent of *it so we don't need to include this
+            // field explicitly. In fact, due to SERVER-6527 if we included this
+            // field, the parent wouldn't be fully included. This logic relies
+            // on on set iterators going in lexicographic order so that a string
+            // is always directly before of all fields it prefixes.
+            return;
+        }
+
+        last = it + ".";
+        proj[it] = 1;
+    });
+
+    if (needId)
+        proj._id = 1;
+    else
+        proj._id = 0;
+
+    return proj;
+};
+
+/**
+ * Takes a depsTracker and builds a simple recursive lookup table out of it.
+ *
+ * @method toParsedDeps
+ * @return {ParsedDeps}
+ */
+proto.toParsedDeps = function toParsedDeps() {
+    var doc;
+
+    if(this.needWholeDocument || this.needTextScore) {
+        // can't use ParsedDeps in this case
+        // TODO: not sure what appropriate equivalent to boost::none is
+        return;
+    }
+
+    var last = "";
+    Object.keys(this.fields).sort().forEach(function (it) {
+        if (last !== "" && it.slice(0, last.length) === last) {
+            // we are including a parent of *it so we don't need to include this
+            // field explicitly. In fact, due to SERVER-6527 if we included this
+            // field, the parent wouldn't be fully included. This logic relies
+            // on on set iterators going in lexicographic order so that a string
+            // is always directly before of all fields it prefixes.
+            return;
+        }
+
+        last = it + ".";
+        doc[it] = true;
+    });
+
+    return new ParsedDeps(doc);
+};