CompareExpression.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. "use strict";
  2. /**
  3. * Generic comparison expression that gets used for $eq, $ne, $lt, $lte, $gt, $gte, and $cmp.
  4. * @class CompareExpression
  5. * @namespace mungedb-aggregate.pipeline.expressions
  6. * @module mungedb-aggregate
  7. * @constructor
  8. */
  9. var CompareExpression = module.exports = function CompareExpression(cmpOp) {
  10. if (!(arguments.length === 1 && typeof cmpOp === "string")) throw new Error(klass.name + ": args expected: cmpOp");
  11. this.cmpOp = cmpOp;
  12. base.call(this);
  13. }, klass = CompareExpression, base = require("./FixedArityExpressionT")(CompareExpression, 2), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
  14. var Value = require("../Value"),
  15. Expression = require("./Expression"),
  16. ConstantExpression = require("./ConstantExpression"),
  17. FieldPathExpression = require("./FieldPathExpression"),
  18. FieldRangeExpression = require("./FieldRangeExpression"),
  19. NaryExpression = require("./NaryExpression");
  20. klass.parse = function parse(jsonExpr, vps, op) {
  21. var expr = new CompareExpression(op),
  22. args = base.parseArguments(jsonExpr, vps);
  23. expr.validateArguments(args);
  24. expr.operands = args;
  25. return expr;
  26. };
  27. /**
  28. * Lookup table for truth value returns
  29. * @param truthValues truth value for -1, 0, 1
  30. * @param reverse reverse comparison operator
  31. * @param name string name
  32. */
  33. var CmpLookup = function CmpLookup(truthValues, reverse, name) { // emulating a struct
  34. if (arguments.length !== 3) throw new Error("args expected: truthValues, reverse, name");
  35. this.truthValues = truthValues;
  36. this.reverse = reverse;
  37. this.name = name;
  38. };
  39. /**
  40. * Enumeration of comparison operators. Any changes to these values require adjustment of
  41. * the lookup table in the implementation.
  42. */
  43. var CmpOp = klass.CmpOp = {
  44. EQ: "$eq",
  45. NE: "$ne",
  46. GT: "$gt",
  47. GTE: "$gte",
  48. LT: "$lt",
  49. LTE: "$lte",
  50. CMP: "$cmp",
  51. };
  52. /**
  53. * a table of cmp type lookups to truth values
  54. * @private
  55. */
  56. var cmpLookupMap = [ //NOTE: converted from this Array to a Dict/Object below using CmpLookup#name as the key
  57. // -1 0 1 reverse name (taking advantage of the fact that our 'enums' are strings below)
  58. new CmpLookup([false, true, false], CmpOp.EQ, CmpOp.EQ),
  59. new CmpLookup([true, false, true], CmpOp.NE, CmpOp.NE),
  60. new CmpLookup([false, false, true], CmpOp.LT, CmpOp.GT),
  61. new CmpLookup([false, true, true], CmpOp.LTE, CmpOp.GTE),
  62. new CmpLookup([true, false, false], CmpOp.GT, CmpOp.LT),
  63. new CmpLookup([true, true, false], CmpOp.GTE, CmpOp.LTE),
  64. // CMP is special. Only name is used.
  65. new CmpLookup([false, false, false], CmpOp.CMP, CmpOp.CMP)
  66. ].reduce(function(r, o) {
  67. r[o.name] = o;
  68. return r;
  69. }, {});
  70. proto.evaluateInternal = function evaluateInternal(vars) {
  71. var left = this.operands[0].evaluateInternal(vars),
  72. right = this.operands[1].evaluateInternal(vars),
  73. cmp = Value.compare(left, right);
  74. // Make cmp one of 1, 0, or -1.
  75. if (cmp === 0) {
  76. //leave as 0
  77. } else if (cmp < 0) {
  78. cmp = -1;
  79. } else if (cmp > 0) {
  80. cmp = 1;
  81. }
  82. if (this.cmpOp === CmpOp.CMP)
  83. return cmp;
  84. var returnValue = cmpLookupMap[this.cmpOp].truthValues[cmp + 1];
  85. return returnValue;
  86. };
  87. proto.getOpName = function getOpName() {
  88. return this.cmpOp;
  89. };
  90. function bindLast(fn, lastArg) { // similar to the boost::bind used in the mongo code
  91. return function() {
  92. return fn.apply(this, Array.prototype.slice.call(arguments).concat([lastArg]));
  93. };
  94. }
  95. Expression.registerExpression("$cmp", bindLast(klass.parse, CmpOp.CMP));
  96. Expression.registerExpression("$eq", bindLast(klass.parse, CmpOp.EQ));
  97. Expression.registerExpression("$gt", bindLast(klass.parse, CmpOp.GT));
  98. Expression.registerExpression("$gte", bindLast(klass.parse, CmpOp.GTE));
  99. Expression.registerExpression("$lt", bindLast(klass.parse, CmpOp.LT));
  100. Expression.registerExpression("$lte", bindLast(klass.parse, CmpOp.LTE));
  101. Expression.registerExpression("$ne", bindLast(klass.parse, CmpOp.NE));