DepsTracker.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. "use strict";
  2. /**
  3. * Allows components in an aggregation pipeline to report what they need from their input.
  4. *
  5. * @class DepsTracker
  6. * @namespace mungedb-aggregate.pipeline
  7. * @module mungedb-aggregate
  8. * @constructor
  9. */
  10. var DepsTracker = module.exports = function DepsTracker() {
  11. // fields is a set of strings
  12. this.fields = {};
  13. this.needWholeDocument = false;
  14. this.needTextScore = false;
  15. }, klass = DepsTracker, base = Object, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
  16. var ParsedDeps = require("./ParsedDeps"),
  17. Document = require("./Document");
  18. /**
  19. * Returns a projection object covering the dependencies tracked by this class.
  20. * @method toProjection
  21. * @return {Object} projection of caller's dependencies
  22. */
  23. proto.toProjection = function toProjection() {
  24. var proj = {};
  25. // if(this.needTextScore) {
  26. // bb.append(Document::metaFieldTextScore, BSON("$meta" << "textScore"));
  27. // }
  28. if (this.needWholeDocument) {
  29. return proj;
  30. }
  31. if (Object.keys(this.fields).length === 0) {
  32. // Projection language lacks good a way to say no fields needed. This fakes it.
  33. proj._id = 0;
  34. proj.$noFieldsNeeded = 1;
  35. return proj;
  36. }
  37. var needId = false,
  38. last = "";
  39. Object.keys(this.fields).sort().forEach(function(it) {
  40. if (it.indexOf("_id") === 0 && (it.length === 3 || it[3] === ".")) {
  41. // _id and subfields are handled specially due in part to SERVER-7502
  42. needId = true;
  43. return;
  44. }
  45. if (last !== "" && it.indexOf(last) === 0) {
  46. // we are including a parent of *it so we don't need to include this
  47. // field explicitly. In fact, due to SERVER-6527 if we included this
  48. // field, the parent wouldn't be fully included. This logic relies
  49. // on on set iterators going in lexicographic order so that a string
  50. // is always directly before of all fields it prefixes.
  51. return;
  52. }
  53. last = it + ".";
  54. proj[it] = 1;
  55. });
  56. if (needId) // we are explicit either way
  57. proj._id = 1;
  58. else
  59. proj._id = 0;
  60. return proj;
  61. };
  62. // ParsedDeps::_fields is a simple recursive look-up table. For each field:
  63. // If the value has type==Bool, the whole field is needed
  64. // If the value has type==Object, the fields in the subobject are needed
  65. // All other fields should be missing which means not needed
  66. /**
  67. * Takes a depsTracker and builds a simple recursive lookup table out of it.
  68. * @method toParsedDeps
  69. * @return {ParsedDeps}
  70. */
  71. proto.toParsedDeps = function toParsedDeps() {
  72. var obj = {};
  73. if (this.needWholeDocument || this.needTextScore) {
  74. // can't use ParsedDeps in this case
  75. return undefined; // TODO: is this equivalent to boost::none ?
  76. }
  77. var last = "";
  78. Object.keys(this.fields).sort().forEach(function (it) {
  79. if (last !== "" && it.indexOf(last) === 0) {
  80. // we are including a parent of *it so we don't need to include this
  81. // field explicitly. In fact, due to SERVER-6527 if we included this
  82. // field, the parent wouldn't be fully included. This logic relies
  83. // on on set iterators going in lexicographic order so that a string
  84. // is always directly before of all fields it prefixes.
  85. return;
  86. }
  87. last = it + ".";
  88. Document.setNestedField(obj, it, true);
  89. });
  90. return new ParsedDeps(obj);
  91. };