MapExpression.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. "use strict";
  2. var MapExpression = module.exports = function MapExpression(varName, varId, input, each){
  3. if (arguments.length !== 4) throw new Error(klass.name + ": args expected: varName, varId, input, each");
  4. this._varName = varName;
  5. this._varId = varId;
  6. this._input = input;
  7. this._each = each;
  8. }, klass = MapExpression, Expression = require("./Expression"), base = Expression, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
  9. var Value = require("../Value"),
  10. Variables = require("./Variables");
  11. klass.parse = function parse(expr, vpsIn) {
  12. // if (!(exprFieldName)) throw new Error("Assertion failure"); //NOTE: DEVIATION FROM MONGO: we do not have exprFieldName here
  13. if (Value.getType(expr) !== "Object") {
  14. throw new Error("$map only supports an object as it's argument; uassert code 16878");
  15. }
  16. // "in" must be parsed after "as" regardless of BSON order
  17. var inputElem,
  18. asElem,
  19. inElem,
  20. args = expr;
  21. for (var argFieldName in args) {
  22. var arg = args[argFieldName];
  23. if (argFieldName === "input") {
  24. inputElem = arg;
  25. } else if (argFieldName === "as") {
  26. asElem = arg;
  27. } else if (argFieldName === "in") {
  28. inElem = arg;
  29. } else {
  30. throw new Error("Unrecognized parameter to $map: " + argFieldName + "; uassert code 16879");
  31. }
  32. }
  33. if (!inputElem) throw new Error("Missing 'input' parameter to $map; uassert code 16880");
  34. if (!asElem) throw new Error("Missing 'as' parameter to $map; uassert code 16881");
  35. if (!inElem) throw new Error("Missing 'in' parameter to $map; uassert code 16882");
  36. // parse "input"
  37. var input = Expression.parseOperand(inputElem, vpsIn); // only has outer vars
  38. // parse "as"
  39. var vpsSub = vpsIn, // vpsSub gets our vars, vpsIn doesn't.
  40. varName = asElem;
  41. Variables.uassertValidNameForUserWrite(varName);
  42. var varId = vpsSub.defineVariable(varName);
  43. // parse "in"
  44. var inExpr = Expression.parseOperand(inElem, vpsSub); // has access to map variable
  45. return new MapExpression(varName, varId, input, inExpr);
  46. };
  47. proto.optimize = function optimize() {
  48. // TODO handle when _input is constant
  49. this._input = this._input.optimize();
  50. this._each = this._each.optimize();
  51. return this;
  52. };
  53. proto.serialize = function serialize(explain) {
  54. return {
  55. $map: {
  56. input: this._input.serialize(explain),
  57. as: this._varName,
  58. in : this._each.serialize(explain)
  59. }
  60. };
  61. };
  62. proto.evaluateInternal = function evaluateInternal(vars) {
  63. // guaranteed at parse time that this isn't using our _varId
  64. var inputVal = this._input.evaluateInternal(vars);
  65. if (inputVal === null || inputVal === undefined)
  66. return null;
  67. if (!(inputVal instanceof Array)){
  68. throw new Error("input to $map must be an Array not " +
  69. Value.getType(inputVal) + "; uassert code 16883");
  70. }
  71. if (inputVal.length === 0)
  72. return inputVal;
  73. var output = new Array(inputVal.length);
  74. for (var i = 0, l = inputVal.length; i < l; i++) {
  75. vars.setValue(this._varId, inputVal[i]);
  76. var toInsert = this._each.evaluateInternal(vars);
  77. if (toInsert === undefined)
  78. toInsert = null; // can't insert missing values into array
  79. output[i] = toInsert;
  80. }
  81. return output;
  82. };
  83. proto.addDependencies = function addDependencies(deps, path) { //jshint ignore:line
  84. this._input.addDependencies(deps);
  85. this._each.addDependencies(deps);
  86. return deps;
  87. };
  88. Expression.registerExpression("$map", klass.parse);