|
|
@@ -8,6 +8,7 @@
|
|
|
* @module mungedb-aggregate
|
|
|
* @constructor
|
|
|
**/
|
|
|
+
|
|
|
var SetIsSubsetExpression = module.exports = function SetIsSubsetExpression() {
|
|
|
this.nargs = 2;
|
|
|
if (arguments.length !== 2) throw new Error("two args expected");
|
|
|
@@ -21,8 +22,25 @@ var SetIsSubsetExpression = module.exports = function SetIsSubsetExpression() {
|
|
|
});
|
|
|
|
|
|
|
|
|
+// lhs should be array, rhs should be set (object). See arrayToSet implementation.
|
|
|
+var setIsSubsetHelper = function setIsSubsetHelper(lhs, rhs){
|
|
|
+
|
|
|
+ var lset = Helpers.arrayToSet(lhs);
|
|
|
+ rkeys = Object.keys(rhs);
|
|
|
+ // do not shortcircuit when lhs.size() > rhs.size()
|
|
|
+ // because lhs can have redundant entries
|
|
|
+ Object.keys(lset).forEach(function (lkey){
|
|
|
+ if (rkeys.indexOf(lkey) < 0){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return true;
|
|
|
+};
|
|
|
+
|
|
|
var Optimized = function Optimized(cachedRhsSet, operands) {
|
|
|
this.operands = operands;
|
|
|
+ this._cachedRhsSet = cachedRhsSet;
|
|
|
}
|
|
|
|
|
|
Optimized.prototype = Object.create(SetIsSubsetExpression.prototype, {
|
|
|
@@ -31,56 +49,30 @@ Optimized.prototype = Object.create(SetIsSubsetExpression.prototype, {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-Optimized.prototype.setIsSubsetHelper = function setIsSubsetHelper(vars){
|
|
|
- // do not shortcircuit when lhs.size() > rhs.size()
|
|
|
- // because lhs can have redundant entries
|
|
|
- lhs.forEach(function (item){
|
|
|
- if (! rhs.contains(item)){
|
|
|
- return false
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- return true;
|
|
|
-};
|
|
|
-
|
|
|
Optimized.prototype.evaluateInternal = function evaluateInternal(vars){
|
|
|
- lhs = operands[0].evaluateInternal(vars);
|
|
|
- rhs = operands[1].evaluateInternal(vars);
|
|
|
+ lhs = this.operands[0].evaluateInternal(vars);
|
|
|
|
|
|
if (!(lhs instanceof Array)) throw new Error("uassert 17046: both operands of " + this.getOpName() + " must be arrays. Second argument is of type " + typeof lhs);
|
|
|
- if (!(rhs instanceof Array)) throw new Error("uassert 17042: both operands of " + this.getOpName() + " must be arrays. Second argument is of type " + typeof rhs);
|
|
|
-
|
|
|
- return setIsSubsetHelper(lhs, arrayToSet(rhs));
|
|
|
+
|
|
|
+ return setIsSubsetHelper(lhs, this._cachedRhsSet);
|
|
|
};
|
|
|
|
|
|
// DEPENDENCIES
|
|
|
var Value = require("../Value"),
|
|
|
- Expression = require("./Expression");
|
|
|
+ Expression = require("./Expression"),
|
|
|
+ Helpers = require("./Helpers.js");
|
|
|
|
|
|
// PROTOTYPE MEMBERS
|
|
|
proto.getOpName = function getOpName() {
|
|
|
return "$setissubset";
|
|
|
};
|
|
|
|
|
|
-//Returns an object containing unique values. All keys are the same as the corresponding value.
|
|
|
-proto.arrayToSet = function arrayToSet(array){
|
|
|
- var set = {};
|
|
|
-
|
|
|
- // This ensures no duplicates.
|
|
|
- array.forEach(function (element) {
|
|
|
- var elementString = JSON.stringify(element);
|
|
|
- set[elementString] = element;
|
|
|
- });
|
|
|
-
|
|
|
- return set;
|
|
|
-};
|
|
|
-
|
|
|
proto.optimize = function optimize(cachedRhsSet, operands) {
|
|
|
|
|
|
// perform basic optimizations
|
|
|
- var optimized = this.optimize();
|
|
|
+ var optimized = base.optimize.call(this);
|
|
|
|
|
|
- // if NaryExoressuib.optimize() created a new value, return it directly
|
|
|
+ // if NaryExpression.optimize() created a new value, return it directly
|
|
|
if(optimized != this){
|
|
|
return optimized;
|
|
|
}
|
|
|
@@ -91,7 +83,7 @@ proto.optimize = function optimize(cachedRhsSet, operands) {
|
|
|
|
|
|
if (!(rhs instanceof Array)) throw new Error("uassert 17311: both operands of " + this.getOpName() + " must be arrays. Second argument is of type " + typeof rhs);
|
|
|
|
|
|
- return new Optimized(proto.arrayToSet(rhs), operands);
|
|
|
+ return new Optimized(Helpers.arrayToSet(rhs), this.operands);
|
|
|
}
|
|
|
|
|
|
return optimized;
|
|
|
@@ -99,34 +91,16 @@ proto.optimize = function optimize(cachedRhsSet, operands) {
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
- * Takes 2 arrays. Returns true if the second is a subset of the first. Returns false otherwise.
|
|
|
+ * Takes 2 arrays. Returns true if the first is a subset of the second. Returns false otherwise.
|
|
|
* @method evaluateInternal
|
|
|
**/
|
|
|
proto.evaluateInternal = function evaluateInternal(vars) {
|
|
|
- var array1 = this.operands[0].evaluateInternal(vars),
|
|
|
- array2 = this.operands[1].evaluateInternal(vars);
|
|
|
- if (!(array1 instanceof Array)) throw new Error(this.getOpName() + ": object 1 must be an array. Got a(n) " + typeof array1);
|
|
|
- if (!(array2 instanceof Array)) throw new Error(this.getOpName() + ": object 2 must be an array. Got a(n) " + typeof array1);
|
|
|
-
|
|
|
- var sizeOfArray1 = array1.length;
|
|
|
- var sizeOfArray2 = array2.length;
|
|
|
- var outerLoop = 0;
|
|
|
- var innerLoop = 0;
|
|
|
- for (outerLoop = 0; outerLoop < sizeOfArray1; outerLoop++) {
|
|
|
- for (innerLoop = 0; innerLoop < sizeOfArray2; innerLoop++) {
|
|
|
- if (array2[outerLoop] == array1[innerLoop])
|
|
|
- break;
|
|
|
- }
|
|
|
+ var lhs = this.operands[0].evaluateInternal(vars),
|
|
|
+ rhs = this.operands[1].evaluateInternal(vars);
|
|
|
+ if (!(lhs instanceof Array)) throw new Error(this.getOpName() + ": object 1 must be an array. Got a(n) " + typeof lhs);
|
|
|
+ if (!(rhs instanceof Array)) throw new Error(this.getOpName() + ": object 2 must be an array. Got a(n) " + typeof rhs);
|
|
|
|
|
|
- /* If the above inner loop was not broken at all then
|
|
|
- array2[i] is not present in array1[] */
|
|
|
- if (innerLoop == sizeOfArray2)
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- /* If we reach here then all elements of array2[]
|
|
|
- are present in array1[] */
|
|
|
- return true;
|
|
|
+ return setIsSubsetHelper(lhs, Helpers.arrayToSet(rhs));
|
|
|
};
|
|
|
|
|
|
/** Register Expression */
|