MapExpression.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. "use strict";
  2. var MapExpression = module.exports = function MapExpression(varName, varId, input, each){
  3. if (arguments.length !== 4) throw new Error("Four args expected");
  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. // DEPENDENCIES
  10. var Variables = require("./Variables"),
  11. VariablesParseState = require("./VariablesParseState");
  12. // PROTOTYPE MEMBERS
  13. klass.parse = function parse(expr, vpsIn){
  14. if(!("$map" in expr)) {
  15. throw new Error("Tried to create a $let with something other than let. Looks like your parse map went all funny.");
  16. }
  17. if(typeof(expr.$map) !== 'object' || (expr.$map instanceof Array)) {
  18. throw new Error("$map only supports an object as it's argument:16878");
  19. }
  20. var args = expr.$map,
  21. inputElem = args.input,
  22. inElem = args['in'],
  23. asElem = args.as;
  24. if(!inputElem) {
  25. throw new Error("Missing 'input' parameter to $map: 16880");
  26. }
  27. if(!asElem) {
  28. throw new Error("Missing 'as' parameter to $map: 16881");
  29. }
  30. if(!inElem) {
  31. throw new Error("Missing 'in' parameter to $let: 16882");
  32. }
  33. if(Object.keys(args).length > 3) {
  34. var bogus = Object.keys(args).filter(function(x) {return !(x === 'in' || x === 'as' || x === 'input');});
  35. throw new Error("Unrecognized parameter to $map: " + bogus.join(",") + "- 16879");
  36. }
  37. // parse "input"
  38. var input = Expression.parseOperand(inputElem, vpsIn);
  39. // parse "as"
  40. var vpsSub = new VariablesParseState(vpsIn),
  41. varName = asElem;
  42. Variables.uassertValidNameForUserWrite(varName);
  43. var varId = vpsSub.defineVariable(varName);
  44. // parse ""in
  45. var invert = Expression.parseOperand(inElem, vpsSub);
  46. return new MapExpression(varName, varId, input, invert);
  47. };
  48. proto.optimize = function optimize() {
  49. this._input = this._input.optimize();
  50. this._each = this._each.optimize();
  51. return this;
  52. };
  53. proto.serialize = function serialize(explain) {
  54. return {$map: {input:this._input.serialize(explain),
  55. as: this._varName,
  56. 'in': this._each.serialize(explain)}};
  57. };
  58. proto.evaluateInternal = function evaluateInternal(vars) {
  59. // guaranteed at parse time that this isn't using our _varId
  60. var inputVal = this._input.evaluateInternal(vars);
  61. if( inputVal === null || inputVal === undefined) {
  62. return null;
  63. }
  64. if(!(inputVal instanceof Array)) {
  65. throw new Error("Uassert 16883: Input to $map must be an Array, not a " + typeof inputVal);
  66. }
  67. if(inputVal.length === 0) {
  68. return inputVal;
  69. }
  70. // Diverge from Mongo source here, as Javascript has a builtin map operator.
  71. return inputVal.map(function(x) {
  72. vars.setValue(this._varId, x);
  73. var toInsert = this._each.evaluateInternal(vars);
  74. if(toInsert === undefined) {
  75. toInsert = null;
  76. }
  77. return toInsert;
  78. });
  79. };
  80. proto.addDependencies = function addDependencies(deps, path){
  81. this._input.addDependencies(deps, path);
  82. this._each.addDependencies(deps, path);
  83. return deps;
  84. };
  85. Expression.registerExpression("$map", klass.parse);