RedactDocumentSource.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. "use strict";
  2. var async = require("async"),
  3. Expression = require("../expressions/Expression"),
  4. Variables = require("../expressions/Variables"),
  5. VariablesIdGenerator = require("../expressions/VariablesIdGenerator"),
  6. VariablesParseState = require("../expressions/VariablesParseState");
  7. /**
  8. * A document source skipper
  9. * @class RedactDocumentSource
  10. * @namespace mungedb-aggregate.pipeline.documentSources
  11. * @module mungedb-aggregate
  12. * @constructor
  13. * @param [ctx] {ExpressionContext}
  14. **/
  15. var RedactDocumentSource = module.exports = function RedactDocumentSource(ctx, expression){
  16. if (arguments.length > 2) throw new Error("up to two args expected");
  17. base.call(this, ctx);
  18. this._expression = expression;
  19. this._variables = new Variables();
  20. this._currentId = null;
  21. }, klass = RedactDocumentSource, base = require("./DocumentSource"), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}}); //jshint ignore:line
  22. klass.redactName = "$redact";
  23. proto.getSourceName = function getSourceName(){
  24. return klass.redactName;
  25. };
  26. var DESCEND_VAL = "descend",
  27. PRUNE_VAL = "prune",
  28. KEEP_VAL = "keep";
  29. proto.getNext = function getNext() {
  30. // if (this.expCtx && this.expCtx.checkForInterrupt) this.expCtx.checkForInterrupt();
  31. var input, result;
  32. while ((input = this.source.getNext())) {
  33. this._variables.setRoot(input);
  34. this._variables.setValue(this._currentId, input);
  35. if ((result = this.redactObject()))
  36. return result;
  37. }
  38. return null;
  39. };
  40. proto.redactValue = function redactValue(input) {
  41. // reorder to make JS happy with types
  42. if (input instanceof Array) {
  43. var newArr = [],
  44. arr = input;
  45. for (var i = 0; i < arr.length; i++) {
  46. if ((arr[i] instanceof Object && arr[i].constructor === Object) || arr[i] instanceof Array) {
  47. var toAdd = this.redactValue(arr[i]);
  48. if (toAdd)
  49. newArr.push(arr[i]);
  50. } else {
  51. newArr.push(arr[i]);
  52. }
  53. }
  54. return newArr;
  55. } else if (input instanceof Object && input.constructor === Object) {
  56. this._variables.setValue(this._currentId, input);
  57. var result = this.redactObject();
  58. if (result !== null)
  59. return result;
  60. return null;
  61. } else {
  62. return input;
  63. }
  64. };
  65. /**
  66. * Redacts the current object
  67. **/
  68. proto.redactObject = function redactObject() {
  69. var expressionResult = this._expression.evaluate(this._variables);
  70. if (expressionResult === KEEP_VAL) {
  71. return this._variables.getDocument(this._currentId);
  72. } else if (expressionResult === PRUNE_VAL) {
  73. return null;
  74. } else if (expressionResult === DESCEND_VAL) {
  75. var input = this._variables.getDocument(this._currentId);
  76. var out = {};
  77. var inputKeys = Object.keys(input);
  78. for (var i = 0; i < inputKeys.length; i++) {
  79. var field = inputKeys[i],
  80. value = input[field];
  81. var val = this.redactValue(value);
  82. if (val)
  83. out[field] = val;
  84. }
  85. return out;
  86. } else {
  87. throw new Error("$redact's expression should not return anything " +
  88. "aside from the variables $$KEEP, $$DESCEND, and " +
  89. "$$PRUNE, but returned " + expressionResult + "; uasserted code 17053");
  90. }
  91. };
  92. proto.optimize = function optimize() {
  93. this._expression = this._expression.optimize();
  94. };
  95. proto.serialize = function serialize(explain) {
  96. var doc = {};
  97. doc[this.getSourceName()] = this._expression.serialize(explain);
  98. return doc;
  99. };
  100. /**
  101. * Creates a new RedactDocumentSource with the input number as the skip
  102. *
  103. * @param {Number} JsonElement this thing is *called* Json, but it expects a number
  104. **/
  105. klass.createFromJson = function createFromJson(jsonElement, ctx) {
  106. if (!jsonElement)
  107. throw new Error("#createFromJson requires at least one argument");
  108. var idGenerator = new VariablesIdGenerator(),
  109. vps = new VariablesParseState(idGenerator),
  110. currentId = vps.defineVariable("CURRENT"),
  111. descendId = vps.defineVariable("DESCEND"),
  112. pruneId = vps.defineVariable("PRUNE"),
  113. keepId = vps.defineVariable("KEEP");
  114. var expression = new Expression.parseOperand(jsonElement, vps),
  115. source = new RedactDocumentSource(ctx, expression);
  116. source._currentId = currentId;
  117. source._variables = new Variables(idGenerator.getIdCount());
  118. source._variables.setValue(descendId, DESCEND_VAL);
  119. source._variables.setValue(pruneId, PRUNE_VAL);
  120. source._variables.setValue(keepId, KEEP_VAL);
  121. return source;
  122. };