CondExpression.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. "use strict";
  2. /**
  3. * $cond expression; @see evaluate
  4. * @class CondExpression
  5. * @namespace mungedb-aggregate.pipeline.expressions
  6. * @module mungedb-aggregate
  7. * @constructor
  8. **/
  9. var CondExpression = module.exports = function CondExpression(vars) {
  10. if (arguments.length !== 0) throw new Error("zero args expected");
  11. base.call(this);
  12. }, klass = CondExpression,
  13. base = require("./FixedArityExpressionT")(klass, 3),
  14. proto = klass.prototype = Object.create(base.prototype, {
  15. constructor: {
  16. value: klass
  17. }
  18. });
  19. // DEPENDENCIES
  20. var Value = require("../Value"),
  21. Expression = require("./Expression"),
  22. FixedArityExpressionT = require("./FixedArityExpressionT");
  23. // PROTOTYPE MEMBERS
  24. klass.opName = "$cond";
  25. proto.getOpName = function getOpName() {
  26. return klass.opName;
  27. };
  28. /**
  29. * A utility class. Not in the c++ code.
  30. * @param v a value that should be tested for being null-ish.
  31. * @returns {boolean}
  32. */
  33. klass.isNullish = function(op) {return (op.value == null || op.value == undefined) && !op._fieldPath};
  34. /**
  35. *
  36. * @param expr - I expect this to be the RHS of $cond:{...} or $cond:[,,,]
  37. * @param vps
  38. * @returns {*}
  39. */
  40. klass.parse = function parse(expr, vps) {
  41. // There may only be one argument - an array of 3 items, or a hash containing 3 keys.
  42. //this.checkArgLimit(3);
  43. // if not an object, return;
  44. // todo I don't understand why we'd do this. shouldn't expr be {}, [], or wrong?
  45. if (typeof(expr) !== 'object')
  46. return FixedArityExpressionT.parse(expr, vps);
  47. //NOTE: Deviation from Mongo: The c++ code has a verify( $cond ) structure. The implementation below will never work.
  48. // // ...or expr could be the entirety of $cond:{...} or $cond:[,,,].
  49. // if(!(klass.opName in expr)) {
  50. // throw new Error("Invalid expression. Expected to see '"+klass.opName+"'");
  51. // }
  52. var ret = new CondExpression();
  53. // If this is an Object and not an array, verify all the bits are specified.
  54. // If this is an Object that is an array, verify there are three bits.
  55. // (My issue here is that we got to this parse function when we parsed the $cond:{...} item, and we're calling
  56. // parseOperand (again) without altering the input.)
  57. // var args = Expression.parseOperand(expr, vps);
  58. // var args = expr[klass.opName];
  59. var args = expr;
  60. if (typeof args !== 'object') throw new Error("this should not happen");
  61. if (args instanceof Array) {
  62. // it's the array form. Convert it to the object form.
  63. if (args.length !== 3) throw new Error("$cond requires exactly three arguments");
  64. args = {if: args[0], then: args[1], else: args[2]};
  65. }
  66. // One way or the other, args is now in object form.
  67. Object.keys(args).forEach(function(arg) {
  68. if (arg === 'if') {
  69. ret.operands[0] = Expression.parseOperand(args['if'], vps);
  70. }
  71. else if (arg === 'then') {
  72. ret.operands[1] = Expression.parseOperand(args['then'], vps);
  73. }
  74. else if (arg === 'else') {
  75. ret.operands[2] = Expression.parseOperand(args['else'], vps);
  76. }
  77. else {
  78. throw new Error("Unrecognized parameter to $cond: '" + arg + "'; code 17083");
  79. }
  80. });
  81. if (klass.isNullish(ret.operands[0])) throw new Error("Missing 'if' parameter to $cond; code 17080");
  82. if (klass.isNullish(ret.operands[1])) throw new Error("Missing 'then' parameter to $cond; code 17081");
  83. if (klass.isNullish(ret.operands[2])) throw new Error("Missing 'else' parameter to $cond; code 17082");
  84. return ret;
  85. };
  86. /**
  87. * Use the $cond operator with the following syntax:
  88. * { $cond: { if: <boolean-expression>, then: <true-case>, else: <false-case-> } }
  89. * -or-
  90. * { $cond: [ <boolean-expression>, <true-case>, <false-case> ] }
  91. * @method evaluate
  92. **/
  93. proto.evaluateInternal = function evaluateInternal(vars) {
  94. var pCond1 = this.operands[0].evaluateInternal(vars);
  95. this.idx = 0;
  96. if (Value.coerceToBool(pCond1)) {
  97. this.idx = 1;
  98. } else {
  99. this.idx = 2;
  100. }
  101. return this.operands[this.idx].evaluateInternal(vars);
  102. };
  103. /** Register Expression */
  104. Expression.registerExpression(klass.opName, klass.parse);