AndExpression.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. "use strict";
  2. /**
  3. * Create an expression that finds the conjunction of n operands. The
  4. * conjunction uses short-circuit logic; the expressions are evaluated in the
  5. * order they were added to the conjunction, and the evaluation stops and
  6. * returns false on the first operand that evaluates to false.
  7. *
  8. * @class AndExpression
  9. * @extends mungedb-aggregate.pipeline.expressions.VariadicExpressionT
  10. * @namespace mungedb-aggregate.pipeline.expressions
  11. * @module mungedb-aggregate
  12. * @constructor
  13. */
  14. var AndExpression = module.exports = function AndExpression() {
  15. if (arguments.length !== 0) throw new Error(klass.name + ": no args expected");
  16. base.call(this);
  17. }, klass = AndExpression, base = require("./VariadicExpressionT")(AndExpression), proto = klass.prototype = Object.create(base.prototype, {constructor: {value: klass}});
  18. var Value = require("../Value"),
  19. ConstantExpression = require("./ConstantExpression"),
  20. CoerceToBoolExpression = require("./CoerceToBoolExpression"),
  21. Expression = require("./Expression");
  22. proto.optimize = function optimize() {
  23. // optimize the conjunction as much as possible
  24. var expr = base.prototype.optimize.call(this);
  25. // if the result isn't a conjunction, we can't do anything
  26. var andExpr = expr instanceof AndExpression ? expr : undefined;
  27. if (!andExpr)
  28. return expr;
  29. /*
  30. * Check the last argument on the result; if it's not constant (as
  31. * promised by ExpressionNary::optimize(),) then there's nothing
  32. * we can do.
  33. */
  34. var n = andExpr.operands.length;
  35. // ExpressionNary::optimize() generates an ExpressionConstant for {$and:[]}.
  36. if (n <= 0) throw new Error("Assertion failure");
  37. var lastExpr = andExpr.operands[n - 1],
  38. constExpr = lastExpr instanceof ConstantExpression ? lastExpr : undefined;
  39. if (!constExpr)
  40. return expr;
  41. /*
  42. * Evaluate and coerce the last argument to a boolean. If it's false,
  43. * then we can replace this entire expression.
  44. */
  45. var last = Value.coerceToBool(constExpr.getValue());
  46. if (!last)
  47. return ConstantExpression.create(false);
  48. /*
  49. * If we got here, the final operand was true, so we don't need it
  50. * anymore. If there was only one other operand, we don't need the
  51. * conjunction either. Note we still need to keep the promise that
  52. * the result will be a boolean.
  53. */
  54. if (n === 2)
  55. return CoerceToBoolExpression.create(andExpr.operands[0]);
  56. /*
  57. * Remove the final "true" value, and return the new expression.
  58. *
  59. * CW TODO:
  60. * Note that because of any implicit conversions, we may need to
  61. * apply an implicit boolean conversion.
  62. */
  63. andExpr.operands.length = n - 1;
  64. return expr;
  65. };
  66. proto.evaluateInternal = function evaluateInternal(vars) {
  67. var n = this.operands.length;
  68. for (var i = 0; i < n; ++i) {
  69. var value = this.operands[i].evaluateInternal(vars);
  70. if (!Value.coerceToBool(value))
  71. return false;
  72. }
  73. return true;
  74. };
  75. Expression.registerExpression("$and", base.parse);
  76. proto.getOpName = function getOpName() {
  77. return "$and";
  78. };
  79. proto.isAssociativeAndCommutative = function isAssociativeAndCommutative() {
  80. return true;
  81. };