MapExpression.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  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. var input = Expression.parseOperand(inputElem, vpsIn);
  38. var vpsSub = new VariablesParseState(vpsIn),
  39. varName = asElem;
  40. Variables.uassertValidNameForUserWrite(varName);
  41. var varId = vpsSub.defineVariable(varName);
  42. var invert = Expression.parseOperand(inElem, vpsSub);
  43. return new MapExpression(varName, varId, input, invert);
  44. };
  45. proto.optimize = function optimize() {
  46. this._input = this._input.optimize();
  47. this._each = this._each.optimize();
  48. return this;
  49. };
  50. proto.serialize = function serialize(explain) {
  51. return {$map: {input:this._input.serialize(explain),
  52. as: this._varName,
  53. 'in': this._each.serialize(explain)}};
  54. };
  55. proto.evaluateInternal = function evaluateInternal(vars) {
  56. var inputVal = this._input.evaluateInternal(vars);
  57. if( inputVal === null) {
  58. return null;
  59. }
  60. if(!(inputVal instanceof Array)) {
  61. throw new Error("Input to $map must be an Array, not a ____ 16883");
  62. }
  63. if(inputVal.length === 0) {
  64. return [];
  65. }
  66. // Diverge from Mongo source here, as Javascript has a builtin map operator.
  67. return inputVal.map(function(x) {
  68. vars.setValue(this._varId, x);
  69. var toInsert = this._each.evaluateInternal(vars);
  70. if(toInsert === undefined) {
  71. toInsert = null;
  72. }
  73. return toInsert;
  74. });
  75. };
  76. proto.addDependencies = function addDependencies(deps, path){
  77. this._input.addDependencies(deps, path);
  78. this._each.addDependencies(deps, path);
  79. return deps;
  80. };
  81. Expression.registerExpression("$map", klass.parse);