SetIsSubsetExpression.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  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(klass.name + ": no args expected");
  12. base.call(this);
  13. }, klass = SetIsSubsetExpression, base = require("./FixedArityExpressionT")(SetIsSubsetExpression, 2), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
  14. var Value = require("../Value"),
  15. Expression = require("./Expression"),
  16. NaryExpression = require("./NaryExpression"),
  17. ConstantExpression = require("./ConstantExpression"),
  18. ValueSet = require("../ValueSet");
  19. function setIsSubsetHelper(lhs, rhs) { //NOTE: vector<Value> &lhs, ValueSet &rhs
  20. // do not shortcircuit when lhs.size() > rhs.size()
  21. // because lhs can have redundant entries
  22. for (var i = 0; i < lhs.length; i++) {
  23. if (!rhs.has(lhs[i])) {
  24. return false;
  25. }
  26. }
  27. return true;
  28. }
  29. proto.evaluateInternal = function evaluateInternal(vars) {
  30. var lhs = this.operands[0].evaluateInternal(vars),
  31. rhs = this.operands[1].evaluateInternal(vars);
  32. if (!(lhs instanceof Array))
  33. throw new Error("both operands of " + this.getOpName() + ": must be arrays. First " +
  34. "argument is of type " + Value.getType(lhs) + "; uassert code 17046");
  35. if (!(rhs instanceof Array))
  36. throw new Error("both operands of " + this.getOpName() + ": must be arrays. Second " +
  37. "argument is of type " + Value.getType(rhs) + "; code 17042");
  38. return setIsSubsetHelper(lhs, new ValueSet(rhs));
  39. };
  40. /**
  41. * This class handles the case where the RHS set is constant.
  42. *
  43. * Since it is constant we can construct the hashset once which makes the runtime performance
  44. * effectively constant with respect to the size of RHS. Large, constant RHS is expected to be a
  45. * major use case for $redact and this has been verified to improve performance significantly.
  46. */
  47. function Optimized(cachedRhsSet, operands) {
  48. this._cachedRhsSet = cachedRhsSet;
  49. this.operands = operands;
  50. }
  51. Optimized.prototype = Object.create(SetIsSubsetExpression.prototype, {constructor:{value:Optimized}});
  52. Optimized.prototype.evaluateInternal = function evaluateInternal(vars){
  53. var lhs = this.operands[0].evaluateInternal(vars);
  54. if (!(lhs instanceof Array))
  55. throw new Error("both operands of " + this.getOpName() + " must be arrays. First " +
  56. "argument is of type: " + Value.getType(lhs) + "; uassert code 17310");
  57. return setIsSubsetHelper(lhs, this._cachedRhsSet);
  58. };
  59. proto.optimize = function optimize(cachedRhsSet, operands) { //jshint ignore:line
  60. // perform basic optimizations
  61. var optimized = NaryExpression.optimize();
  62. // if ExpressionNary::optimize() created a new value, return it directly
  63. if(optimized !== this)
  64. return optimized;
  65. var ce;
  66. if ((ce = this.operands[1] instanceof ConstantExpression ? this.operands[1] : undefined)){
  67. var rhs = ce.getValue();
  68. if (!(rhs instanceof Array))
  69. throw new Error("both operands of " + this.getOpName() + " must be arrays. Second " +
  70. "argument is of type " + Value.getType(rhs) + "; uassert code 17311");
  71. return new Optimized(new ValueSet(rhs), this.operands);
  72. }
  73. return optimized;
  74. };
  75. Expression.registerExpression("$setIsSubset", base.parse);
  76. proto.getOpName = function getOpName() {
  77. return "$setIsSubset";
  78. };