SetIsSubsetExpression.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. "use strict";
  2. /**
  3. * A $setissubset pipeline expression.
  4. * @see evaluateInternal
  5. * @class SetIsSubsetExpression
  6. * @namespace mungedb-aggregate.pipeline.expressions
  7. * @module mungedb-aggregate
  8. * @constructor
  9. **/
  10. var SetIsSubsetExpression = module.exports = function SetIsSubsetExpression() {
  11. if (arguments.length != 0) throw new Error("SetIsSubsetExpression constructor must be called with no args");
  12. base.call(this);
  13. }, klass = SetIsSubsetExpression,
  14. FixedArityExpression = require("./FixedArityExpressionT")(klass, 2),
  15. base = FixedArityExpression,
  16. proto = klass.prototype = Object.create(base.prototype, {
  17. constructor: {
  18. value: klass
  19. }
  20. });
  21. // DEPENDENCIES
  22. var Value = require("../Value"),
  23. Expression = require("./Expression"),
  24. Helpers = require("./Helpers");
  25. // PROTOTYPE MEMBERS
  26. proto.getOpName = function getOpName() {
  27. return "$setIsSubset";
  28. };
  29. // lhs should be array, rhs should be set (object). See arrayToSet implementation.
  30. var setIsSubsetHelper = function setIsSubsetHelper(lhs, rhs){
  31. var rkeys = Object.keys(rhs);
  32. // do not short-circuit when lhs.size() > rhs.size()
  33. // because lhs can have redundant entries
  34. for (var i = 0; i < lhs.length; i++){
  35. if (rkeys.indexOf(JSON.stringify(lhs[i])) < 0) {
  36. return false
  37. }
  38. }
  39. return true;
  40. };
  41. /**
  42. * Takes 2 arrays. Returns true if the first is a subset of the second. Returns false otherwise.
  43. * @method evaluateInternal
  44. **/
  45. proto.evaluateInternal = function evaluateInternal(vars) {
  46. if (this.operands.length !== 2) throw new Error("two args expected");
  47. var lhs = this.operands[0].evaluateInternal(vars),
  48. rhs = this.operands[1].evaluateInternal(vars);
  49. if (!(lhs instanceof Array)) throw new Error("Both operands of " + this.getOpName() + ": must be arrays. First argument is of type " + typeof lhs+"; code 17046");
  50. if (!(rhs instanceof Array)) throw new Error("Both operands of " + this.getOpName() + ": must be arrays. First argument is of type " + typeof rhs+"; code 17042");
  51. return setIsSubsetHelper(lhs, Helpers.arrayToSet(rhs));
  52. };
  53. var Optimized = function Optimized(cachedRhsSet, operands) {
  54. this.operands = operands;
  55. this._cachedRhsSet = cachedRhsSet;
  56. }
  57. Optimized.prototype = Object.create(SetIsSubsetExpression.prototype, {
  58. constructor: {
  59. value: Optimized
  60. }
  61. });
  62. Optimized.prototype.evaluateInternal = function evaluateInternal(vars){
  63. var lhs = this.operands[0].evaluateInternal(vars);
  64. if (!(lhs instanceof Array)) throw new Error("uassert 17310: both operands of " + this.getOpName() + " must be arrays. First argument is of type " + typeof lhs);
  65. return setIsSubsetHelper(lhs, this._cachedRhsSet);
  66. };
  67. proto.optimize = function optimize(cachedRhsSet, operands) {
  68. // perform basic optimizations
  69. //var optimized = base.optimize.call(this);
  70. var optimized = NaryExpression.optimize();
  71. // if NaryExpression.optimize() created a new value, return it directly
  72. if(optimized != this){
  73. return optimized;
  74. }
  75. if (this.operands[1] instanceof ConstantExpression){
  76. var ce = this.operands[1],
  77. rhs = ce.getValue();
  78. if (!(rhs instanceof Array)) throw new Error("uassert 17311: both operands of " + this.getOpName() + " must be arrays. Second argument is of type " + typeof rhs);
  79. return new Optimized(Helpers.arrayToSet(rhs), this.operands);
  80. }
  81. return optimized;
  82. };
  83. /** Register Expression */
  84. Expression.registerExpression("$setIsSubset", base.parse);