NaryExpression.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. "use strict";
  2. /**
  3. * The base class for all n-ary `Expression`s
  4. * @class NaryExpression
  5. * @namespace mungedb-aggregate.pipeline.expressions
  6. * @module mungedb-aggregate
  7. * @extends mungedb-aggregate.pipeline.expressions.Expression
  8. * @constructor
  9. **/
  10. var Expression = require("./Expression");
  11. var NaryExpression = module.exports = function NaryExpression(){
  12. if (arguments.length !== 0) throw new Error("zero args expected");
  13. this.operands = [];
  14. base.call(this);
  15. }, klass = NaryExpression, base = Expression, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
  16. klass.parseArguments = function(exprElement, vps) {
  17. var out = [];
  18. if(exprElement instanceof Array) {
  19. for(var ii = 0; ii < exprElement.length; ii++) {
  20. out.push(Expression.parseOperand(exprElement[ii], vps));
  21. }
  22. } else {
  23. out.push(Expression.parseOperand(exprElement, vps));
  24. }
  25. return out;
  26. };
  27. function partitionBy(fn, coll) {
  28. var ret = {pass:[],
  29. fail:[]};
  30. coll.forEach(function(x) {
  31. if(fn(x)) {
  32. ret.pass.push(x);
  33. } else {
  34. ret.fail.push(x);
  35. }
  36. });
  37. return ret;
  38. }
  39. // DEPENDENCIES
  40. var ConstantExpression = require("./ConstantExpression");
  41. // PROTOTYPE MEMBERS
  42. proto.evaluate = undefined; // evaluate(doc){ ... defined by inheritor ... }
  43. proto.getOpName = function getOpName(doc){
  44. throw new Error("NOT IMPLEMENTED BY INHERITOR");
  45. };
  46. proto.optimize = function optimize(){
  47. var n = this.operands.length,
  48. constantCount = 0;
  49. for(var ii = 0; ii < n; ii++) {
  50. if(this.operands[ii] instanceof ConstantExpression) {
  51. constantCount++;
  52. } else {
  53. this.operands[ii] = this.operands[ii].optimize();
  54. }
  55. }
  56. if(constantCount === n) {
  57. return new ConstantExpression(this.evaluateInternal({}));
  58. }
  59. if(!this.isAssociativeAndCommutative) {
  60. return this;
  61. }
  62. // Flatten and inline nested operations of the same type
  63. var similar = partitionBy(function(x){ return x.getOpName() === this.getOpName();}, this.operands);
  64. this.operands = similar.fail;
  65. similar.pass.forEach(function(x){
  66. this.operands.concat(x.operands);
  67. });
  68. // Partial constant folding
  69. var constantOperands = partitionBy(function(x) {return x instanceof ConstantExpression;}, this.operands);
  70. this.operands = constantOperands.pass;
  71. this.operands = [new ConstantExpression(this.evaluateInternal({}))].concat(constantOperands.fail);
  72. return this;
  73. };
  74. proto.addDependencies = function addDependencies(deps){
  75. for(var i = 0, l = this.operands.length; i < l; ++i)
  76. this.operands[i].addDependencies(deps);
  77. };
  78. /**
  79. * Add an operand to the n-ary expression.
  80. * @method addOperand
  81. * @param pExpression the expression to add
  82. **/
  83. proto.addOperand = function addOperand(expr) {
  84. this.operands.push(expr);
  85. };
  86. proto.serialize = function serialize() {
  87. var ret = {}, subret = [];
  88. for(var ii = 0; ii < this.operands.length; ii++) {
  89. subret.push(this.operands[ii].serialize());
  90. }
  91. ret[this.getOpName()] = subret;
  92. return ret;
  93. };