SetIsSubsetExpression.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  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. this.nargs = 2;
  12. if (arguments.length !== 2) throw new Error("two args expected");
  13. base.call(this);
  14. }, klass = SetIsSubsetExpression,
  15. base = require("./NaryExpression"),
  16. proto = klass.prototype = Object.create(base.prototype, {
  17. constructor: {
  18. value: klass
  19. }
  20. });
  21. // lhs should be array, rhs should be set (object). See arrayToSet implementation.
  22. var setIsSubsetHelper = function setIsSubsetHelper(lhs, rhs){
  23. var lset = Helpers.arrayToSet(lhs);
  24. rkeys = Object.keys(rhs);
  25. // do not shortcircuit when lhs.size() > rhs.size()
  26. // because lhs can have redundant entries
  27. Object.keys(lset).forEach(function (lkey){
  28. if (rkeys.indexOf(lkey) < 0){
  29. return false;
  30. }
  31. });
  32. return true;
  33. };
  34. var Optimized = function Optimized(cachedRhsSet, operands) {
  35. this.operands = operands;
  36. this._cachedRhsSet = cachedRhsSet;
  37. }
  38. Optimized.prototype = Object.create(SetIsSubsetExpression.prototype, {
  39. constructor: {
  40. value: Optimized
  41. }
  42. });
  43. Optimized.prototype.evaluateInternal = function evaluateInternal(vars){
  44. lhs = this.operands[0].evaluateInternal(vars);
  45. if (!(lhs instanceof Array)) throw new Error("uassert 17046: both operands of " + this.getOpName() + " must be arrays. Second argument is of type " + typeof lhs);
  46. return setIsSubsetHelper(lhs, this._cachedRhsSet);
  47. };
  48. // DEPENDENCIES
  49. var Value = require("../Value"),
  50. Expression = require("./Expression"),
  51. Helpers = require("./Helpers.js");
  52. // PROTOTYPE MEMBERS
  53. proto.getOpName = function getOpName() {
  54. return "$setissubset";
  55. };
  56. proto.optimize = function optimize(cachedRhsSet, operands) {
  57. // perform basic optimizations
  58. var optimized = base.optimize.call(this);
  59. // if NaryExpression.optimize() created a new value, return it directly
  60. if(optimized != this){
  61. return optimized;
  62. }
  63. if (operands[1] instanceof ConstantExpression){
  64. var ce = operands[1],
  65. rhs = ce.getValue();
  66. if (!(rhs instanceof Array)) throw new Error("uassert 17311: both operands of " + this.getOpName() + " must be arrays. Second argument is of type " + typeof rhs);
  67. return new Optimized(Helpers.arrayToSet(rhs), this.operands);
  68. }
  69. return optimized;
  70. };
  71. /**
  72. * Takes 2 arrays. Returns true if the first is a subset of the second. Returns false otherwise.
  73. * @method evaluateInternal
  74. **/
  75. proto.evaluateInternal = function evaluateInternal(vars) {
  76. var lhs = this.operands[0].evaluateInternal(vars),
  77. rhs = this.operands[1].evaluateInternal(vars);
  78. if (!(lhs instanceof Array)) throw new Error(this.getOpName() + ": object 1 must be an array. Got a(n) " + typeof lhs);
  79. if (!(rhs instanceof Array)) throw new Error(this.getOpName() + ": object 2 must be an array. Got a(n) " + typeof rhs);
  80. return setIsSubsetHelper(lhs, Helpers.arrayToSet(rhs));
  81. };
  82. /** Register Expression */
  83. Expression.registerExpression("$setissubset", base.parse(SetIsSubsetExpression));