summaryrefslogtreecommitdiff
path: root/src/mongo/dbtests/expressiontests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/dbtests/expressiontests.cpp')
-rw-r--r--src/mongo/dbtests/expressiontests.cpp1253
1 files changed, 764 insertions, 489 deletions
diff --git a/src/mongo/dbtests/expressiontests.cpp b/src/mongo/dbtests/expressiontests.cpp
index c8149e3f694..ab4cf9c4479 100644
--- a/src/mongo/dbtests/expressiontests.cpp
+++ b/src/mongo/dbtests/expressiontests.cpp
@@ -14,18 +14,51 @@
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
*/
-#include "pch.h"
-
-#include "mongo/db/pipeline/expression.h"
+#include "mongo/pch.h"
#include "mongo/db/pipeline/document.h"
-
-#include "dbtests.h"
+#include "mongo/db/pipeline/expression.h"
+#include "mongo/dbtests/dbtests.h"
namespace ExpressionTests {
+ /** Convert BSONObj to a BSONObj with our $const wrappings. */
+ static BSONObj constify(const BSONObj& obj, bool parentIsArray=false) {
+ BSONObjBuilder bob;
+ for (BSONObjIterator itr(obj); itr.more(); itr.next()) {
+ BSONElement elem = *itr;
+ if (elem.type() == Object) {
+ bob << elem.fieldName() << constify(elem.Obj(), false);
+ }
+ else if (elem.type() == Array && !parentIsArray) {
+ // arrays within arrays are treated as constant values by the real parser
+ bob << elem.fieldName() << BSONArray(constify(elem.Obj(), true));
+ }
+ else if (str::equals(elem.fieldName(), "$const") ||
+ (elem.type() == mongo::String && elem.valuestrsafe()[0] == '$')) {
+ bob.append(elem);
+ }
+ else {
+ bob.append(elem.fieldName(), BSON("$const" << elem));
+ }
+ }
+ return bob.obj();
+ }
+
/** Check binary equality, ensuring use of the same numeric types. */
static void assertBinaryEqual( const BSONObj& expected, const BSONObj& actual ) {
ASSERT_EQUALS( expected, actual );
@@ -41,27 +74,23 @@ namespace ExpressionTests {
/** Convert Expression to BSON. */
static BSONObj expressionToBson( const intrusive_ptr<Expression>& expression ) {
- BSONObjBuilder bob;
- expression->addToBsonObj( &bob, "", true );
- return bob.obj().firstElement().embeddedObject().getOwned();
+ return BSON("" << expression->serialize(false)).firstElement().embeddedObject().getOwned();
}
/** Convert Document to BSON. */
static BSONObj toBson( const Document& document ) {
- BSONObjBuilder bob;
- document->toBson( &bob );
- return bob.obj();
+ return document.toBson();
}
/** Create a Document from a BSONObj. */
Document fromBson( BSONObj obj ) {
- return Document::createFromBsonObj( &obj );
+ return Document(obj);
}
/** Create a Value from a BSONObj. */
Value valueFromBson( BSONObj obj ) {
BSONElement element = obj.firstElement();
- return Value::createFromBsonElement( &element );
+ return Value( element );
}
namespace Add {
@@ -70,7 +99,7 @@ namespace ExpressionTests {
public:
virtual ~ExpectedResultBase() {}
void run() {
- intrusive_ptr<ExpressionNary> expression = ExpressionAdd::create();
+ intrusive_ptr<ExpressionNary> expression = new ExpressionAdd();
populateOperands( expression );
ASSERT_EQUALS( expectedResult(),
toBson( expression->evaluate( Document() ) ) );
@@ -84,8 +113,8 @@ namespace ExpressionTests {
class NullDocument {
public:
void run() {
- intrusive_ptr<ExpressionNary> expression = ExpressionAdd::create();
- expression->addOperand( ExpressionConstant::create( Value::createInt( 2 ) ) );
+ intrusive_ptr<ExpressionNary> expression = new ExpressionAdd();
+ expression->addOperand( ExpressionConstant::create( Value( 2 ) ) );
ASSERT_EQUALS( BSON( "" << 2 ), toBson( expression->evaluate( Document() ) ) );
}
};
@@ -100,8 +129,8 @@ namespace ExpressionTests {
class String {
public:
void run() {
- intrusive_ptr<ExpressionNary> expression = ExpressionAdd::create();
- expression->addOperand( ExpressionConstant::create( Value::createString( "a" ) ) );
+ intrusive_ptr<ExpressionNary> expression = new ExpressionAdd();
+ expression->addOperand( ExpressionConstant::create( Value( "a" ) ) );
ASSERT_THROWS( expression->evaluate( Document() ), UserException );
}
};
@@ -110,7 +139,7 @@ namespace ExpressionTests {
class Bool {
public:
void run() {
- intrusive_ptr<ExpressionNary> expression = ExpressionAdd::create();
+ intrusive_ptr<ExpressionNary> expression = new ExpressionAdd();
expression->addOperand( ExpressionConstant::create( Value(true) ) );
ASSERT_THROWS( expression->evaluate( Document() ), UserException );
}
@@ -269,8 +298,10 @@ namespace ExpressionTests {
void run() {
BSONObj specObject = BSON( "" << spec() );
BSONElement specElement = specObject.firstElement();
- intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement );
- ASSERT_EQUALS( spec(), expressionToBson( expression ) );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ intrusive_ptr<Expression> expression = Expression::parseOperand(specElement, vps);
+ ASSERT_EQUALS( constify( spec() ), expressionToBson( expression ) );
ASSERT_EQUALS( BSON( "" << expectedResult() ),
toBson( expression->evaluate( fromBson( BSON( "a" << 1 ) ) ) ) );
intrusive_ptr<Expression> optimized = expression->optimize();
@@ -289,8 +320,10 @@ namespace ExpressionTests {
void run() {
BSONObj specObject = BSON( "" << spec() );
BSONElement specElement = specObject.firstElement();
- intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement );
- ASSERT_EQUALS( spec(), expressionToBson( expression ) );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ intrusive_ptr<Expression> expression = Expression::parseOperand(specElement, vps);
+ ASSERT_EQUALS( constify( spec() ), expressionToBson( expression ) );
intrusive_ptr<Expression> optimized = expression->optimize();
ASSERT_EQUALS( expectedOptimized(), expressionToBson( optimized ) );
}
@@ -300,7 +333,7 @@ namespace ExpressionTests {
};
class NoOptimizeBase : public OptimizeBase {
- BSONObj expectedOptimized() { return spec(); }
+ BSONObj expectedOptimized() { return constify( spec() ); }
};
/** $and without operands. */
@@ -386,9 +419,16 @@ namespace ExpressionTests {
BSONObj spec() { return BSON( "$and" << BSON_ARRAY( "$a" ) ); }
};
- /** An expression beginning with a single constant is not optimized. SERVER-6192 */
- class ConstantNonConstant : public NoOptimizeBase {
+ /** An expression beginning with a single constant is optimized. */
+ class ConstantNonConstantTrue : public OptimizeBase {
BSONObj spec() { return BSON( "$and" << BSON_ARRAY( 1 << "$a" ) ); }
+ BSONObj expectedOptimized() { return BSON( "$and" << BSON_ARRAY( "$a" ) ); }
+ // note: using $and as serialization of ExpressionCoerceToBool rather than ExpressionAnd
+ };
+
+ class ConstantNonConstantFalse : public OptimizeBase {
+ BSONObj spec() { return BSON( "$and" << BSON_ARRAY( 0 << "$a" ) ); }
+ BSONObj expectedOptimized() { return BSON( "$const" << false ); }
};
/** An expression with a field path and '1'. */
@@ -458,7 +498,7 @@ namespace ExpressionTests {
public:
void run() {
intrusive_ptr<Expression> nested =
- ExpressionConstant::create( Value::createInt( 5 ) );
+ ExpressionConstant::create( Value( 5 ) );
intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create( nested );
ASSERT( expression->evaluate( Document() ).getBool() );
}
@@ -469,7 +509,7 @@ namespace ExpressionTests {
public:
void run() {
intrusive_ptr<Expression> nested =
- ExpressionConstant::create( Value::createInt( 0 ) );
+ ExpressionConstant::create( Value( 0 ) );
intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create( nested );
ASSERT( !expression->evaluate( Document() ).getBool() );
}
@@ -481,10 +521,12 @@ namespace ExpressionTests {
void run() {
intrusive_ptr<Expression> nested = ExpressionFieldPath::create( "a.b" );
intrusive_ptr<Expression> expression = ExpressionCoerceToBool::create( nested );
- set<string> dependencies;
- expression->addDependencies( dependencies );
- ASSERT_EQUALS( 1U, dependencies.size() );
- ASSERT_EQUALS( 1U, dependencies.count( "a.b" ) );
+ DepsTracker dependencies;
+ expression->addDependencies( &dependencies );
+ ASSERT_EQUALS( 1U, dependencies.fields.size() );
+ ASSERT_EQUALS( 1U, dependencies.fields.count( "a.b" ) );
+ ASSERT_EQUALS( false, dependencies.needWholeDocument );
+ ASSERT_EQUALS( false, dependencies.needTextScore );
}
};
@@ -500,10 +542,7 @@ namespace ExpressionTests {
}
private:
static BSONObj toBsonObj(const intrusive_ptr<Expression>& expression) {
-
- BSONObjBuilder bob;
- expression->addToBsonObj(&bob, "field", false);
- return bob.obj();
+ return BSON("field" << expression->serialize(false));
}
};
@@ -520,7 +559,7 @@ namespace ExpressionTests {
private:
static BSONArray toBsonArray(const intrusive_ptr<Expression>& expression) {
BSONArrayBuilder bab;
- expression->addToBsonArray(&bab);
+ bab << expression->serialize(false);
return bab.arr();
}
};
@@ -539,26 +578,23 @@ namespace ExpressionTests {
void run() {
BSONObj specObject = BSON( "" << spec() );
BSONElement specElement = specObject.firstElement();
- intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ intrusive_ptr<Expression> expression = Expression::parseOperand(specElement, vps);
intrusive_ptr<Expression> optimized = expression->optimize();
- ASSERT_EQUALS( expectedFieldRange(),
- (bool)dynamic_pointer_cast<ExpressionFieldRange>( optimized ) );
- ASSERT_EQUALS( expectedOptimized(), expressionToBson( optimized ) );
+ ASSERT_EQUALS( constify( expectedOptimized() ), expressionToBson( optimized ) );
}
protected:
virtual BSONObj spec() = 0;
virtual BSONObj expectedOptimized() = 0;
- virtual bool expectedFieldRange() = 0;
};
class FieldRangeOptimize : public OptimizeBase {
BSONObj expectedOptimized() { return spec(); }
- bool expectedFieldRange() { return true; }
};
class NoOptimize : public OptimizeBase {
BSONObj expectedOptimized() { return spec(); }
- bool expectedFieldRange() { return false; }
};
/** Check expected result for expressions depending on constants. */
@@ -568,9 +604,11 @@ namespace ExpressionTests {
OptimizeBase::run();
BSONObj specObject = BSON( "" << spec() );
BSONElement specElement = specObject.firstElement();
- intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ intrusive_ptr<Expression> expression = Expression::parseOperand(specElement, vps);
// Check expression spec round trip.
- ASSERT_EQUALS( spec(), expressionToBson( expression ) );
+ ASSERT_EQUALS( constify( spec() ), expressionToBson( expression ) );
// Check evaluation result.
ASSERT_EQUALS( expectedResult(),
toBson( expression->evaluate( Document() ) ) );
@@ -586,7 +624,6 @@ namespace ExpressionTests {
virtual BSONObj expectedOptimized() {
return BSON( "$const" << expectedResult().firstElement() );
}
- virtual bool expectedFieldRange() { return false; }
};
class ExpectedTrue : public ExpectedResultBase {
@@ -604,7 +641,9 @@ namespace ExpressionTests {
void run() {
BSONObj specObject = BSON( "" << spec() );
BSONElement specElement = specObject.firstElement();
- ASSERT_THROWS( Expression::parseOperand( &specElement ), UserException );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ ASSERT_THROWS( Expression::parseOperand(specElement, vps), UserException );
}
protected:
virtual BSONObj spec() = 0;
@@ -745,7 +784,9 @@ namespace ExpressionTests {
void run() {
BSONObj specObject = BSON( "" << BSON( "$ne" << BSON_ARRAY( "a" << 1 ) ) );
BSONElement specElement = specObject.firstElement();
- intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ intrusive_ptr<Expression> expression = Expression::parseOperand(specElement, vps);
ASSERT_EQUALS(expression->evaluate(Document()), Value(true));
}
};
@@ -757,7 +798,6 @@ namespace ExpressionTests {
class OptimizeConstants : public OptimizeBase {
BSONObj spec() { return BSON( "$eq" << BSON_ARRAY( 1 << 1 ) ); }
BSONObj expectedOptimized() { return BSON( "$const" << true ); }
- bool expectedFieldRange() { return false; }
};
/** $cmp is not optimized. */
@@ -797,7 +837,6 @@ namespace ExpressionTests {
/** A reverse sense equality expression is optimized. */
class OptimizeEqReverse : public FieldRangeOptimize {
BSONObj spec() { return BSON( "$eq" << BSON_ARRAY( 1 << "$a" ) ); }
- BSONObj expectedOptimized() { return BSON( "$eq" << BSON_ARRAY( "$a" << 1 ) ); }
};
/** A $lt expression is optimized. */
@@ -808,7 +847,6 @@ namespace ExpressionTests {
/** A reverse sense $lt expression is optimized. */
class OptimizeLtReverse : public FieldRangeOptimize {
BSONObj spec() { return BSON( "$lt" << BSON_ARRAY( 1 << "$a" ) ); }
- BSONObj expectedOptimized() { return BSON( "$gt" << BSON_ARRAY( "$a" << 1 ) ); }
};
/** A $lte expression is optimized. */
@@ -819,7 +857,6 @@ namespace ExpressionTests {
/** A reverse sense $lte expression is optimized. */
class OptimizeLteReverse : public FieldRangeOptimize {
BSONObj spec() { return BSON( "$lte" << BSON_ARRAY( 2 << "$b" ) ); }
- BSONObj expectedOptimized() { return BSON( "$gte" << BSON_ARRAY( "$b" << 2 ) ); }
};
/** A $gt expression is optimized. */
@@ -830,7 +867,6 @@ namespace ExpressionTests {
/** A reverse sense $gt expression is optimized. */
class OptimizeGtReverse : public FieldRangeOptimize {
BSONObj spec() { return BSON( "$gt" << BSON_ARRAY( 2 << "$b" ) ); }
- BSONObj expectedOptimized() { return BSON( "$lt" << BSON_ARRAY( "$b" << 2 ) ); }
};
/** A $gte expression is optimized. */
@@ -841,7 +877,6 @@ namespace ExpressionTests {
/** A reverse sense $gte expression is optimized. */
class OptimizeGteReverse : public FieldRangeOptimize {
BSONObj spec() { return BSON( "$gte" << BSON_ARRAY( 2 << "$b" ) ); }
- BSONObj expectedOptimized() { return BSON( "$lte" << BSON_ARRAY( "$b" << 2 ) ); }
};
} // namespace Compare
@@ -853,7 +888,7 @@ namespace ExpressionTests {
public:
void run() {
intrusive_ptr<Expression> expression =
- ExpressionConstant::create( Value::createInt( 5 ) );
+ ExpressionConstant::create( Value( 5 ) );
assertBinaryEqual( BSON( "" << 5 ),
toBson( expression->evaluate( Document() ) ) );
}
@@ -865,8 +900,10 @@ namespace ExpressionTests {
void run() {
BSONObj spec = BSON( "IGNORED_FIELD_NAME" << "foo" );
BSONElement specElement = spec.firstElement();
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
intrusive_ptr<Expression> expression =
- ExpressionConstant::createFromBsonElement( &specElement );
+ ExpressionConstant::parse( specElement, vps );
assertBinaryEqual( BSON( "" << "foo" ),
toBson( expression->evaluate( Document() ) ) );
}
@@ -877,7 +914,7 @@ namespace ExpressionTests {
public:
void run() {
intrusive_ptr<Expression> expression =
- ExpressionConstant::create( Value::createInt( 5 ) );
+ ExpressionConstant::create( Value( 5 ) );
// An attempt to optimize returns the Expression itself.
ASSERT_EQUALS( expression, expression->optimize() );
}
@@ -888,10 +925,12 @@ namespace ExpressionTests {
public:
void run() {
intrusive_ptr<Expression> expression =
- ExpressionConstant::create( Value::createInt( 5 ) );
- set<string> dependencies;
- expression->addDependencies( dependencies );
- ASSERT_EQUALS( 0U, dependencies.size() );
+ ExpressionConstant::create( Value( 5 ) );
+ DepsTracker dependencies;
+ expression->addDependencies( &dependencies );
+ ASSERT_EQUALS( 0U, dependencies.fields.size() );
+ ASSERT_EQUALS( false, dependencies.needWholeDocument );
+ ASSERT_EQUALS( false, dependencies.needTextScore );
}
};
@@ -900,19 +939,14 @@ namespace ExpressionTests {
public:
void run() {
intrusive_ptr<Expression> expression =
- ExpressionConstant::create( Value::createInt( 5 ) );
- // The constant is copied out as is.
- assertBinaryEqual( BSON( "field" << 5 ), toBsonObj( expression, false ) );
+ ExpressionConstant::create( Value( 5 ) );
// The constant is replaced with a $ expression.
assertBinaryEqual( BSON( "field" << BSON( "$const" << 5 ) ),
- toBsonObj( expression, true ) );
+ toBsonObj( expression ) );
}
private:
- static BSONObj toBsonObj( const intrusive_ptr<Expression>& expression,
- bool requireExpression ) {
- BSONObjBuilder bob;
- expression->addToBsonObj( &bob, "field", requireExpression );
- return bob.obj();
+ static BSONObj toBsonObj( const intrusive_ptr<Expression>& expression ) {
+ return BSON("field" << expression->serialize(false));
}
};
@@ -921,14 +955,14 @@ namespace ExpressionTests {
public:
void run() {
intrusive_ptr<Expression> expression =
- ExpressionConstant::create( Value::createInt( 5 ) );
+ ExpressionConstant::create( Value( 5 ) );
// The constant is copied out as is.
- assertBinaryEqual( BSON_ARRAY( 5 ), toBsonArray( expression ) );
+ assertBinaryEqual( constify( BSON_ARRAY( 5 ) ), toBsonArray( expression ) );
}
private:
static BSONObj toBsonArray( const intrusive_ptr<Expression>& expression ) {
BSONArrayBuilder bab;
- expression->addToBsonArray( &bab );
+ bab << expression->serialize(false);
return bab.obj();
}
};
@@ -960,10 +994,12 @@ namespace ExpressionTests {
public:
void run() {
intrusive_ptr<Expression> expression = ExpressionFieldPath::create( "a.b" );
- set<string> dependencies;
- expression->addDependencies( dependencies );
- ASSERT_EQUALS( 1U, dependencies.size() );
- ASSERT_EQUALS( 1U, dependencies.count( "a.b" ) );
+ DepsTracker dependencies;
+ expression->addDependencies( &dependencies );
+ ASSERT_EQUALS( 1U, dependencies.fields.size() );
+ ASSERT_EQUALS( 1U, dependencies.fields.count( "a.b" ) );
+ ASSERT_EQUALS( false, dependencies.needWholeDocument );
+ ASSERT_EQUALS( false, dependencies.needTextScore );
}
};
@@ -1144,9 +1180,8 @@ namespace ExpressionTests {
public:
void run() {
intrusive_ptr<Expression> expression = ExpressionFieldPath::create( "a.b.c" );
- BSONObjBuilder bob;
- expression->addToBsonObj( &bob, "foo", false );
- assertBinaryEqual( BSON( "foo" << "$a.b.c" ), bob.obj() );
+ assertBinaryEqual(BSON("foo" << "$a.b.c"),
+ BSON("foo" << expression->serialize(false)));
}
};
@@ -1156,237 +1191,60 @@ namespace ExpressionTests {
void run() {
intrusive_ptr<Expression> expression = ExpressionFieldPath::create( "a.b.c" );
BSONArrayBuilder bab;
- expression->addToBsonArray( &bab );
+ bab << expression->serialize(false);
assertBinaryEqual( BSON_ARRAY( "$a.b.c" ), bab.arr() );
}
};
} // namespace FieldPath
- namespace FieldRange {
-
- // Much of ExpressionFieldRange's functionality is not reachable in mongo 2.2. and some of
- // it does not work properly. These tests generally focus portions of ExpressionFieldRange
- // that are reachable in mongo 2.2.
-
- class CheckResultBase {
- public:
- virtual ~CheckResultBase() {
- }
- void run() {
- intrusive_ptr<ExpressionFieldRange> expression =
- ExpressionFieldRange::create( mongo::ExpressionFieldPath::create( "a" ),
- compareOp(), valueFromBson( value() ) );
- ASSERT_EQUALS( expectedSpec(), expressionToBson( expression ) );
- ASSERT_EQUALS( toBson( expectedResult() ? Value(true) : Value(false) ),
- toBson( expression->evaluate( fromBson( sourceDocument() ) ) ) );
- }
- protected:
- virtual Expression::CmpOp compareOp() = 0;
- virtual BSONObj expectedSpec() = 0;
- virtual BSONObj value() = 0;
- virtual BSONObj sourceDocument() = 0;
- virtual bool expectedResult() = 0;
- };
-
- class EqBase : public CheckResultBase {
- Expression::CmpOp compareOp() { return Expression::EQ; }
- BSONObj expectedSpec() { return BSON( "$eq" << BSON_ARRAY( "$a" << 1 ) ); }
- BSONObj value() { return BSON( "" << 1 ); }
- };
-
- /** $eq operator with 'a' < value. */
- class EqLt : public EqBase {
- BSONObj sourceDocument() { return BSON( "a" << 0 ); }
- bool expectedResult() { return false; }
- };
-
- /** $eq operator with 'a' == value. */
- class EqEq : public EqBase {
- BSONObj sourceDocument() { return BSON( "a" << 1 ); }
- bool expectedResult() { return true; }
- };
-
- /** $eq operator with 'a' > value. */
- class EqGt : public EqBase {
- BSONObj sourceDocument() { return BSON( "a" << 2 ); }
- bool expectedResult() { return false; }
- };
-
- class LtBase : public CheckResultBase {
- Expression::CmpOp compareOp() { return Expression::LT; }
- BSONObj expectedSpec() { return BSON( "$lt" << BSON_ARRAY( "$a" << "y" ) ); }
- BSONObj value() { return BSON( "" << "y" ); }
- };
-
- /** $lt operator with 'a' < value. */
- class LtLt : public LtBase {
- BSONObj sourceDocument() { return BSON( "a" << "x" ); }
- bool expectedResult() { return true; }
- };
-
- /** $lt operator with 'a' == value. */
- class LtEq : public LtBase {
- BSONObj sourceDocument() { return BSON( "a" << "y" ); }
- bool expectedResult() { return false; }
- };
-
- /** $lt operator with 'a' > value. */
- class LtGt : public LtBase {
- BSONObj sourceDocument() { return BSON( "a" << "z" ); }
- bool expectedResult() { return false; }
- };
-
- class LteBase : public CheckResultBase {
- Expression::CmpOp compareOp() { return Expression::LTE; }
- BSONObj expectedSpec() { return BSON( "$lte" << BSON_ARRAY( "$a" << 1.1 ) ); }
- BSONObj value() { return BSON( "" << 1.1 ); }
- };
-
- /** $lte operator with 'a' < value. */
- class LteLt : public LteBase {
- BSONObj sourceDocument() { return BSON( "a" << 1.0 ); }
- bool expectedResult() { return true; }
- };
-
- /** $lte operator with 'a' == value. */
- class LteEq : public LteBase {
- BSONObj sourceDocument() { return BSON( "a" << 1.1 ); }
- bool expectedResult() { return true; }
- };
-
- /** $lte operator with 'a' > value. */
- class LteGt : public LteBase {
- BSONObj sourceDocument() { return BSON( "a" << 1.2 ); }
- bool expectedResult() { return false; }
- };
-
- class GtBase : public CheckResultBase {
- Expression::CmpOp compareOp() { return Expression::GT; }
- BSONObj expectedSpec() { return BSON( "$gt" << BSON_ARRAY( "$a" << 100 ) ); }
- BSONObj value() { return BSON( "" << 100 ); }
- };
-
- /** $gt operator with 'a' < value. */
- class GtLt : public GtBase {
- BSONObj sourceDocument() { return BSON( "a" << 50 ); }
- bool expectedResult() { return false; }
- };
-
- /** $gt operator with 'a' == value. */
- class GtEq : public GtBase {
- BSONObj sourceDocument() { return BSON( "a" << 100 ); }
- bool expectedResult() { return false; }
- };
-
- /** $gt operator with 'a' > value. */
- class GtGt : public GtBase {
- BSONObj sourceDocument() { return BSON( "a" << 150 ); }
- bool expectedResult() { return true; }
- };
-
- class GteBase : public CheckResultBase {
- Expression::CmpOp compareOp() { return Expression::GTE; }
- BSONObj expectedSpec() { return BSON( "$gte" << BSON_ARRAY( "$a" << "abc" ) ); }
- BSONObj value() { return BSON( "" << "abc" ); }
- };
-
- /** $gte operator with 'a' < value. */
- class GteLt : public GteBase {
- BSONObj sourceDocument() { return BSON( "a" << "a" ); }
- bool expectedResult() { return false; }
- };
-
- /** $gte operator with 'a' == value. */
- class GteEq : public GteBase {
- BSONObj sourceDocument() { return BSON( "a" << "abc" ); }
- bool expectedResult() { return true; }
- };
-
- /** $gte operator with 'a' > value. */
- class GteGt : public GteBase {
- BSONObj sourceDocument() { return BSON( "a" << "abcd" ); }
- bool expectedResult() { return true; }
- };
-
- /** The FieldRange's FieldPath is a dependency. */
- class Dependencies {
- public:
- void run() {
- intrusive_ptr<ExpressionFieldRange> expression =
- ExpressionFieldRange::create( mongo::ExpressionFieldPath::create( "a.b.c" ),
- Expression::EQ, Value(0) );
- set<string> dependencies;
- expression->addDependencies( dependencies );
- ASSERT_EQUALS( 1U, dependencies.size() );
- ASSERT_EQUALS( 1U, dependencies.count( "a.b.c" ) );
- }
- };
-
- /** Comparison is performed for multikey values rather than set-containment. */
- class Multikey {
- public:
- void run() {
- intrusive_ptr<ExpressionFieldRange> expression =
- ExpressionFieldRange::create( mongo::ExpressionFieldPath::create( "a" ),
- Expression::EQ, Value(0) );
- Document document =
- fromBson( BSON( "a" << BSON_ARRAY( 1 << 0 << 2 ) ) );
- ASSERT_EQUALS(expression->evaluate(document), Value(false));
- }
- };
-
- } // namespace FieldRange
namespace Nary {
/** A dummy child of ExpressionNary used for testing. */
class Testable : public ExpressionNary {
public:
- virtual Value evaluate(const Document& pDocument) const {
+ virtual Value evaluateInternal(Variables* vars) const {
// Just put all the values in a list. This is not associative/commutative so
// the results will change if a factory is provided and operations are reordered.
vector<Value> values;
for( ExpressionVector::const_iterator i = vpOperand.begin(); i != vpOperand.end();
++i ) {
- values.push_back( (*i)->evaluate( pDocument ) );
+ values.push_back( (*i)->evaluateInternal(vars) );
}
- return Value::createArray( values );
+ return Value( values );
}
virtual const char* getOpName() const { return "$testable"; }
- virtual intrusive_ptr<ExpressionNary> (*getFactory() const)() {
- return _haveFactory ? factory : NULL;
+ virtual bool isAssociativeAndCommutative() const {
+ return _isAssociativeAndCommutative;
}
- static intrusive_ptr<Testable> create( bool haveFactory = false ) {
- return new Testable( haveFactory );
+ static intrusive_ptr<Testable> create( bool associativeAndCommutative = false ) {
+ return new Testable(associativeAndCommutative);
}
static intrusive_ptr<ExpressionNary> factory() {
- return new Testable( true );
+ return new Testable(true);
}
static intrusive_ptr<Testable> createFromOperands( const BSONArray& operands,
bool haveFactory = false ) {
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
intrusive_ptr<Testable> testable = create( haveFactory );
BSONObjIterator i( operands );
while( i.more() ) {
BSONElement element = i.next();
- testable->addOperand( Expression::parseOperand( &element ) );
+ testable->addOperand( Expression::parseOperand(element, vps) );
}
return testable;
}
void assertContents( const BSONArray& expectedContents ) {
- ASSERT_EQUALS( BSON( "$testable" << expectedContents ), expressionToBson( this ) );
- }
- void checkArgLimit( unsigned maxArgs ) const {
- return ExpressionNary::checkArgLimit( maxArgs );
- }
- void checkArgCount( unsigned reqArgs ) const {
- return ExpressionNary::checkArgCount( reqArgs );
+ ASSERT_EQUALS( constify( BSON( "$testable" << expectedContents ) ), expressionToBson( this ) );
}
+
private:
- Testable( bool haveFactory ) :
- _haveFactory( haveFactory ) {
- }
- bool _haveFactory;
+ Testable(bool isAssociativeAndCommutative)
+ : _isAssociativeAndCommutative(isAssociativeAndCommutative)
+ {}
+ bool _isAssociativeAndCommutative;
};
/** Adding operands to the expression. */
@@ -1394,59 +1252,13 @@ namespace ExpressionTests {
public:
void run() {
intrusive_ptr<Testable> testable = Testable::create();
- testable->addOperand( ExpressionConstant::create( Value::createInt( 9 ) ) );
+ testable->addOperand( ExpressionConstant::create( Value( 9 ) ) );
testable->assertContents( BSON_ARRAY( 9 ) );
testable->addOperand( ExpressionFieldPath::create( "ab.c" ) );
testable->assertContents( BSON_ARRAY( 9 << "$ab.c" ) );
}
};
- /** Checking the max number of operands. */
- class CheckArgLimit {
- public:
- void run() {
- intrusive_ptr<Testable> testable = Testable::create();
-
- // No arguments.
- testable->checkArgLimit( 1 ); // No assertion.
-
- // One argument.
- testable->addOperand( ExpressionConstant::create( Value::createInt( 1 ) ) );
- ASSERT_THROWS( testable->checkArgLimit( 1 ), UserException );
- testable->checkArgLimit( 2 ); // No assertion.
-
- // Two arguments.
- testable->addOperand( ExpressionConstant::create( Value::createInt( 2 ) ) );
- ASSERT_THROWS( testable->checkArgLimit( 1 ), UserException );
- ASSERT_THROWS( testable->checkArgLimit( 2 ), UserException );
- testable->checkArgLimit( 3 ); // No assertion.
- }
- };
-
- /** Checking the expected count of operands. */
- class CheckArgCount {
- public:
- void run() {
- intrusive_ptr<Testable> testable = Testable::create();
-
- // No arguments.
- testable->checkArgCount( 0 ); // No assertion.
- ASSERT_THROWS( testable->checkArgCount( 1 ), UserException );
-
- // One argument.
- testable->addOperand( ExpressionConstant::create( Value::createInt( 1 ) ) );
- ASSERT_THROWS( testable->checkArgCount( 0 ), UserException );
- testable->checkArgCount( 1 ); // No assertion.
- ASSERT_THROWS( testable->checkArgCount( 2 ), UserException );
-
- // Two arguments.
- testable->addOperand( ExpressionConstant::create( Value::createInt( 2 ) ) );
- ASSERT_THROWS( testable->checkArgCount( 1 ), UserException );
- testable->checkArgCount( 2 ); // No assertion.
- ASSERT_THROWS( testable->checkArgCount( 3 ), UserException );
- }
- };
-
/** Dependencies of the expression. */
class Dependencies {
public:
@@ -1457,7 +1269,7 @@ namespace ExpressionTests {
assertDependencies( BSONArray(), testable );
// Add a constant argument.
- testable->addOperand( ExpressionConstant::create( Value::createInt( 1 ) ) );
+ testable->addOperand( ExpressionConstant::create( Value( 1 ) ) );
assertDependencies( BSONArray(), testable );
// Add a field path argument.
@@ -1468,20 +1280,25 @@ namespace ExpressionTests {
BSONObj spec = BSON( "" << BSON( "a" << "$x" << "q" << "$r" ) );
BSONElement specElement = spec.firstElement();
Expression::ObjectCtx ctx( Expression::ObjectCtx::DOCUMENT_OK );
- testable->addOperand( Expression::parseObject( &specElement, &ctx ) );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ testable->addOperand( Expression::parseObject(specElement.Obj(), &ctx, vps) );
assertDependencies( BSON_ARRAY( "ab.c" << "r" << "x" ), testable );
}
private:
void assertDependencies( const BSONArray& expectedDependencies,
const intrusive_ptr<Expression>& expression ) {
- set<string> dependencies;
- expression->addDependencies( dependencies );
+ DepsTracker dependencies;
+ expression->addDependencies( &dependencies );
BSONArrayBuilder dependenciesBson;
- for( set<string>::const_iterator i = dependencies.begin(); i != dependencies.end();
- ++i ) {
+ for( set<string>::const_iterator i = dependencies.fields.begin();
+ i != dependencies.fields.end();
+ ++i ) {
dependenciesBson << *i;
}
ASSERT_EQUALS( expectedDependencies, dependenciesBson.arr() );
+ ASSERT_EQUALS( false, dependencies.needWholeDocument );
+ ASSERT_EQUALS( false, dependencies.needTextScore );
}
};
@@ -1490,22 +1307,9 @@ namespace ExpressionTests {
public:
void run() {
intrusive_ptr<Testable> testable = Testable::create();
- testable->addOperand( ExpressionConstant::create( Value::createInt( 5 ) ) );
- // requireExpression == false
- {
- BSONObjBuilder bob;
- testable->addToBsonObj( &bob, "foo", false );
- ASSERT_EQUALS( BSON( "foo" << BSON( "$testable" << BSON_ARRAY( 5 ) ) ),
- bob.obj() );
- }
- // requireExpression == true
- if ( 0 ) { // SERVER-6769
- BSONObjBuilder bob;
- testable->addToBsonObj( &bob, "foo", true );
- ASSERT_EQUALS( BSON( "foo" << BSON( "$testable" << BSON_ARRAY
- ( BSON( "$const" << 5 ) ) ) ),
- bob.obj() );
- }
+ testable->addOperand( ExpressionConstant::create( Value( 5 ) ) );
+ ASSERT_EQUALS(BSON("foo" << BSON("$testable" << BSON_ARRAY(BSON("$const" << 5)))),
+ BSON("foo" << testable->serialize(false)));
}
};
@@ -1514,10 +1318,9 @@ namespace ExpressionTests {
public:
void run() {
intrusive_ptr<Testable> testable = Testable::create();
- testable->addOperand( ExpressionConstant::create( Value::createInt( 5 ) ) );
- BSONArrayBuilder bab;
- testable->addToBsonArray( &bab );
- ASSERT_EQUALS( BSON_ARRAY( BSON( "$testable" << BSON_ARRAY( 5 ) ) ), bab.arr() );
+ testable->addOperand( ExpressionConstant::create( Value( 5 ) ) );
+ ASSERT_EQUALS(constify(BSON_ARRAY(BSON("$testable" << BSON_ARRAY(5)))),
+ BSON_ARRAY(testable->serialize(false)));
}
};
@@ -1589,11 +1392,9 @@ namespace ExpressionTests {
intrusive_ptr<Testable> testable =
Testable::createFromOperands( BSON_ARRAY( 55 << 66 << "$path" ), true );
intrusive_ptr<Expression> optimized = testable->optimize();
- // Optimization generates a new expression.
- ASSERT( testable != optimized );
// The constant expressions are evaluated separately and placed at the end.
- ASSERT_EQUALS( BSON( "$testable"
- << BSON_ARRAY( "$path" << BSON_ARRAY( 55 << 66 ) ) ),
+ ASSERT_EQUALS( constify( BSON( "$testable"
+ << BSON_ARRAY( "$path" << BSON_ARRAY( 55 << 66 ) ) ) ),
expressionToBson( optimized ) );
}
};
@@ -1615,15 +1416,14 @@ namespace ExpressionTests {
( Testable::createFromOperands
( BSON_ARRAY( 99 << 100 << "$another_path" ), true ) );
intrusive_ptr<Expression> optimized = testable->optimize();
- ASSERT( testable != optimized );
ASSERT_EQUALS
- ( BSON( "$testable" <<
+ ( constify( BSON( "$testable" <<
BSON_ARRAY( // non constant parts
"$path" <<
BSON( "$add" << BSON_ARRAY( "$q" << 11 ) ) <<
"$another_path" <<
// constant part last
- BSON_ARRAY( 55 << 66 << BSON_ARRAY( 99 << 100 ) ) ) ),
+ BSON_ARRAY( 55 << 66 << BSON_ARRAY( 99 << 100 ) ) ) ) ),
expressionToBson( optimized ) );
}
};
@@ -1640,13 +1440,12 @@ namespace ExpressionTests {
( Testable::createFromOperands( BSON_ARRAY( 5 << 6 << "$c" ), true ) );
top->addOperand( nested );
intrusive_ptr<Expression> optimized = top->optimize();
- ASSERT( top != optimized );
ASSERT_EQUALS
- ( BSON( "$testable" <<
+ ( constify( BSON( "$testable" <<
BSON_ARRAY( "$a" << "$b" << "$c" <<
BSON_ARRAY( 1 << 2 <<
BSON_ARRAY( 3 << 4 <<
- BSON_ARRAY( 5 << 6 ) ) ) ) ),
+ BSON_ARRAY( 5 << 6 ) ) ) ) ) ),
expressionToBson( optimized ) );
}
};
@@ -1660,15 +1459,18 @@ namespace ExpressionTests {
void assertDependencies( const BSONArray& expectedDependencies,
const intrusive_ptr<ExpressionObject>& expression,
bool includePath = true ) const {
- set<string> dependencies;
vector<string> path;
- expression->addDependencies( dependencies, includePath ? &path : 0 );
+ DepsTracker dependencies;
+ expression->addDependencies( &dependencies, includePath ? &path : 0 );
BSONArrayBuilder bab;
- for( set<string>::const_iterator i = dependencies.begin(); i != dependencies.end();
- ++i ) {
+ for( set<string>::const_iterator i = dependencies.fields.begin();
+ i != dependencies.fields.end();
+ ++i ) {
bab << *i;
}
ASSERT_EQUALS( expectedDependencies, bab.arr() );
+ ASSERT_EQUALS( false, dependencies.needWholeDocument );
+ ASSERT_EQUALS( false, dependencies.needTextScore );
}
};
@@ -1677,11 +1479,12 @@ namespace ExpressionTests {
virtual ~ExpectedResultBase() {
}
void run() {
- _expression = ExpressionObject::create();
+ _expression = ExpressionObject::createRoot();
prepareExpression();
Document document = fromBson( source() );
MutableDocument result;
- expression()->addToDocument( result, document, document );
+ Variables vars(0, document);
+ expression()->addToDocument( result, document, &vars );
assertBinaryEqual( expected(), toBson( result.freeze() ) );
assertDependencies( expectedDependencies(), _expression );
ASSERT_EQUALS( expectedBsonRepresentation(), expressionToBson( _expression ) );
@@ -1885,7 +1688,7 @@ namespace ExpressionTests {
}
void prepareExpression() {
expression()->addField( mongo::FieldPath( "a" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
+ ExpressionConstant::create( Value( 5 ) ) );
}
BSONObj expected() { return BSON( "_id" << 0 << "a" << 5 ); }
BSONArray expectedDependencies() { return BSON_ARRAY( "_id" ); }
@@ -1910,7 +1713,7 @@ namespace ExpressionTests {
}
void prepareExpression() {
expression()->addField( mongo::FieldPath( "a" ),
- ExpressionConstant::create( Value(mongo::Undefined) ) );
+ ExpressionConstant::create( Value(BSONUndefined) ) );
}
BSONObj expected() { return BSON( "_id" << 0 << "a" << BSONUndefined); }
BSONArray expectedDependencies() { return BSON_ARRAY( "_id" ); }
@@ -1935,7 +1738,7 @@ namespace ExpressionTests {
}
void prepareExpression() {
expression()->addField( mongo::FieldPath( "a" ),
- ExpressionConstant::create( Value(mongo::jstNULL) ) );
+ ExpressionConstant::create( Value(BSONNULL) ) );
}
BSONObj expected() { return BSON( "_id" << 0 << "a" << BSONNULL ); }
BSONArray expectedDependencies() { return BSON_ARRAY( "_id" ); }
@@ -1951,7 +1754,7 @@ namespace ExpressionTests {
virtual BSONObj source() { return BSON( "_id" << 0 ); }
void prepareExpression() {
expression()->addField( mongo::FieldPath( "a.b" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
+ ExpressionConstant::create( Value( 5 ) ) );
}
BSONObj expected() { return BSON( "_id" << 0 << "a" << BSON( "b" << 5 ) ); }
BSONArray expectedDependencies() { return BSON_ARRAY( "_id" ); }
@@ -2020,7 +1823,7 @@ namespace ExpressionTests {
// Create a sub expression returning an empty object.
intrusive_ptr<ExpressionObject> subExpression = ExpressionObject::create();
subExpression->addField( mongo::FieldPath( "b" ),
- ExpressionConstant::create( Value::createInt( 6 ) ) );
+ ExpressionConstant::create( Value( 6 ) ) );
expression()->addField( mongo::FieldPath( "a" ), subExpression );
}
BSONObj expected() { return BSON( "_id" << 0 << "a" << BSON( "b" << 6 ) ); }
@@ -2039,9 +1842,9 @@ namespace ExpressionTests {
}
void prepareExpression() {
expression()->addField( mongo::FieldPath( "a.b" ),
- ExpressionConstant::create( Value::createInt( 6 ) ) );
+ ExpressionConstant::create( Value( 6 ) ) );
expression()->addField( mongo::FieldPath( "a.c" ),
- ExpressionConstant::create( Value::createInt( 7 ) ) );
+ ExpressionConstant::create( Value( 7 ) ) );
}
BSONObj expected() { return BSON( "_id" << 0 << "a" << BSON( "b" << 6 << "c" << 7 ) ); }
BSONArray expectedDependencies() { return BSON_ARRAY( "_id" ); }
@@ -2055,10 +1858,10 @@ namespace ExpressionTests {
class AdjacentDottedAndNestedComputedFields : public AdjacentDottedComputedFields {
void prepareExpression() {
expression()->addField( mongo::FieldPath( "a.b" ),
- ExpressionConstant::create( Value::createInt( 6 ) ) );
+ ExpressionConstant::create( Value( 6 ) ) );
intrusive_ptr<ExpressionObject> subExpression = ExpressionObject::create();
subExpression->addField( mongo::FieldPath( "c" ),
- ExpressionConstant::create( Value::createInt( 7 ) ) );
+ ExpressionConstant::create( Value( 7 ) ) );
expression()->addField( mongo::FieldPath( "a" ), subExpression );
}
};
@@ -2068,10 +1871,10 @@ namespace ExpressionTests {
void prepareExpression() {
intrusive_ptr<ExpressionObject> subExpression = ExpressionObject::create();
subExpression->addField( mongo::FieldPath( "b" ),
- ExpressionConstant::create( Value::createInt( 6 ) ) );
+ ExpressionConstant::create( Value( 6 ) ) );
expression()->addField( mongo::FieldPath( "a" ), subExpression );
expression()->addField( mongo::FieldPath( "a.c" ),
- ExpressionConstant::create( Value::createInt( 7 ) ) );
+ ExpressionConstant::create( Value( 7 ) ) );
}
};
@@ -2080,12 +1883,12 @@ namespace ExpressionTests {
void prepareExpression() {
intrusive_ptr<ExpressionObject> firstSubExpression = ExpressionObject::create();
firstSubExpression->addField( mongo::FieldPath( "b" ),
- ExpressionConstant::create( Value::createInt( 6 ) ) );
+ ExpressionConstant::create( Value( 6 ) ) );
expression()->addField( mongo::FieldPath( "a" ), firstSubExpression );
intrusive_ptr<ExpressionObject> secondSubExpression = ExpressionObject::create();
secondSubExpression->addField( mongo::FieldPath( "c" ),
ExpressionConstant::create
- ( Value::createInt( 7 ) ) );
+ ( Value( 7 ) ) );
expression()->addField( mongo::FieldPath( "a" ), secondSubExpression );
}
};
@@ -2098,13 +1901,13 @@ namespace ExpressionTests {
}
void prepareExpression() {
expression()->addField( mongo::FieldPath( "a.b" ),
- ExpressionConstant::create( Value::createInt( 6 ) ) );
+ ExpressionConstant::create( Value( 6 ) ) );
intrusive_ptr<ExpressionObject> subExpression = ExpressionObject::create();
// Add field 'd' then 'c'. Expect the same field ordering in the result doc.
subExpression->addField( mongo::FieldPath( "d" ),
- ExpressionConstant::create( Value::createInt( 7 ) ) );
+ ExpressionConstant::create( Value( 7 ) ) );
subExpression->addField( mongo::FieldPath( "c" ),
- ExpressionConstant::create( Value::createInt( 8 ) ) );
+ ExpressionConstant::create( Value( 8 ) ) );
expression()->addField( mongo::FieldPath( "a" ), subExpression );
}
BSONObj expected() {
@@ -2125,10 +1928,10 @@ namespace ExpressionTests {
}
void prepareExpression() {
expression()->addField( mongo::FieldPath( "a.b.c" ),
- ExpressionConstant::create( Value::createInt( 6 ) ) );
+ ExpressionConstant::create( Value( 6 ) ) );
intrusive_ptr<ExpressionObject> bSubExpression = ExpressionObject::create();
bSubExpression->addField( mongo::FieldPath( "d" ),
- ExpressionConstant::create( Value::createInt( 7 ) ) );
+ ExpressionConstant::create( Value( 7 ) ) );
intrusive_ptr<ExpressionObject> aSubExpression = ExpressionObject::create();
aSubExpression->addField( mongo::FieldPath( "b" ), bSubExpression );
expression()->addField( mongo::FieldPath( "a" ), aSubExpression );
@@ -2147,12 +1950,12 @@ namespace ExpressionTests {
class ConflictingExpressionFields : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->addField( mongo::FieldPath( "a" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
+ ExpressionConstant::create( Value( 5 ) ) );
ASSERT_THROWS( expression->addField( mongo::FieldPath( "a" ), // Duplicate field.
ExpressionConstant::create
- ( Value::createInt( 6 ) ) ),
+ ( Value( 6 ) ) ),
UserException );
}
};
@@ -2161,11 +1964,11 @@ namespace ExpressionTests {
class ConflictingInclusionExpressionFields : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->includePath( "a" );
ASSERT_THROWS( expression->addField( mongo::FieldPath( "a" ),
ExpressionConstant::create
- ( Value::createInt( 6 ) ) ),
+ ( Value( 6 ) ) ),
UserException );
}
};
@@ -2174,9 +1977,9 @@ namespace ExpressionTests {
class ConflictingExpressionInclusionFields : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->addField( mongo::FieldPath( "a" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
+ ExpressionConstant::create( Value( 5 ) ) );
ASSERT_THROWS( expression->includePath( "a" ),
UserException );
}
@@ -2186,13 +1989,13 @@ namespace ExpressionTests {
class ConflictingObjectConstantExpressionFields : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
intrusive_ptr<ExpressionObject> subExpression = ExpressionObject::create();
subExpression->includePath( "b" );
expression->addField( mongo::FieldPath( "a" ), subExpression );
ASSERT_THROWS( expression->addField( mongo::FieldPath( "a.b" ),
ExpressionConstant::create
- ( Value::createInt( 6 ) ) ),
+ ( Value( 6 ) ) ),
UserException );
}
};
@@ -2201,9 +2004,9 @@ namespace ExpressionTests {
class ConflictingConstantObjectExpressionFields : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->addField( mongo::FieldPath( "a.b" ),
- ExpressionConstant::create( Value::createInt( 6 ) ) );
+ ExpressionConstant::create( Value( 6 ) ) );
intrusive_ptr<ExpressionObject> subExpression = ExpressionObject::create();
subExpression->includePath( "b" );
ASSERT_THROWS( expression->addField( mongo::FieldPath( "a" ), subExpression ),
@@ -2215,12 +2018,12 @@ namespace ExpressionTests {
class ConflictingNestedFields : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->addField( mongo::FieldPath( "a.b" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
+ ExpressionConstant::create( Value( 5 ) ) );
ASSERT_THROWS( expression->addField( mongo::FieldPath( "a.b" ), // Duplicate field.
ExpressionConstant::create
- ( Value::createInt( 6 ) ) ),
+ ( Value( 6 ) ) ),
UserException );
}
};
@@ -2229,12 +2032,12 @@ namespace ExpressionTests {
class ConflictingFieldAndSubfield : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->addField( mongo::FieldPath( "a" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
+ ExpressionConstant::create( Value( 5 ) ) );
ASSERT_THROWS( expression->addField( mongo::FieldPath( "a.b" ),
ExpressionConstant::create
- ( Value::createInt( 5 ) ) ),
+ ( Value( 5 ) ) ),
UserException );
}
};
@@ -2243,12 +2046,12 @@ namespace ExpressionTests {
class ConflictingFieldAndNestedField : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->addField( mongo::FieldPath( "a" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
+ ExpressionConstant::create( Value( 5 ) ) );
intrusive_ptr<ExpressionObject> subExpression = ExpressionObject::create();
subExpression->addField( mongo::FieldPath( "b" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
+ ExpressionConstant::create( Value( 5 ) ) );
ASSERT_THROWS( expression->addField( mongo::FieldPath( "a" ), subExpression ),
UserException );
}
@@ -2258,12 +2061,12 @@ namespace ExpressionTests {
class ConflictingSubfieldAndField : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->addField( mongo::FieldPath( "a.b" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
+ ExpressionConstant::create( Value( 5 ) ) );
ASSERT_THROWS( expression->addField( mongo::FieldPath( "a" ),
ExpressionConstant::create
- ( Value::createInt( 5 ) ) ),
+ ( Value( 5 ) ) ),
UserException );
}
};
@@ -2272,14 +2075,14 @@ namespace ExpressionTests {
class ConflictingNestedFieldAndField : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
intrusive_ptr<ExpressionObject> subExpression = ExpressionObject::create();
subExpression->addField( mongo::FieldPath( "b" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
+ ExpressionConstant::create( Value( 5 ) ) );
expression->addField( mongo::FieldPath( "a" ), subExpression );
ASSERT_THROWS( expression->addField( mongo::FieldPath( "a" ),
ExpressionConstant::create
- ( Value::createInt( 5 ) ) ),
+ ( Value( 5 ) ) ),
UserException );
}
};
@@ -2288,9 +2091,9 @@ namespace ExpressionTests {
class NonInclusionDependencies : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->addField( mongo::FieldPath( "a" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
+ ExpressionConstant::create( Value( 5 ) ) );
assertDependencies( BSON_ARRAY( "_id" ), expression, true );
assertDependencies( BSONArray(), expression, false );
expression->addField( mongo::FieldPath( "b" ),
@@ -2304,12 +2107,12 @@ namespace ExpressionTests {
class InclusionDependencies : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->includePath( "a" );
assertDependencies( BSON_ARRAY( "_id" << "a" ), expression, true );
- set<string> unused;
+ DepsTracker unused;
// 'path' must be provided for inclusion expressions.
- ASSERT_THROWS( expression->addDependencies( unused ), UserException );
+ ASSERT_THROWS( expression->addDependencies( &unused ), UserException );
}
};
@@ -2317,11 +2120,12 @@ namespace ExpressionTests {
class Optimize : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
// Add inclusion.
expression->includePath( "a" );
// Add non inclusion.
- expression->addField( mongo::FieldPath( "b" ), ExpressionAnd::create() );
+ intrusive_ptr<Expression> andExpr = new ExpressionAnd();
+ expression->addField( mongo::FieldPath( "b" ), andExpr );
expression->optimize();
// Optimizing 'expression' optimizes its non inclusion sub expressions, while
// inclusion sub expressions are passed through.
@@ -2334,12 +2138,11 @@ namespace ExpressionTests {
class AddToBsonObj : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->addField( mongo::FieldPath( "a" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
- BSONObjBuilder bob;
- expression->addToBsonObj( &bob, "foo", false );
- ASSERT_EQUALS( BSON( "foo" << BSON( "a" << 5 ) ), bob.obj() );
+ ExpressionConstant::create( Value( 5 ) ) );
+ ASSERT_EQUALS(constify(BSON("foo" << BSON("a" << 5))),
+ BSON("foo" << expression->serialize(false)));
}
};
@@ -2347,12 +2150,11 @@ namespace ExpressionTests {
class AddToBsonObjRequireExpression : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->addField( mongo::FieldPath( "a" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
- BSONObjBuilder bob;
- expression->addToBsonObj( &bob, "foo", true );
- ASSERT_EQUALS( BSON( "foo" << BSON( "a" << BSON( "$const" << 5 ) ) ), bob.obj() );
+ ExpressionConstant::create( Value( 5 ) ) );
+ ASSERT_EQUALS(BSON("foo" << BSON("a" << BSON("$const" << 5))),
+ BSON("foo" << expression->serialize(false)));
}
};
@@ -2360,12 +2162,12 @@ namespace ExpressionTests {
class AddToBsonArray : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->addField( mongo::FieldPath( "a" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
+ ExpressionConstant::create( Value( 5 ) ) );
BSONArrayBuilder bab;
- expression->addToBsonArray( &bab );
- ASSERT_EQUALS( BSON_ARRAY( BSON( "a" << 5 ) ), bab.arr() );
+ bab << expression->serialize(false);
+ ASSERT_EQUALS( constify( BSON_ARRAY( BSON( "a" << 5 ) ) ), bab.arr() );
}
};
@@ -2377,10 +2179,10 @@ namespace ExpressionTests {
class Evaluate : public Base {
public:
void run() {
- intrusive_ptr<ExpressionObject> expression = ExpressionObject::create();
+ intrusive_ptr<ExpressionObject> expression = ExpressionObject::createRoot();
expression->includePath( "a" );
expression->addField( mongo::FieldPath( "b" ),
- ExpressionConstant::create( Value::createInt( 5 ) ) );
+ ExpressionConstant::create( Value( 5 ) ) );
expression->addField( mongo::FieldPath( "c" ),
ExpressionFieldPath::create( "a" ) );
ASSERT_EQUALS( BSON( "b" << 5 << "c" << 1 ),
@@ -2401,8 +2203,10 @@ namespace ExpressionTests {
void run() {
BSONObj specObject = BSON( "" << spec() );
BSONElement specElement = specObject.firstElement();
- intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement );
- ASSERT_EQUALS( spec(), expressionToBson( expression ) );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ intrusive_ptr<Expression> expression = Expression::parseOperand(specElement, vps);
+ ASSERT_EQUALS( constify( spec() ), expressionToBson( expression ) );
ASSERT_EQUALS( BSON( "" << expectedResult() ),
toBson( expression->evaluate( fromBson( BSON( "a" << 1 ) ) ) ) );
intrusive_ptr<Expression> optimized = expression->optimize();
@@ -2421,8 +2225,10 @@ namespace ExpressionTests {
void run() {
BSONObj specObject = BSON( "" << spec() );
BSONElement specElement = specObject.firstElement();
- intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement );
- ASSERT_EQUALS( spec(), expressionToBson( expression ) );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ intrusive_ptr<Expression> expression = Expression::parseOperand(specElement, vps);
+ ASSERT_EQUALS( constify( spec() ), expressionToBson( expression ) );
intrusive_ptr<Expression> optimized = expression->optimize();
ASSERT_EQUALS( expectedOptimized(), expressionToBson( optimized ) );
}
@@ -2432,7 +2238,7 @@ namespace ExpressionTests {
};
class NoOptimizeBase : public OptimizeBase {
- BSONObj expectedOptimized() { return spec(); }
+ BSONObj expectedOptimized() { return constify( spec() ); }
};
/** $or without operands. */
@@ -2518,9 +2324,17 @@ namespace ExpressionTests {
BSONObj spec() { return BSON( "$or" << BSON_ARRAY( "$a" ) ); }
};
- /** An expression beginning with a single constant is not optimized. SERVER-6192 */
- class ConstantNonConstant : public NoOptimizeBase {
+ /** An expression beginning with a single constant is optimized. */
+ class ConstantNonConstantTrue : public OptimizeBase {
BSONObj spec() { return BSON( "$or" << BSON_ARRAY( 1 << "$a" ) ); }
+ BSONObj expectedOptimized() { return BSON( "$const" << true ); }
+ };
+
+ /** An expression beginning with a single constant is optimized. */
+ class ConstantNonConstantFalse : public OptimizeBase {
+ BSONObj spec() { return BSON( "$or" << BSON_ARRAY( 0 << "$a" ) ); }
+ BSONObj expectedOptimized() { return BSON( "$and" << BSON_ARRAY("$a") ); }
+ // note: using $and as serialization of ExpressionCoerceToBool rather than ExpressionAnd
};
/** An expression with a field path and '1'. */
@@ -2594,8 +2408,10 @@ namespace ExpressionTests {
BSONObj specObject = BSON( "" << spec() );
BSONElement specElement = specObject.firstElement();
Expression::ObjectCtx context = objectCtx();
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
intrusive_ptr<Expression> expression =
- Expression::parseObject( &specElement, &context );
+ Expression::parseObject( specElement.Obj(), &context, vps );
ASSERT_EQUALS( expectedBson(), expressionToBson( expression ) );
}
protected:
@@ -2603,7 +2419,7 @@ namespace ExpressionTests {
virtual Expression::ObjectCtx objectCtx() {
return Expression::ObjectCtx( Expression::ObjectCtx::DOCUMENT_OK );
}
- virtual BSONObj expectedBson() { return spec(); }
+ virtual BSONObj expectedBson() { return constify( spec() ); }
};
class ParseError {
@@ -2613,7 +2429,9 @@ namespace ExpressionTests {
BSONObj specObject = BSON( "" << spec() );
BSONElement specElement = specObject.firstElement();
Expression::ObjectCtx context = objectCtx();
- ASSERT_THROWS( Expression::parseObject( &specElement, &context ),
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ ASSERT_THROWS( Expression::parseObject( specElement.Obj(), &context, vps ),
UserException );
}
protected:
@@ -2631,7 +2449,9 @@ namespace ExpressionTests {
BSONElement specElement = specObject.firstElement();
Expression::ObjectCtx context =
Expression::ObjectCtx( Expression::ObjectCtx::DOCUMENT_OK );
- ASSERT_THROWS( Expression::parseObject( &specElement, &context ),
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ ASSERT_THROWS( Expression::parseObject( specElement.Obj(), &context, vps ),
UserException );
}
};
@@ -2799,13 +2619,15 @@ namespace ExpressionTests {
void run() {
BSONObj specObject = spec();
BSONElement specElement = specObject.firstElement();
- intrusive_ptr<Expression> expression =
- Expression::parseExpression( specElement.fieldName(), &specElement );
- ASSERT_EQUALS( expectedBson(), expressionToBson( expression ) );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ intrusive_ptr<Expression> expression = Expression::parseExpression(specElement,
+ vps);
+ ASSERT_EQUALS( constify( expectedBson() ), expressionToBson( expression ) );
}
protected:
virtual BSONObj spec() = 0;
- virtual BSONObj expectedBson() { return spec(); }
+ virtual BSONObj expectedBson() { return constify( spec() ); }
};
class ParseError {
@@ -2814,9 +2636,9 @@ namespace ExpressionTests {
void run() {
BSONObj specObject = spec();
BSONElement specElement = specObject.firstElement();
- ASSERT_THROWS( Expression::parseExpression
- ( specElement.fieldName(), &specElement ),
- UserException );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ ASSERT_THROWS(Expression::parseExpression(specElement, vps), UserException);
}
protected:
virtual BSONObj spec() = 0;
@@ -2874,9 +2696,10 @@ namespace ExpressionTests {
BSONObj expectedBson() { return BSON( "$not" << BSON_ARRAY( 1 ) ); }
};
- /** An object (apparently) can't be provided as a singleton argument. */
- class ObjectSingleton : public ParseError {
+ /** An object can be provided as a singleton argument. */
+ class ObjectSingleton : public Base {
BSONObj spec() { return BSON( "$and" << BSON( "$const" << 1 ) ); }
+ BSONObj expectedBson() { return BSON("$and" << BSON_ARRAY(BSON("$const" << 1))); }
};
/** An object can be provided as an array agrument. */
@@ -2895,13 +2718,15 @@ namespace ExpressionTests {
void run() {
BSONObj specObject = spec();
BSONElement specElement = specObject.firstElement();
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
intrusive_ptr<mongo::Expression> expression =
- mongo::Expression::parseOperand( &specElement );
+ mongo::Expression::parseOperand(specElement, vps);
ASSERT_EQUALS( expectedBson(), expressionToBson( expression ) );
}
protected:
virtual BSONObj spec() = 0;
- virtual BSONObj expectedBson() { return spec(); }
+ virtual BSONObj expectedBson() { return constify( spec() ); }
};
class ParseError {
@@ -2910,7 +2735,9 @@ namespace ExpressionTests {
void run() {
BSONObj specObject = spec();
BSONElement specElement = specObject.firstElement();
- ASSERT_THROWS( mongo::Expression::parseOperand( &specElement ), UserException );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ ASSERT_THROWS(mongo::Expression::parseOperand(specElement, vps), UserException);
}
protected:
virtual BSONObj spec() = 0;
@@ -2922,11 +2749,11 @@ namespace ExpressionTests {
void run() {
BSONObj specObject = BSON( "" << "$field" );
BSONElement specElement = specObject.firstElement();
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
intrusive_ptr<mongo::Expression> expression =
- mongo::Expression::parseOperand( &specElement );
- BSONObjBuilder bob;
- expression->addToBsonObj( &bob, "", true );
- ASSERT_EQUALS( specObject, bob.obj() );
+ mongo::Expression::parseOperand(specElement, vps);
+ ASSERT_EQUALS(specObject, BSON("" << expression->serialize(false)));
}
};
@@ -2957,6 +2784,330 @@ namespace ExpressionTests {
} // namespace Parse
+ namespace Set {
+ Value sortSet(Value set) {
+ if (set.nullish()) {
+ return Value(BSONNULL);
+ }
+ vector<Value> sortedSet = set.getArray();
+ std::sort(sortedSet.begin(), sortedSet.end());
+ return Value(sortedSet);
+ }
+
+ class ExpectedResultBase {
+ public:
+ virtual ~ExpectedResultBase() {}
+ void run() {
+ const Document spec = getSpec();
+ const Value args = spec["input"];
+ if (!spec["expected"].missing()) {
+ FieldIterator fields(spec["expected"].getDocument());
+ while (fields.more()) {
+ const Document::FieldPair field(fields.next());
+ const Value expected = field.second;
+ const BSONObj obj = BSON(field.first << args);
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ const intrusive_ptr<Expression> expr =
+ Expression::parseExpression(obj.firstElement(), vps);
+ Value result = expr->evaluate(Document());
+ if (result.getType() == Array) {
+ result = sortSet(result);
+ }
+ if (result != expected) {
+ string errMsg = str::stream()
+ << "for expression " << field.first.toString()
+ << " with argument " << args.toString()
+ << " full tree: " << expr->serialize(false).toString()
+ << " expected: " << expected.toString()
+ << " but got: " << result.toString();
+ FAIL(errMsg);
+ }
+ //TODO test optimize here
+ }
+ }
+ if (!spec["error"].missing()) {
+ const vector<Value>& asserters = spec["error"].getArray();
+ size_t n = asserters.size();
+ for (size_t i = 0; i < n; i++) {
+ const BSONObj obj = BSON(asserters[i].getString() << args);
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ ASSERT_THROWS({
+ // NOTE: parse and evaluatation failures are treated the same
+ const intrusive_ptr<Expression> expr =
+ Expression::parseExpression(obj.firstElement(), vps);
+ expr->evaluate(Document());
+ }, UserException);
+ }
+ }
+ }
+ private:
+ virtual Document getSpec() = 0;
+ };
+
+ class Same : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1 << 2)
+ << DOC_ARRAY(1 << 2) )
+ << "expected" << DOC("$setIsSubset" << true
+ << "$setEquals" << true
+ << "$setIntersection" << DOC_ARRAY(1 << 2)
+ << "$setUnion" << DOC_ARRAY(1 << 2)
+ << "$setDifference" << vector<Value>() )
+ );
+
+ }
+ };
+
+ class Redundant : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1 << 2)
+ << DOC_ARRAY(1 << 2 << 2) )
+ << "expected" << DOC("$setIsSubset" << true
+ << "$setEquals" << true
+ << "$setIntersection" << DOC_ARRAY(1 << 2)
+ << "$setUnion" << DOC_ARRAY(1 << 2)
+ << "$setDifference" << vector<Value>() )
+ );
+
+ }
+ };
+
+ class DoubleRedundant : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1 << 1 << 2)
+ << DOC_ARRAY(1 << 2 << 2) )
+ << "expected" << DOC("$setIsSubset" << true
+ << "$setEquals" << true
+ << "$setIntersection" << DOC_ARRAY(1 << 2)
+ << "$setUnion" << DOC_ARRAY(1 << 2)
+ << "$setDifference" << vector<Value>() )
+ );
+
+ }
+ };
+
+ class Super : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1 << 2)
+ << DOC_ARRAY(1) )
+ << "expected" << DOC("$setIsSubset" << false
+ << "$setEquals" << false
+ << "$setIntersection" << DOC_ARRAY(1)
+ << "$setUnion" << DOC_ARRAY(1 << 2)
+ << "$setDifference" << DOC_ARRAY(2) )
+ );
+
+ }
+ };
+
+ class SuperWithRedundant : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1 << 2 << 2)
+ << DOC_ARRAY(1) )
+ << "expected" << DOC("$setIsSubset" << false
+ << "$setEquals" << false
+ << "$setIntersection" << DOC_ARRAY(1)
+ << "$setUnion" << DOC_ARRAY(1 << 2)
+ << "$setDifference" << DOC_ARRAY(2) )
+ );
+
+ }
+ };
+
+ class Sub : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1)
+ << DOC_ARRAY(1 << 2) )
+ << "expected" << DOC("$setIsSubset" << true
+ << "$setEquals" << false
+ << "$setIntersection" << DOC_ARRAY(1)
+ << "$setUnion" << DOC_ARRAY(1 << 2)
+ << "$setDifference" << vector<Value>() )
+ );
+
+ }
+ };
+
+ class SameBackwards : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1 << 2)
+ << DOC_ARRAY(2 << 1) )
+ << "expected" << DOC("$setIsSubset" << true
+ << "$setEquals" << true
+ << "$setIntersection" << DOC_ARRAY(1 << 2)
+ << "$setUnion" << DOC_ARRAY(1 << 2)
+ << "$setDifference" << vector<Value>() )
+ );
+
+ }
+ };
+
+ class NoOverlap : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1 << 2)
+ << DOC_ARRAY(8 << 4) )
+ << "expected" << DOC("$setIsSubset" << false
+ << "$setEquals" << false
+ << "$setIntersection" << vector<Value>()
+ << "$setUnion" << DOC_ARRAY(1 << 2 << 4 << 8)
+ << "$setDifference" << DOC_ARRAY(1 << 2))
+ );
+
+ }
+ };
+
+ class Overlap : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1 << 2)
+ << DOC_ARRAY(8 << 2 << 4) )
+ << "expected" << DOC("$setIsSubset" << false
+ << "$setEquals" << false
+ << "$setIntersection" << DOC_ARRAY(2)
+ << "$setUnion" << DOC_ARRAY(1 << 2 << 4 << 8)
+ << "$setDifference" << DOC_ARRAY(1))
+ );
+
+ }
+ };
+
+ class LastNull : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1 << 2)
+ << Value(BSONNULL) )
+ << "expected" << DOC("$setIntersection" << BSONNULL
+ << "$setUnion" << BSONNULL
+ << "$setDifference" << BSONNULL )
+ << "error" << DOC_ARRAY("$setEquals"
+ << "$setIsSubset")
+ );
+
+ }
+ };
+
+ class FirstNull : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( Value(BSONNULL)
+ << DOC_ARRAY(1 << 2) )
+ << "expected" << DOC("$setIntersection" << BSONNULL
+ << "$setUnion" << BSONNULL
+ << "$setDifference" << BSONNULL )
+ << "error" << DOC_ARRAY("$setEquals"
+ << "$setIsSubset")
+ );
+
+ }
+ };
+
+ class NoArg : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << vector<Value>()
+ << "expected" << DOC("$setIntersection" << vector<Value>()
+ << "$setUnion" << vector<Value>() )
+ << "error" << DOC_ARRAY("$setEquals"
+ << "$setIsSubset"
+ << "$setDifference")
+ );
+
+ }
+ };
+
+ class OneArg : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1 << 2) )
+ << "expected" << DOC("$setIntersection" << DOC_ARRAY(1 << 2)
+ << "$setUnion" << DOC_ARRAY(1 << 2) )
+ << "error" << DOC_ARRAY("$setEquals"
+ << "$setIsSubset"
+ << "$setDifference")
+ );
+
+ }
+ };
+
+ class EmptyArg : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( vector<Value>() )
+ << "expected" << DOC("$setIntersection" << vector<Value>()
+ << "$setUnion" << vector<Value>() )
+ << "error" << DOC_ARRAY("$setEquals"
+ << "$setIsSubset"
+ << "$setDifference")
+ );
+
+ }
+ };
+
+ class LeftArgEmpty : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( vector<Value>()
+ << DOC_ARRAY(1 << 2) )
+ << "expected" << DOC("$setIntersection" << vector<Value>()
+ << "$setUnion" << DOC_ARRAY(1 << 2)
+ << "$setIsSubset" << true
+ << "$setEquals" << false
+ << "$setDifference" << vector<Value>() )
+ );
+
+ }
+ };
+
+ class RightArgEmpty : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1 << 2)
+ << vector<Value>() )
+ << "expected" << DOC("$setIntersection" << vector<Value>()
+ << "$setUnion" << DOC_ARRAY(1 << 2)
+ << "$setIsSubset" << false
+ << "$setEquals" << false
+ << "$setDifference" << DOC_ARRAY(1 << 2) )
+ );
+
+ }
+ };
+
+ class ManyArgs : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(8 << 3)
+ << DOC_ARRAY("asdf" << "foo")
+ << DOC_ARRAY(80.3 << 34)
+ << vector<Value>()
+ << DOC_ARRAY(80.3 << "foo" << 11 << "yay") )
+ << "expected" << DOC("$setIntersection" << vector<Value>()
+ << "$setEquals" << false
+ << "$setUnion" << DOC_ARRAY(3
+ << 8
+ << 11
+ << 34
+ << 80.3
+ << "asdf"
+ << "foo"
+ << "yay") )
+ << "error" << DOC_ARRAY("$setIsSubset"
+ << "$setDifference")
+ );
+
+ }
+ };
+
+ class ManyArgsEqual : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1 << 2 << 4)
+ << DOC_ARRAY(1 << 2 << 2 << 4)
+ << DOC_ARRAY(4 << 1 << 2)
+ << DOC_ARRAY(2 << 1 << 1 << 4) )
+ << "expected" << DOC("$setIntersection" << DOC_ARRAY(1 << 2 << 4)
+ << "$setEquals" << true
+ << "$setUnion" << DOC_ARRAY(1 << 2 << 4) )
+ << "error" << DOC_ARRAY("$setIsSubset"
+ << "$setDifference")
+ );
+
+ }
+ };
+ } // namespace Set
+
namespace Strcasecmp {
class ExpectedResultBase {
@@ -2977,8 +3128,10 @@ namespace ExpressionTests {
void assertResult( int expectedResult, const BSONObj& spec ) {
BSONObj specObj = BSON( "" << spec );
BSONElement specElement = specObj.firstElement();
- intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement );
- ASSERT_EQUALS( spec, expressionToBson( expression ) );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ intrusive_ptr<Expression> expression = Expression::parseOperand(specElement, vps);
+ ASSERT_EQUALS( constify( spec ), expressionToBson( expression ) );
ASSERT_EQUALS( BSON( "" << expectedResult ),
toBson( expression->evaluate( Document() ) ) );
}
@@ -3025,8 +3178,10 @@ namespace ExpressionTests {
void run() {
BSONObj specObj = BSON( "" << spec() );
BSONElement specElement = specObj.firstElement();
- intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement );
- ASSERT_EQUALS( spec(), expressionToBson( expression ) );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ intrusive_ptr<Expression> expression = Expression::parseOperand(specElement, vps);
+ ASSERT_EQUALS( constify( spec() ), expressionToBson( expression ) );
ASSERT_EQUALS( BSON( "" << expectedResult() ),
toBson( expression->evaluate( Document() ) ) );
}
@@ -3092,8 +3247,10 @@ namespace ExpressionTests {
void run() {
BSONObj specObj = BSON( "" << spec() );
BSONElement specElement = specObj.firstElement();
- intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement );
- ASSERT_EQUALS( spec(), expressionToBson( expression ) );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ intrusive_ptr<Expression> expression = Expression::parseOperand(specElement, vps);
+ ASSERT_EQUALS( constify( spec() ), expressionToBson( expression ) );
ASSERT_EQUALS( BSON( "" << expectedResult() ),
toBson( expression->evaluate( Document() ) ) );
}
@@ -3135,8 +3292,10 @@ namespace ExpressionTests {
void run() {
BSONObj specObj = BSON( "" << spec() );
BSONElement specElement = specObj.firstElement();
- intrusive_ptr<Expression> expression = Expression::parseOperand( &specElement );
- ASSERT_EQUALS( spec(), expressionToBson( expression ) );
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ intrusive_ptr<Expression> expression = Expression::parseOperand(specElement, vps);
+ ASSERT_EQUALS( constify( spec() ), expressionToBson( expression ) );
ASSERT_EQUALS( BSON( "" << expectedResult() ),
toBson( expression->evaluate( Document() ) ) );
}
@@ -3169,6 +3328,114 @@ namespace ExpressionTests {
} // namespace ToUpper
+ namespace AllAnyElements {
+ class ExpectedResultBase {
+ public:
+ virtual ~ExpectedResultBase() {}
+ void run() {
+ const Document spec = getSpec();
+ const Value args = spec["input"];
+ if (!spec["expected"].missing()) {
+ FieldIterator fields(spec["expected"].getDocument());
+ while (fields.more()) {
+ const Document::FieldPair field(fields.next());
+ const Value expected = field.second;
+ const BSONObj obj = BSON(field.first << args);
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ const intrusive_ptr<Expression> expr =
+ Expression::parseExpression(obj.firstElement(), vps);
+ const Value result = expr->evaluate(Document());
+ if (result != expected) {
+ string errMsg = str::stream()
+ << "for expression " << field.first.toString()
+ << " with argument " << args.toString()
+ << " full tree: " << expr->serialize(false).toString()
+ << " expected: " << expected.toString()
+ << " but got: " << result.toString();
+ FAIL(errMsg);
+ }
+ //TODO test optimize here
+ }
+ }
+ if (!spec["error"].missing()) {
+ const vector<Value>& asserters = spec["error"].getArray();
+ size_t n = asserters.size();
+ for (size_t i = 0; i < n; i++) {
+ const BSONObj obj = BSON(asserters[i].getString() << args);
+ VariablesIdGenerator idGenerator;
+ VariablesParseState vps(&idGenerator);
+ ASSERT_THROWS({
+ // NOTE: parse and evaluatation failures are treated the same
+ const intrusive_ptr<Expression> expr =
+ Expression::parseExpression(obj.firstElement(), vps);
+ expr->evaluate(Document());
+ }, UserException);
+ }
+ }
+ }
+ private:
+ virtual Document getSpec() = 0;
+ };
+
+ class JustFalse : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(false) )
+ << "expected" << DOC("$allElementsTrue" << false
+ << "$anyElementTrue" << false) );
+ }
+ };
+
+ class JustTrue : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(true) )
+ << "expected" << DOC("$allElementsTrue" << true
+ << "$anyElementTrue" << true) );
+ }
+ };
+
+ class OneTrueOneFalse : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(true << false) )
+ << "expected" << DOC("$allElementsTrue" << false
+ << "$anyElementTrue" << true) );
+ }
+ };
+
+ class Empty : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( vector<Value>() )
+ << "expected" << DOC("$allElementsTrue" << true
+ << "$anyElementTrue" << false) );
+ }
+ };
+
+ class TrueViaInt : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(1) )
+ << "expected" << DOC("$allElementsTrue" << true
+ << "$anyElementTrue" << true) );
+ }
+ };
+
+ class FalseViaInt : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY( DOC_ARRAY(0) )
+ << "expected" << DOC("$allElementsTrue" << false
+ << "$anyElementTrue" << false) );
+ }
+ };
+
+ class Null : public ExpectedResultBase {
+ Document getSpec() {
+ return DOC("input" << DOC_ARRAY(BSONNULL)
+ << "error" << DOC_ARRAY("$allElementsTrue"
+ << "$anyElementTrue") );
+ }
+ };
+
+ } // namespace AllAnyElements
+
class All : public Suite {
public:
All() : Suite( "expression" ) {
@@ -3210,7 +3477,8 @@ namespace ExpressionTests {
add<And::FieldPath>();
add<And::OptimizeConstantExpression>();
add<And::NonConstant>();
- add<And::ConstantNonConstant>();
+ add<And::ConstantNonConstantTrue>();
+ add<And::ConstantNonConstantFalse>();
add<And::NonConstantOne>();
add<And::NonConstantZero>();
add<And::NonConstantNonConstantOne>();
@@ -3298,27 +3566,7 @@ namespace ExpressionTests {
add<FieldPath::AddToBsonObj>();
add<FieldPath::AddToBsonArray>();
- add<FieldRange::EqLt>();
- add<FieldRange::EqEq>();
- add<FieldRange::EqGt>();
- add<FieldRange::LtLt>();
- add<FieldRange::LtEq>();
- add<FieldRange::LtGt>();
- add<FieldRange::LteLt>();
- add<FieldRange::LteEq>();
- add<FieldRange::LteGt>();
- add<FieldRange::GtLt>();
- add<FieldRange::GtEq>();
- add<FieldRange::GtGt>();
- add<FieldRange::GteLt>();
- add<FieldRange::GteEq>();
- add<FieldRange::GteGt>();
- add<FieldRange::Dependencies>();
- add<FieldRange::Multikey>();
-
add<Nary::AddOperand>();
- add<Nary::CheckArgLimit>();
- add<Nary::CheckArgCount>();
add<Nary::Dependencies>();
add<Nary::AddToBsonObj>();
add<Nary::AddToBsonArray>();
@@ -3392,7 +3640,8 @@ namespace ExpressionTests {
add<Or::FieldPath>();
add<Or::OptimizeConstantExpression>();
add<Or::NonConstant>();
- add<Or::ConstantNonConstant>();
+ add<Or::ConstantNonConstantTrue>();
+ add<Or::ConstantNonConstantFalse>();
add<Or::NonConstantOne>();
add<Or::NonConstantZero>();
add<Or::NonConstantNonConstantOne>();
@@ -3464,6 +3713,32 @@ namespace ExpressionTests {
add<ToUpper::NullBegin>();
add<ToUpper::NullMiddle>();
add<ToUpper::NullEnd>();
+
+ add<Set::Same>();
+ add<Set::Redundant>();
+ add<Set::DoubleRedundant>();
+ add<Set::Sub>();
+ add<Set::Super>();
+ add<Set::SameBackwards>();
+ add<Set::NoOverlap>();
+ add<Set::Overlap>();
+ add<Set::FirstNull>();
+ add<Set::LastNull>();
+ add<Set::NoArg>();
+ add<Set::OneArg>();
+ add<Set::EmptyArg>();
+ add<Set::LeftArgEmpty>();
+ add<Set::RightArgEmpty>();
+ add<Set::ManyArgs>();
+ add<Set::ManyArgsEqual>();
+
+ add<AllAnyElements::JustFalse>();
+ add<AllAnyElements::JustTrue>();
+ add<AllAnyElements::OneTrueOneFalse>();
+ add<AllAnyElements::Empty>();
+ add<AllAnyElements::TrueViaInt>();
+ add<AllAnyElements::FalseViaInt>();
+ add<AllAnyElements::Null>();
}
} myall;