NaryExpression.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. "use strict";
  2. var assert = require("assert"),
  3. VariablesParseState = require("../../../../lib/pipeline/expressions/VariablesParseState"),
  4. VariablesIdGenerator = require("../../../../lib/pipeline/expressions/VariablesIdGenerator"),
  5. NaryExpression = require("../../../../lib/pipeline/expressions/NaryExpression"),
  6. ConstantExpression = require("../../../../lib/pipeline/expressions/ConstantExpression"),
  7. FieldPathExpression = require("../../../../lib/pipeline/expressions/FieldPathExpression"),
  8. Expression = require("../../../../lib/pipeline/expressions/Expression");
  9. // A dummy child of NaryExpression used for testing
  10. var TestableExpression = (function(){
  11. // CONSTRUCTOR
  12. var klass = function TestableExpression(operands, haveFactory){
  13. base.call(this);
  14. if (operands) {
  15. var self = this;
  16. operands.forEach(function(operand) {
  17. self.addOperand(operand);
  18. });
  19. }
  20. this.haveFactory = !!haveFactory;
  21. }, base = NaryExpression, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
  22. // PROTOTYPE MEMBERS
  23. proto.evaluateInternal = function evaluateInternal(vps) {
  24. // Just put all the values in a list. This is not associative/commutative so
  25. // the results will change if a factory is provided and operations are reordered.
  26. return this.operands.map(function(operand) {
  27. return operand.evaluateInternal(vps);
  28. });
  29. };
  30. proto.isAssociativeAndCommutative = function isAssociativeAndCommutative(){
  31. return this.isAssociativeAndCommutative;
  32. };
  33. proto.getOpName = function getOpName() {
  34. return "$testable";
  35. };
  36. klass.createFromOperands = function(operands) {
  37. var vps = new VariablesParseState(new VariablesIdGenerator()),
  38. testable = new TestableExpression();
  39. operands.forEach(function(x) {
  40. testable.addOperand(Expression.parseOperand(x, vps));
  41. });
  42. return testable;
  43. };
  44. return klass;
  45. })();
  46. module.exports = {
  47. "NaryExpression": {
  48. "constructor()": {
  49. },
  50. "#optimize()": {
  51. "should suboptimize": function() {
  52. var testable = TestableExpression.createFromOperands([{"$and": []}, "$abc"], true);
  53. testable = testable.optimize();
  54. assert.deepEqual(testable.serialize(), {$testable: [true,"$abc"]});
  55. },
  56. "should fold constants": function() {
  57. var testable = TestableExpression.createFromOperands([1,2], true);
  58. testable = testable.optimize();
  59. assert.deepEqual(testable.serialize(), {$const: [1,2]});
  60. },
  61. "should place constants at the end of operands array": function() {
  62. var testable = TestableExpression.createFromOperands([55,65, "$path"], true);
  63. testable = testable.optimize();
  64. assert.deepEqual(testable.serialize(), {$testable:["$path", [55,66]]});
  65. },
  66. "should flatten two layers" : function() {
  67. var testable = TestableExpression.createFromOperands([55, "$path", {$add: [5,6,"$q"]}], true);
  68. testable.addOperand(TestableExpression.createFromOperands([99,100,"$another_path"], true));
  69. testable = testable.optimize();
  70. assert.deepEqual(testable.serialize(), {$testable: ["$path", {$add: [5,6,"$q"]}, "$another_path", [55,66,[99,100]]]});
  71. },
  72. "should flatten three layers": function(){
  73. var bottom = TestableExpression.createFromOperands([5,6,"$c"], true),
  74. middle = TestableExpression.createFromOperands([3,4,"$b"], true).addOperand(bottom),
  75. top = TestableExpression.createFromOperands([1,2,"$a"], true);
  76. var testable = top.optimize();
  77. assert.deepEqual(testable.serialize(), {$testable: ["$a", "$b", "$c", [1,2,[3,4,[5,6]]]]});
  78. }
  79. },
  80. "#addOperand() should be able to add operands to expressions": function testAddOperand(){
  81. var foo = new TestableExpression([new ConstantExpression(9)]).serialize();
  82. var bar = new TestableExpression([new ConstantExpression(9)]).serialize();
  83. var baz = {"$testable":[{"$const":9}]};
  84. assert.deepEqual(foo,bar);
  85. assert.deepEqual(foo, baz);
  86. assert.deepEqual(baz,foo);
  87. assert.deepEqual(new TestableExpression([new ConstantExpression(9)]).serialize(), {"$testable":[{"$const":9}]});
  88. assert.deepEqual(new TestableExpression([new FieldPathExpression("ab.c")]).serialize(), {$testable:["$ab.c"]});
  89. },
  90. "#serialize() should convert an object to json": function(){
  91. var testable = new TestableExpression();
  92. testable.addOperand(new ConstantExpression(5));
  93. assert.deepEqual({foo: testable.serialize()}, {foo:{$testable:[{$const: 5}]}});
  94. },
  95. //the following test case is eagerly awaiting ObjectExpression
  96. "#addDependencies()": function testDependencies(){
  97. var testableExpr = new TestableExpression();
  98. var deps = {};
  99. // no arguments
  100. testableExpr.addDependencies(deps);
  101. assert.deepEqual(deps, {});
  102. // add a constant argument
  103. testableExpr.addOperand(new ConstantExpression(1));
  104. deps = {};
  105. testableExpr.addDependencies(deps);
  106. assert.deepEqual(deps, {});
  107. // add a field path argument
  108. testableExpr.addOperand(new FieldPathExpression("ab.c"));
  109. deps = {};
  110. testableExpr.addDependencies(deps);
  111. assert.deepEqual(deps, {"ab.c":1});
  112. // add an object expression
  113. testableExpr.addOperand(Expression.parseObject({a:"$x",q:"$r"}, new Expression.ObjectCtx({isDocumentOk:1})));
  114. deps = {};
  115. testableExpr.addDependencies(deps);
  116. assert.deepEqual(deps, {"ab.c":1, "x":1, "r":1});
  117. }
  118. }
  119. };
  120. if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);