|
@@ -1,367 +1,468 @@
|
|
|
"use strict";
|
|
"use strict";
|
|
|
var assert = require("assert"),
|
|
var assert = require("assert"),
|
|
|
- Expression = require("../../../../lib/pipeline/expressions/Expression"),
|
|
|
|
|
|
|
+ pipeline = require("../../../../lib/pipeline"),
|
|
|
|
|
+ expressions = pipeline.expressions,
|
|
|
|
|
+ Expression = expressions.Expression,
|
|
|
CompareExpression = require("../../../../lib/pipeline/expressions/CompareExpression"),
|
|
CompareExpression = require("../../../../lib/pipeline/expressions/CompareExpression"),
|
|
|
- FieldRangeExpression = require("../../../../lib/pipeline/expressions/FieldRangeExpression"),
|
|
|
|
|
VariablesParseState = require("../../../../Lib/pipeline/expressions/VariablesParseState"),
|
|
VariablesParseState = require("../../../../Lib/pipeline/expressions/VariablesParseState"),
|
|
|
VariablesIdGenerator = require("../../../../Lib/pipeline/expressions/VariablesIdGenerator"),
|
|
VariablesIdGenerator = require("../../../../Lib/pipeline/expressions/VariablesIdGenerator"),
|
|
|
- ConstantExpression = require("../../../../Lib/pipeline/expressions/ConstantExpression");
|
|
|
|
|
-
|
|
|
|
|
-module.exports = {
|
|
|
|
|
-
|
|
|
|
|
- "CompareExpression": {
|
|
|
|
|
-
|
|
|
|
|
- "constructor()": {
|
|
|
|
|
-
|
|
|
|
|
- "should throw Error if no args": function testConstructor() {
|
|
|
|
|
- assert.throws(function() {
|
|
|
|
|
- new CompareExpression();
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ utils = require("./utils"),
|
|
|
|
|
+ constify = utils.constify,
|
|
|
|
|
+ expressionToJson = utils.expressionToJson;
|
|
|
|
|
+
|
|
|
|
|
+// Mocha one-liner to make these tests self-hosted
|
|
|
|
|
+if(!module.parent)return(require.cache[__filename]=null,(new(require("mocha"))({ui:"exports",reporter:"spec",grep:process.env.TEST_GREP})).addFile(__filename).run(process.exit));
|
|
|
|
|
+
|
|
|
|
|
+var TestBase = function TestBase(overrides) {
|
|
|
|
|
+ //NOTE: DEVIATION FROM MONGO: using this base class to make things easier to initialize
|
|
|
|
|
+ for (var key in overrides)
|
|
|
|
|
+ this[key] = overrides[key];
|
|
|
|
|
+ },
|
|
|
|
|
+ OptimizeBase = (function() {
|
|
|
|
|
+ var klass = function OptimizeBase() {
|
|
|
|
|
+ base.apply(this, arguments);
|
|
|
|
|
+ },
|
|
|
|
|
+ base = TestBase,
|
|
|
|
|
+ proto = klass.prototype = Object.create(base.prototype);
|
|
|
|
|
+ proto.run = function() {
|
|
|
|
|
+ var specElement = this.spec,
|
|
|
|
|
+ idGenerator = new VariablesIdGenerator(),
|
|
|
|
|
+ vps = new VariablesParseState(idGenerator),
|
|
|
|
|
+ expression = Expression.parseOperand(specElement, vps),
|
|
|
|
|
+ optimized = expression.optimize();
|
|
|
|
|
+ assert.deepEqual(constify(this.expectedOptimized()), expressionToJson(optimized));
|
|
|
|
|
+ };
|
|
|
|
|
+ return klass;
|
|
|
|
|
+ })(),
|
|
|
|
|
+ FieldRangeOptimize = (function() {
|
|
|
|
|
+ var klass = function FieldRangeOptimize() {
|
|
|
|
|
+ base.apply(this, arguments);
|
|
|
|
|
+ },
|
|
|
|
|
+ base = OptimizeBase,
|
|
|
|
|
+ proto = klass.prototype = Object.create(base.prototype);
|
|
|
|
|
+ proto.expectedOptimized = function(){
|
|
|
|
|
+ return this.spec;
|
|
|
|
|
+ };
|
|
|
|
|
+ return klass;
|
|
|
|
|
+ })(),
|
|
|
|
|
+ NoOptimize = (function() {
|
|
|
|
|
+ var klass = function NoOptimize() {
|
|
|
|
|
+ base.apply(this, arguments);
|
|
|
|
|
+ },
|
|
|
|
|
+ base = OptimizeBase,
|
|
|
|
|
+ proto = klass.prototype = Object.create(base.prototype);
|
|
|
|
|
+ proto.expectedOptimized = function(){
|
|
|
|
|
+ return this.spec;
|
|
|
|
|
+ };
|
|
|
|
|
+ return klass;
|
|
|
|
|
+ })(),
|
|
|
|
|
+ ExpectedResultBase = (function() {
|
|
|
|
|
+ /** Check expected result for expressions depending on constants. */
|
|
|
|
|
+ var klass = function ExpectedResultBase() {
|
|
|
|
|
+ base.apply(this, arguments);
|
|
|
|
|
+ },
|
|
|
|
|
+ base = OptimizeBase,
|
|
|
|
|
+ proto = klass.prototype = Object.create(base.prototype);
|
|
|
|
|
+ proto.run = function() {
|
|
|
|
|
+ base.prototype.run.call(this);
|
|
|
|
|
+ var specElement = this.spec,
|
|
|
|
|
+ idGenerator = new VariablesIdGenerator(),
|
|
|
|
|
+ vps = new VariablesParseState(idGenerator),
|
|
|
|
|
+ expression = Expression.parseOperand(specElement, vps);
|
|
|
|
|
+ // Check expression spec round trip.
|
|
|
|
|
+ assert.deepEqual(expressionToJson(expression), constify(specElement));
|
|
|
|
|
+ // Check evaluation result.
|
|
|
|
|
+ assert.strictEqual(expression.evaluate({}), this.expectedResult);
|
|
|
|
|
+ // Check that the result is the same after optimizing.
|
|
|
|
|
+ var optimized = expression.optimize();
|
|
|
|
|
+ assert.strictEqual(optimized.evaluate({}), this.expectedResult);
|
|
|
|
|
+ };
|
|
|
|
|
+ proto.expectedOptimized = function() {
|
|
|
|
|
+ return {$const:this.expectedResult};
|
|
|
|
|
+ };
|
|
|
|
|
+ return klass;
|
|
|
|
|
+ })(),
|
|
|
|
|
+ ExpectedTrue = (function(){
|
|
|
|
|
+ var klass = function ExpectedTrue() {
|
|
|
|
|
+ base.apply(this, arguments);
|
|
|
|
|
+ },
|
|
|
|
|
+ base = ExpectedResultBase,
|
|
|
|
|
+ proto = klass.prototype = Object.create(base.prototype);
|
|
|
|
|
+ proto.expectedResult = true;
|
|
|
|
|
+ return klass;
|
|
|
|
|
+ })(),
|
|
|
|
|
+ ExpectedFalse = (function(){
|
|
|
|
|
+ var klass = function ExpectedFalse() {
|
|
|
|
|
+ base.apply(this, arguments);
|
|
|
|
|
+ },
|
|
|
|
|
+ base = ExpectedResultBase,
|
|
|
|
|
+ proto = klass.prototype = Object.create(base.prototype);
|
|
|
|
|
+ proto.expectedResult = false;
|
|
|
|
|
+ return klass;
|
|
|
|
|
+ })(),
|
|
|
|
|
+ ParseError = (function(){
|
|
|
|
|
+ var klass = function ParseError() {
|
|
|
|
|
+ base.apply(this, arguments);
|
|
|
|
|
+ },
|
|
|
|
|
+ base = TestBase,
|
|
|
|
|
+ proto = klass.prototype = Object.create(base.prototype);
|
|
|
|
|
+ proto.run = function() {
|
|
|
|
|
+ var specElement = this.spec,
|
|
|
|
|
+ idGenerator = new VariablesIdGenerator(),
|
|
|
|
|
+ vps = new VariablesParseState(idGenerator);
|
|
|
|
|
+ assert.throws(function() {
|
|
|
|
|
+ Expression.parseOperand(specElement, vps);
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+ return klass;
|
|
|
|
|
+ })();
|
|
|
|
|
+
|
|
|
|
|
+exports.CompareExpression = {
|
|
|
|
|
+
|
|
|
|
|
+ "constructor()": {
|
|
|
|
|
+
|
|
|
|
|
+ "should throw Error if no args": function() {
|
|
|
|
|
+ assert.throws(function() {
|
|
|
|
|
+ new CompareExpression();
|
|
|
|
|
+ });
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- "#getOpName()": {
|
|
|
|
|
-
|
|
|
|
|
- "should return the correct op name; $eq, $ne, $gt, $gte, $lt, $lte, $cmp": function testOpName() {
|
|
|
|
|
- assert.equal((new CompareExpression(CompareExpression.EQ)).getOpName(), "$eq");
|
|
|
|
|
- assert.equal((new CompareExpression(CompareExpression.NE)).getOpName(), "$ne");
|
|
|
|
|
- assert.equal((new CompareExpression(CompareExpression.GT)).getOpName(), "$gt");
|
|
|
|
|
- assert.equal((new CompareExpression(CompareExpression.GTE)).getOpName(), "$gte");
|
|
|
|
|
- assert.equal((new CompareExpression(CompareExpression.LT)).getOpName(), "$lt");
|
|
|
|
|
- assert.equal((new CompareExpression(CompareExpression.LTE)).getOpName(), "$lte");
|
|
|
|
|
- assert.equal((new CompareExpression(CompareExpression.CMP)).getOpName(), "$cmp");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ "should throw if more than 1 args": function() {
|
|
|
|
|
+ assert.throws(function() {
|
|
|
|
|
+ new CompareExpression(1,2);
|
|
|
|
|
+ });
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- "#evaluateInternal()": {
|
|
|
|
|
-
|
|
|
|
|
- "$eq": {
|
|
|
|
|
-
|
|
|
|
|
- "should return false if first < second; {$eq:[1,2]}": function testEqLt() {
|
|
|
|
|
- //debugger;
|
|
|
|
|
- var idGenerator = new VariablesIdGenerator();
|
|
|
|
|
- var vps = new VariablesParseState(idGenerator);
|
|
|
|
|
- var parseOp = Expression.parseOperand({
|
|
|
|
|
- $eq: [{
|
|
|
|
|
- $const: 1
|
|
|
|
|
- }, {
|
|
|
|
|
- $const: 2
|
|
|
|
|
- }]
|
|
|
|
|
- }, vps);
|
|
|
|
|
- var result = parseOp.evaluateInternal({});
|
|
|
|
|
-
|
|
|
|
|
- //assert.equal(new CompareExpression( CompareExpression.EQ).evaluateInternal({"$eq":[1,2]}), false);
|
|
|
|
|
- assert.equal(result, false);
|
|
|
|
|
-
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- "should return true if first == second; {$eq:[1,1]}": function testEqEq() {
|
|
|
|
|
- var idGenerator = new VariablesIdGenerator();
|
|
|
|
|
- var vps = new VariablesParseState(idGenerator);
|
|
|
|
|
-
|
|
|
|
|
- assert.equal(Expression.parseOperand({
|
|
|
|
|
- $eq: [1, 1]
|
|
|
|
|
- }, vps).evaluateInternal({}), true);
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- "should return false if first > second {$eq:[1,0]}": function testEqGt() {
|
|
|
|
|
- var idGenerator = new VariablesIdGenerator();
|
|
|
|
|
- var vps = new VariablesParseState(idGenerator);
|
|
|
|
|
- assert.equal(Expression.parseOperand({
|
|
|
|
|
- $eq: [1, 0]
|
|
|
|
|
- }).evaluateInternal({}), false);
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- "should return false if first and second are different types {$eq:[null,0]}": function testEqGt() {
|
|
|
|
|
- var idGenerator = new VariablesIdGenerator();
|
|
|
|
|
- var vps = new VariablesParseState(idGenerator);
|
|
|
|
|
- assert.equal(Expression.parseOperand({
|
|
|
|
|
- $eq: [null, 0]
|
|
|
|
|
- }, vps).evaluateInternal({}), false);
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- "should return false if first and second are different types {$eq:[undefined,0]}": function testEqGt() {
|
|
|
|
|
- var idGenerator = new VariablesIdGenerator();
|
|
|
|
|
- var vps = new VariablesParseState(idGenerator);
|
|
|
|
|
- assert.equal(Expression.parseOperand({
|
|
|
|
|
- $eq: [undefined, 0]
|
|
|
|
|
- }, vps).evaluateInternal({}), false);
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- "should return false if first and second are different arrays {$eq:[[1],[null]]}": function testEqGt() {
|
|
|
|
|
- var idGenerator = new VariablesIdGenerator();
|
|
|
|
|
- var vps = new VariablesParseState(idGenerator);
|
|
|
|
|
- assert.equal(Expression.parseOperand({
|
|
|
|
|
- $eq: [
|
|
|
|
|
- [1],
|
|
|
|
|
- [null]
|
|
|
|
|
- ]
|
|
|
|
|
- }, vps).evaluateInternal({}), false);
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- "should return false if first and second are different arrays {$eq:[[1],[]]}": function testEqGt() {
|
|
|
|
|
- assert.equal(Expression.parseOperand({
|
|
|
|
|
- $eq: [
|
|
|
|
|
- [1],
|
|
|
|
|
- []
|
|
|
|
|
- ]
|
|
|
|
|
- }, vps).evaluateInternal({}), false);
|
|
|
|
|
- var idGenerator = new VariablesIdGenerator();
|
|
|
|
|
- var vps = new VariablesParseState(idGenerator);
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- "should return true if first and second are the same arrays {$eq:[[1],[1]]}": function testEqGt() {
|
|
|
|
|
- var idGenerator = new VariablesIdGenerator();
|
|
|
|
|
- var vps = new VariablesParseState(idGenerator);
|
|
|
|
|
- assert.equal(Expression.parseOperand({
|
|
|
|
|
- $eq: [
|
|
|
|
|
- [1],
|
|
|
|
|
- [1]
|
|
|
|
|
- ]
|
|
|
|
|
- }, vps).evaluateInternal({}), true);
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- // "$ne": {
|
|
|
|
|
-
|
|
|
|
|
- // "should return true if first < second; {$ne:[1,2]}": function testNeLt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$ne:[1,2]}).evaluateInternal({}), true);
|
|
|
|
|
- // },
|
|
|
|
|
-
|
|
|
|
|
- // "should return false if first == second; {$ne:[1,1]}": function testNeLt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$ne:[1,1]}).evaluateInternal({}), false);
|
|
|
|
|
- // },
|
|
|
|
|
-
|
|
|
|
|
- // "should return true if first > second; {$ne:[1,0]}": function testNeGt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$ne:[1,0]}).evaluateInternal({}), true);
|
|
|
|
|
- // }
|
|
|
|
|
-
|
|
|
|
|
- // },
|
|
|
|
|
-
|
|
|
|
|
- // "$gt": {
|
|
|
|
|
-
|
|
|
|
|
- // "should return false if first < second; {$gt:[1,2]}": function testGtLt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$gt:[1,2]}).evaluateInternal({}), false);
|
|
|
|
|
- // },
|
|
|
|
|
-
|
|
|
|
|
- // "should return false if first == second; {$gt:[1,1]}": function testGtLt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$gt:[1,1]}).evaluateInternal({}), false);
|
|
|
|
|
- // },
|
|
|
|
|
-
|
|
|
|
|
- // "should return true if first > second; {$gt:[1,0]}": function testGtGt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$gt:[1,0]}).evaluateInternal({}), true);
|
|
|
|
|
- // }
|
|
|
|
|
-
|
|
|
|
|
- // },
|
|
|
|
|
-
|
|
|
|
|
- // "$gte": {
|
|
|
|
|
-
|
|
|
|
|
- // "should return false if first < second; {$gte:[1,2]}": function testGteLt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$gte:[1,2]}).evaluateInternal({}), false);
|
|
|
|
|
- // },
|
|
|
|
|
-
|
|
|
|
|
- // "should return true if first == second; {$gte:[1,1]}": function testGteLt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$gte:[1,1]}).evaluateInternal({}), true);
|
|
|
|
|
- // },
|
|
|
|
|
-
|
|
|
|
|
- // "should return true if first > second; {$gte:[1,0]}": function testGteGt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$gte:[1,0]}).evaluateInternal({}), true);
|
|
|
|
|
- // }
|
|
|
|
|
-
|
|
|
|
|
- // },
|
|
|
|
|
-
|
|
|
|
|
- // "$lt": {
|
|
|
|
|
-
|
|
|
|
|
- // "should return true if first < second; {$lt:[1,2]}": function testLtLt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$lt:[1,2]}).evaluateInternal({}), true);
|
|
|
|
|
- // },
|
|
|
|
|
-
|
|
|
|
|
- // "should return false if first == second; {$lt:[1,1]}": function testLtLt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$lt:[1,1]}).evaluateInternal({}), false);
|
|
|
|
|
- // },
|
|
|
|
|
-
|
|
|
|
|
- // "should return false if first > second; {$lt:[1,0]}": function testLtGt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$lt:[1,0]}).evaluateInternal({}), false);
|
|
|
|
|
- // }
|
|
|
|
|
-
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ "should not throw if 1 arg and arg is string": function() {
|
|
|
|
|
+ assert.doesNotThrow(function() {
|
|
|
|
|
+ new CompareExpression("a");
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "$lte": {
|
|
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should return true if first < second; {$lte:[1,2]}": function testLteLt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$lte:[1,2]}).evaluateInternal({}), true);
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ "#getOpName()": {
|
|
|
|
|
|
|
|
- // "should return true if first == second; {$lte:[1,1]}": function testLteLt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$lte:[1,1]}).evaluateInternal({}), true);
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ "should return the correct op name; $eq, $ne, $gt, $gte, $lt, $lte, $cmp": function() {
|
|
|
|
|
+ assert.equal(new CompareExpression(CompareExpression.CmpOp.EQ).getOpName(), "$eq");
|
|
|
|
|
+ assert.equal(new CompareExpression(CompareExpression.CmpOp.NE).getOpName(), "$ne");
|
|
|
|
|
+ assert.equal(new CompareExpression(CompareExpression.CmpOp.GT).getOpName(), "$gt");
|
|
|
|
|
+ assert.equal(new CompareExpression(CompareExpression.CmpOp.GTE).getOpName(), "$gte");
|
|
|
|
|
+ assert.equal(new CompareExpression(CompareExpression.CmpOp.LT).getOpName(), "$lt");
|
|
|
|
|
+ assert.equal(new CompareExpression(CompareExpression.CmpOp.LTE).getOpName(), "$lte");
|
|
|
|
|
+ assert.equal(new CompareExpression(CompareExpression.CmpOp.CMP).getOpName(), "$cmp");
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should return false if first > second; {$lte:[1,0]}": function testLteGt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$lte:[1,0]}).evaluateInternal({}), false);
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ "#evaluate()": {
|
|
|
|
|
|
|
|
- // "$cmp": {
|
|
|
|
|
|
|
+ /** $eq with first < second. */
|
|
|
|
|
+ "EqLt": function EqLt() {
|
|
|
|
|
+ new ExpectedFalse({
|
|
|
|
|
+ spec: {$eq:[1,2]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should return -1 if first < second; {$cmp:[1,2]}": function testCmpLt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$cmp:[1,2]}).evaluateInternal({}), -1);
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $eq with first == second. */
|
|
|
|
|
+ "EqEq": function EqEq() {
|
|
|
|
|
+ new ExpectedTrue({
|
|
|
|
|
+ spec: {$eq:[1,1]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should return 0 if first < second; {$cmp:[1,1]}": function testCmpLt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$cmp:[1,1]}).evaluateInternal({}), 0);
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $eq with first > second. */
|
|
|
|
|
+ "EqGt": function EqEq() {
|
|
|
|
|
+ new ExpectedFalse({
|
|
|
|
|
+ spec: {$eq:[1,0]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should return 1 if first < second; {$cmp:[1,0]}": function testCmpLt(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$cmp:[1,0]}).evaluateInternal({}), 1);
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $ne with first < second. */
|
|
|
|
|
+ "NeLt": function NeLt() {
|
|
|
|
|
+ new ExpectedTrue({
|
|
|
|
|
+ spec: {$ne:[1,2]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should return 1 even if comparison is larger; {$cmp:['z','a']}": function testCmpBracketed(){
|
|
|
|
|
- // assert.equal(Expression.parseOperand({$cmp:['z','a']}).evaluateInternal({}), 1);
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+ /** $ne with first == second. */
|
|
|
|
|
+ "NeEq": function NeEq() {
|
|
|
|
|
+ new ExpectedFalse({
|
|
|
|
|
+ spec: {$ne:[1,1]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $ne with first > second. */
|
|
|
|
|
+ "NeGt": function NeGt() {
|
|
|
|
|
+ new ExpectedTrue({
|
|
|
|
|
+ spec: {$ne:[1,0]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should throw Error": {
|
|
|
|
|
|
|
+ /** $gt with first < second. */
|
|
|
|
|
+ "GtLt": function GtLt() {
|
|
|
|
|
+ new ExpectedFalse({
|
|
|
|
|
+ spec: {$gt:[1,2]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "if zero operands are provided; {$ne:[]}": function testZeroOperands(){
|
|
|
|
|
- // assert.throws(function(){
|
|
|
|
|
- // Expression.parseOperand({$ne:[]}).evaluateInternal({});
|
|
|
|
|
- // });
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $gt with first == second. */
|
|
|
|
|
+ "GtEq": function GtEq() {
|
|
|
|
|
+ new ExpectedFalse({
|
|
|
|
|
+ spec: {$gt:[1,1]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "if one operand is provided; {$eq:[1]}": function testOneOperand(){
|
|
|
|
|
- // assert.throws(function(){
|
|
|
|
|
- // Expression.parseOperand({$eq:[1]}).evaluateInternal({});
|
|
|
|
|
- // });
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $gt with first > second. */
|
|
|
|
|
+ "GtGt": function GtGt() {
|
|
|
|
|
+ new ExpectedTrue({
|
|
|
|
|
+ spec: {$gt:[1,0]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "if three operands are provided; {$gt:[2,3,4]}": function testThreeOperands(){
|
|
|
|
|
- // assert.throws(function(){
|
|
|
|
|
- // Expression.parseOperand({$gt:[2,3,4]}).evaluateInternal({});
|
|
|
|
|
- // });
|
|
|
|
|
- // }
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+ /** $gte with first < second. */
|
|
|
|
|
+ "GteLt": function GteLt() {
|
|
|
|
|
+ new ExpectedFalse({
|
|
|
|
|
+ spec: {$gte:[1,2]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $gte with first == second. */
|
|
|
|
|
+ "GteEq": function GteEq() {
|
|
|
|
|
+ new ExpectedTrue({
|
|
|
|
|
+ spec: {$gte:[1,1]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "#optimize()": {
|
|
|
|
|
|
|
+ /** $gte with first > second. */
|
|
|
|
|
+ "GteGt": function GteGt() {
|
|
|
|
|
+ new ExpectedTrue({
|
|
|
|
|
+ spec: {$gte:[1,0]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should optimize constants; {$eq:[1,1]}": function testOptimizeConstants(){
|
|
|
|
|
- // assert.deepEqual(Expression.parseOperand({$eq:[1,1]}).optimize().toJSON(true), {$const:true});
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $gte with first > second. */
|
|
|
|
|
+ "LtLt": function LtLt() {
|
|
|
|
|
+ new ExpectedTrue({
|
|
|
|
|
+ spec: {$lt:[1,2]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should not optimize if $cmp op; {$cmp:[1,'$a']}": function testNoOptimizeCmp(){
|
|
|
|
|
- // assert.deepEqual(Expression.parseOperand({$cmp:[1,'$a']}).optimize().toJSON(), {$cmp:[1,'$a']});
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $lt with first == second. */
|
|
|
|
|
+ "LtEq": function LtEq() {
|
|
|
|
|
+ new ExpectedFalse({
|
|
|
|
|
+ spec: {$lt:[1,1]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should not optimize if $ne op; {$ne:[1,'$a']}": function testNoOptimizeNe(){
|
|
|
|
|
- // assert.deepEqual(Expression.parseOperand({$ne:[1,'$a']}).optimize().toJSON(), {$ne:[1,'$a']});
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $lt with first > second. */
|
|
|
|
|
+ "LtGt": function LtGt() {
|
|
|
|
|
+ new ExpectedFalse({
|
|
|
|
|
+ spec: {$lt:[1,0]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should not optimize if no constants; {$ne:['$a','$b']}": function testNoOptimizeNoConstant(){
|
|
|
|
|
- // assert.deepEqual(Expression.parseOperand({$ne:['$a','$b']}).optimize().toJSON(), {$ne:['$a','$b']});
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $lte with first < second. */
|
|
|
|
|
+ "LteLt": function LteLt() {
|
|
|
|
|
+ new ExpectedTrue({
|
|
|
|
|
+ spec: {$lte:[1,2]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should not optimize without an immediate field path;": {
|
|
|
|
|
|
|
+ /** $lte with first == second. */
|
|
|
|
|
+ "LteEq": function LteEq() {
|
|
|
|
|
+ new ExpectedTrue({
|
|
|
|
|
+ spec: {$lte:[1,1]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "{$eq:[{$and:['$a']},1]}": function testNoOptimizeWithoutFieldPath(){
|
|
|
|
|
- // assert.deepEqual(Expression.parseOperand({$eq:[{$and:['$a']},1]}).optimize().toJSON(), {$eq:[{$and:['$a']},1]});
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $lte with first > second. */
|
|
|
|
|
+ "LteGt": function LteGt() {
|
|
|
|
|
+ new ExpectedFalse({
|
|
|
|
|
+ spec: {$lte:[1,0]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "(reversed); {$eq:[1,{$and:['$a']}]}": function testNoOptimizeWithoutFieldPathReverse(){
|
|
|
|
|
- // assert.deepEqual(Expression.parseOperand({$eq:[1,{$and:['$a']}]}).optimize().toJSON(), {$eq:[1,{$and:['$a']}]});
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+ /** $cmp with first < second. */
|
|
|
|
|
+ "CmpLt": function CmpLt() {
|
|
|
|
|
+ new ExpectedResultBase({
|
|
|
|
|
+ spec: {$cmp:[1,2]},
|
|
|
|
|
+ expectedResult: -1,
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $cmp with first == second. */
|
|
|
|
|
+ "CmpEq": function CmpEq() {
|
|
|
|
|
+ new ExpectedResultBase({
|
|
|
|
|
+ spec: {$cmp:[1,1]},
|
|
|
|
|
+ expectedResult: 0,
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should optimize $eq expressions;": {
|
|
|
|
|
|
|
+ /** $cmp with first > second. */
|
|
|
|
|
+ "CmpGt": function CmpGt() {
|
|
|
|
|
+ new ExpectedResultBase({
|
|
|
|
|
+ spec: {$cmp:[1,0]},
|
|
|
|
|
+ expectedResult: 1,
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "{$eq:['$a',1]}": function testOptimizeEq(){
|
|
|
|
|
- // var expr = Expression.parseOperand({$eq:['$a',1]}).optimize();
|
|
|
|
|
- // assert(expr instanceof FieldRangeExpression, "not optimized");
|
|
|
|
|
- // assert.deepEqual(expr.toJSON(), {$eq:['$a',1]});
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $cmp results are bracketed to an absolute value of 1. */
|
|
|
|
|
+ "CmpBracketed": function CmpBracketed() {
|
|
|
|
|
+ var test = new ExpectedResultBase({
|
|
|
|
|
+ spec: {$cmp:["z","a"]},
|
|
|
|
|
+ expectedResult: 1,
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "{$eq:[1,'$a']} (reversed)": function testOptimizeEqReverse(){
|
|
|
|
|
- // var expr = Expression.parseOperand({$eq:[1,'$a']}).optimize();
|
|
|
|
|
- // assert(expr instanceof FieldRangeExpression, "not optimized");
|
|
|
|
|
- // assert.deepEqual(expr.toJSON(), {$eq:['$a',1]});
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+ /** Zero operands provided. */
|
|
|
|
|
+ "ZeroOperands": function ZeroOperands() {
|
|
|
|
|
+ new ParseError({
|
|
|
|
|
+ spec: {$ne:[]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** One operands provided. */
|
|
|
|
|
+ "OneOperand": function OneOperand() {
|
|
|
|
|
+ new ParseError({
|
|
|
|
|
+ spec: {$eq:[1]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should optimize $lt expressions;": {
|
|
|
|
|
|
|
+ /** Incompatible types can be compared. */
|
|
|
|
|
+ "IncompatibleTypes": function IncompatibleTypes() {
|
|
|
|
|
+ var specElement = {$ne:["a",1]},
|
|
|
|
|
+ idGenerator = new VariablesIdGenerator(),
|
|
|
|
|
+ vps = new VariablesParseState(idGenerator),
|
|
|
|
|
+ expr = Expression.parseOperand(specElement, vps);
|
|
|
|
|
+ assert.deepEqual(expr.evaluate({}), true);
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "{$lt:['$a',1]}": function testOptimizeLt(){
|
|
|
|
|
- // var expr = Expression.parseOperand({$lt:['$a',1]}).optimize();
|
|
|
|
|
- // assert(expr instanceof FieldRangeExpression, "not optimized");
|
|
|
|
|
- // assert.deepEqual(expr.toJSON(), {$lt:['$a',1]});
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** Three operands provided. */
|
|
|
|
|
+ "ThreeOperands": function ThreeOperands() {
|
|
|
|
|
+ new ParseError({
|
|
|
|
|
+ spec: {$gt:[2,3,4]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "{$lt:[1,'$a']} (reversed)": function testOptimizeLtReverse(){
|
|
|
|
|
- // var expr = Expression.parseOperand({$lt:[1,'$a']}).optimize();
|
|
|
|
|
- // assert(expr instanceof FieldRangeExpression, "not optimized");
|
|
|
|
|
- // assert.deepEqual(expr.toJSON(), {$gt:['$a',1]});
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * An expression depending on constants is optimized to a constant via
|
|
|
|
|
+ * ExpressionNary::optimize().
|
|
|
|
|
+ */
|
|
|
|
|
+ "OptimizeConstants": function OptimizeConstants() {
|
|
|
|
|
+ new OptimizeBase({
|
|
|
|
|
+ spec: {$eq:[1,1]},
|
|
|
|
|
+ expectedOptimized: function() {
|
|
|
|
|
+ return {$const: true};
|
|
|
|
|
+ },
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** $cmp is not optimized. */
|
|
|
|
|
+ "NoOptimizeCmp": function NoOptimizeCmp() {
|
|
|
|
|
+ new NoOptimize({
|
|
|
|
|
+ spec: {$cmp:[1,"$a"]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should optimize $lte expressions;": {
|
|
|
|
|
|
|
+ /** $ne is not optimized. */
|
|
|
|
|
+ "NoOptimizeNe": function NoOptimizeNe() {
|
|
|
|
|
+ new NoOptimize({
|
|
|
|
|
+ spec: {$ne:[1,"$a"]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "{$lte:['$b',2]}": function testOptimizeLte(){
|
|
|
|
|
- // var expr = Expression.parseOperand({$lte:['$b',2]}).optimize();
|
|
|
|
|
- // assert(expr instanceof FieldRangeExpression, "not optimized");
|
|
|
|
|
- // assert.deepEqual(expr.toJSON(), {$lte:['$b',2]});
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** No optimization is performend without a constant. */
|
|
|
|
|
+ "NoOptimizeNoConstant": function NoOptimizeNoConstant() {
|
|
|
|
|
+ new NoOptimize({
|
|
|
|
|
+ spec: {$ne:["$a", "$b"]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "{$lte:[2,'$b']} (reversed)": function testOptimizeLteReverse(){
|
|
|
|
|
- // var expr = Expression.parseOperand({$lte:[2,'$b']}).optimize();
|
|
|
|
|
- // assert(expr instanceof FieldRangeExpression, "not optimized");
|
|
|
|
|
- // assert.deepEqual(expr.toJSON(), {$gte:['$b',2]});
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+ /** No optimization is performend without an immediate field path. */
|
|
|
|
|
+ "NoOptimizeWithoutFieldPath": function NoOptimizeWithoutFieldPath() {
|
|
|
|
|
+ new NoOptimize({
|
|
|
|
|
+ spec: {$eq:[{$and:["$a"]},1]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** No optimization is performend without an immediate field path. */
|
|
|
|
|
+ "NoOptimizeWithoutFieldPathReverse": function NoOptimizeWithoutFieldPathReverse() {
|
|
|
|
|
+ new NoOptimize({
|
|
|
|
|
+ spec: {$eq:[1,{$and:["$a"]}]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should optimize $gt expressions;": {
|
|
|
|
|
|
|
+ /** An equality expression is optimized. */
|
|
|
|
|
+ "OptimizeEq": function OptimizeEq() {
|
|
|
|
|
+ new FieldRangeOptimize({
|
|
|
|
|
+ spec: {$eq:["$a",1]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "{$gt:['$b',2]}": function testOptimizeGt(){
|
|
|
|
|
- // var expr = Expression.parseOperand({$gt:['$b',2]}).optimize();
|
|
|
|
|
- // assert(expr instanceof FieldRangeExpression, "not optimized");
|
|
|
|
|
- // assert.deepEqual(expr.toJSON(), {$gt:['$b',2]});
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** A reverse sense equality expression is optimized. */
|
|
|
|
|
+ "OptimizeEqReverse": function OptimizeEqReverse() {
|
|
|
|
|
+ new FieldRangeOptimize({
|
|
|
|
|
+ spec: {$eq:[1,"$a"]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "{$gt:[2,'$b']} (reversed)": function testOptimizeGtReverse(){
|
|
|
|
|
- // var expr = Expression.parseOperand({$gt:[2,'$b']}).optimize();
|
|
|
|
|
- // assert(expr instanceof FieldRangeExpression, "not optimized");
|
|
|
|
|
- // assert.deepEqual(expr.toJSON(), {$lt:['$b',2]});
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+ /** A $lt expression is optimized. */
|
|
|
|
|
+ "OptimizeLt": function OptimizeLt() {
|
|
|
|
|
+ new FieldRangeOptimize({
|
|
|
|
|
+ spec: {$lt:["$a",1]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** A reverse sense $lt expression is optimized. */
|
|
|
|
|
+ "OptimizeLtReverse": function OptimizeLtReverse() {
|
|
|
|
|
+ new FieldRangeOptimize({
|
|
|
|
|
+ spec: {$lt:[1,"$a"]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "should optimize $gte expressions;": {
|
|
|
|
|
|
|
+ /** A $lte expression is optimized. */
|
|
|
|
|
+ "OptimizeLte": function OptimizeLte() {
|
|
|
|
|
+ new FieldRangeOptimize({
|
|
|
|
|
+ spec: {$lte:["$b",2]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "{$gte:['$b',2]}": function testOptimizeGte(){
|
|
|
|
|
- // var expr = Expression.parseOperand({$gte:['$b',2]}).optimize();
|
|
|
|
|
- // assert(expr instanceof FieldRangeExpression, "not optimized");
|
|
|
|
|
- // assert.deepEqual(expr.toJSON(), {$gte:['$b',2]});
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** A reverse sense $lte expression is optimized. */
|
|
|
|
|
+ "OptimizeLteReverse": function OptimizeLteReverse() {
|
|
|
|
|
+ new FieldRangeOptimize({
|
|
|
|
|
+ spec: {$lte:[2,"$b"]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // "{$gte:[2,'$b']} (reversed)": function testOptimizeGteReverse(){
|
|
|
|
|
- // var expr = Expression.parseOperand({$gte:[2,'$b']}).optimize();
|
|
|
|
|
- // assert(expr instanceof FieldRangeExpression, "not optimized");
|
|
|
|
|
- // assert.deepEqual(expr.toJSON(), {$lte:['$b',2]});
|
|
|
|
|
- // }
|
|
|
|
|
|
|
+ /** A $gt expression is optimized. */
|
|
|
|
|
+ "OptimizeGt": function OptimizeGt() {
|
|
|
|
|
+ new FieldRangeOptimize({
|
|
|
|
|
+ spec: {$gt:["$b",2]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- // },
|
|
|
|
|
|
|
+ /** A reverse sense $gt expression is optimized. */
|
|
|
|
|
+ "OptimizeGtReverse": function OptimizeGtReverse() {
|
|
|
|
|
+ new FieldRangeOptimize({
|
|
|
|
|
+ spec: {$gt:["$b",2]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
|
|
+ /** A $gte expression is optimized. */
|
|
|
|
|
+ "OptimizeGte": function OptimizeGte() {
|
|
|
|
|
+ new FieldRangeOptimize({
|
|
|
|
|
+ spec: {$gte:["$b",2]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /** A reverse sense $gte expression is optimized. */
|
|
|
|
|
+ "OptimizeGteReverse": function OptimizeGteReverse() {
|
|
|
|
|
+ new FieldRangeOptimize({
|
|
|
|
|
+ spec: {$gte:[2,"$b"]},
|
|
|
|
|
+ }).run();
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
};
|
|
};
|
|
|
-
|
|
|
|
|
-if (!module.parent)(new(require("mocha"))()).ui("exports").reporter("spec").addFile(__filename).run(process.exit);
|
|
|