summaryrefslogtreecommitdiff
path: root/src/mongo/db/matcher/expression_tree_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/matcher/expression_tree_test.cpp')
-rw-r--r--src/mongo/db/matcher/expression_tree_test.cpp571
1 files changed, 571 insertions, 0 deletions
diff --git a/src/mongo/db/matcher/expression_tree_test.cpp b/src/mongo/db/matcher/expression_tree_test.cpp
new file mode 100644
index 00000000000..c0ba4cead87
--- /dev/null
+++ b/src/mongo/db/matcher/expression_tree_test.cpp
@@ -0,0 +1,571 @@
+/**
+ * Copyright (C) 2012 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * 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.
+ */
+
+/** Unit tests for MatchMatchExpression operator implementations in match_operators.{h,cpp}. */
+
+#include "mongo/unittest/unittest.h"
+
+#include "mongo/db/jsobj.h"
+#include "mongo/db/json.h"
+#include "mongo/db/matcher/expression.h"
+#include "mongo/db/matcher/expression_tree.h"
+#include "mongo/db/matcher/expression_leaf.h"
+
+namespace mongo {
+
+ TEST( NotMatchExpression, MatchesScalar ) {
+ BSONObj baseOperand = BSON( "$lt" << 5 );
+ auto_ptr<ComparisonMatchExpression> lt( new LTMatchExpression() );
+ ASSERT( lt->init( "a", baseOperand[ "$lt" ] ).isOK() );
+ NotMatchExpression notOp;
+ ASSERT( notOp.init( lt.release() ).isOK() );
+ ASSERT( notOp.matchesBSON( BSON( "a" << 6 ), NULL ) );
+ ASSERT( !notOp.matchesBSON( BSON( "a" << 4 ), NULL ) );
+ }
+
+ TEST( NotMatchExpression, MatchesArray ) {
+ BSONObj baseOperand = BSON( "$lt" << 5 );
+ auto_ptr<ComparisonMatchExpression> lt( new LTMatchExpression() );
+ ASSERT( lt->init( "a", baseOperand[ "$lt" ] ).isOK() );
+ NotMatchExpression notOp;
+ ASSERT( notOp.init( lt.release() ).isOK() );
+ ASSERT( notOp.matchesBSON( BSON( "a" << BSON_ARRAY( 6 ) ), NULL ) );
+ ASSERT( !notOp.matchesBSON( BSON( "a" << BSON_ARRAY( 4 ) ), NULL ) );
+ // All array elements must match.
+ ASSERT( !notOp.matchesBSON( BSON( "a" << BSON_ARRAY( 4 << 5 << 6 ) ), NULL ) );
+ }
+
+ TEST( NotMatchExpression, ElemMatchKey ) {
+ BSONObj baseOperand = BSON( "$lt" << 5 );
+ auto_ptr<ComparisonMatchExpression> lt( new LTMatchExpression() );
+ ASSERT( lt->init( "a", baseOperand[ "$lt" ] ).isOK() );
+ NotMatchExpression notOp;
+ ASSERT( notOp.init( lt.release() ).isOK() );
+ MatchDetails details;
+ details.requestElemMatchKey();
+ ASSERT( !notOp.matchesBSON( BSON( "a" << BSON_ARRAY( 1 ) ), &details ) );
+ ASSERT( !details.hasElemMatchKey() );
+ ASSERT( notOp.matchesBSON( BSON( "a" << 6 ), &details ) );
+ ASSERT( !details.hasElemMatchKey() );
+ ASSERT( notOp.matchesBSON( BSON( "a" << BSON_ARRAY( 6 ) ), &details ) );
+ // elemMatchKey is not implemented for negative match operators.
+ ASSERT( !details.hasElemMatchKey() );
+ }
+ /*
+ TEST( NotMatchExpression, MatchesIndexKey ) {
+ BSONObj baseOperand = BSON( "$lt" << 5 );
+ auto_ptr<ComparisonMatchExpression> lt( new ComparisonMatchExpression() );
+ ASSERT( lt->init( "a", baseOperand[ "$lt" ] ).isOK() );
+ NotMatchExpression notOp;
+ ASSERT( notOp.init( lt.release() ).isOK() );
+ IndexSpec indexSpec( BSON( "a" << 1 ) );
+ BSONObj indexKey = BSON( "" << "7" );
+ ASSERT( MatchMatchExpression::PartialMatchResult_Unknown ==
+ notOp.matchesIndexKey( indexKey, indexSpec ) );
+ }
+ */
+
+ /**
+ TEST( AndOp, MatchesElementSingleClause ) {
+ BSONObj baseOperand = BSON( "$lt" << 5 );
+ BSONObj match = BSON( "a" << 4 );
+ BSONObj notMatch = BSON( "a" << 5 );
+ auto_ptr<ComparisonMatchExpression> lt( new ComparisonMatchExpression() );
+ ASSERT( lt->init( "", baseOperand[ "$lt" ] ).isOK() );
+ OwnedPointerVector<MatchMatchExpression> subMatchExpressions;
+ subMatchExpressions.mutableVector().push_back( lt.release() );
+ AndOp andOp;
+ ASSERT( andOp.init( &subMatchExpressions ).isOK() );
+ ASSERT( andOp.matchesSingleElement( match[ "a" ] ) );
+ ASSERT( !andOp.matchesSingleElement( notMatch[ "a" ] ) );
+ }
+ */
+
+ TEST( AndOp, NoClauses ) {
+ AndMatchExpression andMatchExpression;
+ ASSERT( andMatchExpression.matchesBSON( BSONObj(), NULL ) );
+ }
+
+ TEST( AndOp, MatchesElementThreeClauses ) {
+ BSONObj baseOperand1 = BSON( "$lt" << "z1" );
+ BSONObj baseOperand2 = BSON( "$gt" << "a1" );
+ BSONObj match = BSON( "a" << "r1" );
+ BSONObj notMatch1 = BSON( "a" << "z1" );
+ BSONObj notMatch2 = BSON( "a" << "a1" );
+ BSONObj notMatch3 = BSON( "a" << "r" );
+
+ auto_ptr<ComparisonMatchExpression> sub1( new LTMatchExpression() );
+ ASSERT( sub1->init( "a", baseOperand1[ "$lt" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub2( new GTMatchExpression() );
+ ASSERT( sub2->init( "a", baseOperand2[ "$gt" ] ).isOK() );
+ auto_ptr<RegexMatchExpression> sub3( new RegexMatchExpression() );
+ ASSERT( sub3->init( "a", "1", "" ).isOK() );
+
+ AndMatchExpression andOp;
+ andOp.add( sub1.release() );
+ andOp.add( sub2.release() );
+ andOp.add( sub3.release() );
+
+ ASSERT( andOp.matchesBSON( match ) );
+ ASSERT( !andOp.matchesBSON( notMatch1 ) );
+ ASSERT( !andOp.matchesBSON( notMatch2 ) );
+ ASSERT( !andOp.matchesBSON( notMatch3 ) );
+ }
+
+ TEST( AndOp, MatchesSingleClause ) {
+ BSONObj baseOperand = BSON( "$ne" << 5 );
+ auto_ptr<ComparisonMatchExpression> eq( new EqualityMatchExpression() );
+ ASSERT( eq->init( "a", baseOperand[ "$ne" ] ).isOK() );
+ auto_ptr<NotMatchExpression> ne( new NotMatchExpression() );
+ ASSERT( ne->init( eq.release() ).isOK() );
+
+ AndMatchExpression andOp;
+ andOp.add( ne.release() );
+
+ ASSERT( andOp.matchesBSON( BSON( "a" << 4 ), NULL ) );
+ ASSERT( andOp.matchesBSON( BSON( "a" << BSON_ARRAY( 4 << 6 ) ), NULL ) );
+ ASSERT( !andOp.matchesBSON( BSON( "a" << 5 ), NULL ) );
+ ASSERT( !andOp.matchesBSON( BSON( "a" << BSON_ARRAY( 4 << 5 ) ), NULL ) );
+ }
+
+ TEST( AndOp, MatchesThreeClauses ) {
+ BSONObj baseOperand1 = BSON( "$gt" << 1 );
+ BSONObj baseOperand2 = BSON( "$lt" << 10 );
+ BSONObj baseOperand3 = BSON( "$lt" << 100 );
+
+ auto_ptr<ComparisonMatchExpression> sub1( new GTMatchExpression() );
+ ASSERT( sub1->init( "a", baseOperand1[ "$gt" ] ).isOK() );
+
+ auto_ptr<ComparisonMatchExpression> sub2( new LTMatchExpression() );
+ ASSERT( sub2->init( "a", baseOperand2[ "$lt" ] ).isOK() );
+
+ auto_ptr<ComparisonMatchExpression> sub3( new LTMatchExpression() );
+ ASSERT( sub3->init( "b", baseOperand3[ "$lt" ] ).isOK() );
+
+ AndMatchExpression andOp;
+ andOp.add( sub1.release() );
+ andOp.add( sub2.release() );
+ andOp.add( sub3.release() );
+
+ ASSERT( andOp.matchesBSON( BSON( "a" << 5 << "b" << 6 ), NULL ) );
+ ASSERT( !andOp.matchesBSON( BSON( "a" << 5 ), NULL ) );
+ ASSERT( !andOp.matchesBSON( BSON( "b" << 6 ), NULL ) );
+ ASSERT( !andOp.matchesBSON( BSON( "a" << 1 << "b" << 6 ), NULL ) );
+ ASSERT( !andOp.matchesBSON( BSON( "a" << 10 << "b" << 6 ), NULL ) );
+ }
+
+ TEST( AndOp, ElemMatchKey ) {
+ BSONObj baseOperand1 = BSON( "a" << 1 );
+ BSONObj baseOperand2 = BSON( "b" << 2 );
+
+ auto_ptr<ComparisonMatchExpression> sub1( new EqualityMatchExpression() );
+ ASSERT( sub1->init( "a", baseOperand1[ "a" ] ).isOK() );
+
+ auto_ptr<ComparisonMatchExpression> sub2( new EqualityMatchExpression() );
+ ASSERT( sub2->init( "b", baseOperand2[ "b" ] ).isOK() );
+
+ AndMatchExpression andOp;
+ andOp.add( sub1.release() );
+ andOp.add( sub2.release() );
+
+ MatchDetails details;
+ details.requestElemMatchKey();
+ ASSERT( !andOp.matchesBSON( BSON( "a" << BSON_ARRAY( 1 ) ), &details ) );
+ ASSERT( !details.hasElemMatchKey() );
+ ASSERT( !andOp.matchesBSON( BSON( "b" << BSON_ARRAY( 2 ) ), &details ) );
+ ASSERT( !details.hasElemMatchKey() );
+ ASSERT( andOp.matchesBSON( BSON( "a" << BSON_ARRAY( 1 ) << "b" << BSON_ARRAY( 1 << 2 ) ),
+ &details ) );
+ ASSERT( details.hasElemMatchKey() );
+ // The elem match key for the second $and clause is recorded.
+ ASSERT_EQUALS( "1", details.elemMatchKey() );
+ }
+
+ /**
+ TEST( AndOp, MatchesIndexKeyWithoutUnknown ) {
+ BSONObj baseOperand1 = BSON( "$gt" << 1 );
+ BSONObj baseOperand2 = BSON( "$lt" << 5 );
+ auto_ptr<ComparisonMatchExpression> sub1( new ComparisonMatchExpression() );
+ ASSERT( sub1->init( "a", baseOperand1[ "$gt" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub2( new ComparisonMatchExpression() );
+ ASSERT( sub2->init( "a", baseOperand2[ "$lt" ] ).isOK() );
+ OwnedPointerVector<MatchMatchExpression> subMatchExpressions;
+ subMatchExpressions.mutableVector().push_back( sub1.release() );
+ subMatchExpressions.mutableVector().push_back( sub2.release() );
+ AndOp andOp;
+ ASSERT( andOp.init( &subMatchExpressions ).isOK() );
+ IndexSpec indexSpec( BSON( "a" << 1 ) );
+ ASSERT( MatchMatchExpression::PartialMatchResult_True ==
+ andOp.matchesIndexKey( BSON( "" << 3 ), indexSpec ) );
+ ASSERT( MatchMatchExpression::PartialMatchResult_False ==
+ andOp.matchesIndexKey( BSON( "" << 0 ), indexSpec ) );
+ ASSERT( MatchMatchExpression::PartialMatchResult_False ==
+ andOp.matchesIndexKey( BSON( "" << 6 ), indexSpec ) );
+ }
+
+ TEST( AndOp, MatchesIndexKeyWithUnknown ) {
+ BSONObj baseOperand1 = BSON( "$gt" << 1 );
+ BSONObj baseOperand2 = BSON( "$lt" << 5 );
+ // This part will return PartialMatchResult_Unknown.
+ BSONObj baseOperand3 = BSON( "$ne" << 5 );
+ auto_ptr<ComparisonMatchExpression> sub1( new ComparisonMatchExpression() );
+ ASSERT( sub1->init( "a", baseOperand1[ "$gt" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub2( new ComparisonMatchExpression() );
+ ASSERT( sub2->init( "a", baseOperand2[ "$lt" ] ).isOK() );
+ auto_ptr<NeOp> sub3( new NeOp() );
+ ASSERT( sub3->init( "a", baseOperand3[ "$ne" ] ).isOK() );
+ OwnedPointerVector<MatchMatchExpression> subMatchExpressions;
+ subMatchExpressions.mutableVector().push_back( sub1.release() );
+ subMatchExpressions.mutableVector().push_back( sub2.release() );
+ subMatchExpressions.mutableVector().push_back( sub3.release() );
+ AndOp andOp;
+ ASSERT( andOp.init( &subMatchExpressions ).isOK() );
+ IndexSpec indexSpec( BSON( "a" << 1 ) );
+ ASSERT( MatchMatchExpression::PartialMatchResult_Unknown ==
+ andOp.matchesIndexKey( BSON( "" << 3 ), indexSpec ) );
+ ASSERT( MatchMatchExpression::PartialMatchResult_False ==
+ andOp.matchesIndexKey( BSON( "" << 0 ), indexSpec ) );
+ ASSERT( MatchMatchExpression::PartialMatchResult_False ==
+ andOp.matchesIndexKey( BSON( "" << 6 ), indexSpec ) );
+ }
+ */
+
+ /**
+ TEST( OrOp, MatchesElementSingleClause ) {
+ BSONObj baseOperand = BSON( "$lt" << 5 );
+ BSONObj match = BSON( "a" << 4 );
+ BSONObj notMatch = BSON( "a" << 5 );
+ auto_ptr<ComparisonMatchExpression> lt( new ComparisonMatchExpression() );
+ ASSERT( lt->init( "a", baseOperand[ "$lt" ] ).isOK() );
+ OwnedPointerVector<MatchMatchExpression> subMatchExpressions;
+ subMatchExpressions.mutableVector().push_back( lt.release() );
+ OrOp orOp;
+ ASSERT( orOp.init( &subMatchExpressions ).isOK() );
+ ASSERT( orOp.matchesSingleElement( match[ "a" ] ) );
+ ASSERT( !orOp.matchesSingleElement( notMatch[ "a" ] ) );
+ }
+ */
+
+ TEST( OrOp, NoClauses ) {
+ OrMatchExpression orOp;
+ ASSERT( !orOp.matchesBSON( BSONObj(), NULL ) );
+ }
+ /*
+ TEST( OrOp, MatchesElementThreeClauses ) {
+ BSONObj baseOperand1 = BSON( "$lt" << 0 );
+ BSONObj baseOperand2 = BSON( "$gt" << 10 );
+ BSONObj baseOperand3 = BSON( "a" << 5 );
+ BSONObj match1 = BSON( "a" << -1 );
+ BSONObj match2 = BSON( "a" << 11 );
+ BSONObj match3 = BSON( "a" << 5 );
+ BSONObj notMatch = BSON( "a" << "6" );
+ auto_ptr<ComparisonMatchExpression> sub1( new ComparisonMatchExpression() );
+ ASSERT( sub1->init( "a", baseOperand1[ "$lt" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub2( new ComparisonMatchExpression() );
+ ASSERT( sub2->init( "a", baseOperand2[ "$gt" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub3( new ComparisonMatchExpression() );
+ ASSERT( sub3->init( "a", baseOperand3[ "a" ] ).isOK() );
+ OwnedPointerVector<MatchMatchExpression> subMatchExpressions;
+ subMatchExpressions.mutableVector().push_back( sub1.release() );
+ subMatchExpressions.mutableVector().push_back( sub2.release() );
+ subMatchExpressions.mutableVector().push_back( sub3.release() );
+ OrOp orOp;
+ ASSERT( orOp.init( &subMatchExpressions ).isOK() );
+ ASSERT( orOp.matchesSingleElement( match1[ "a" ] ) );
+ ASSERT( orOp.matchesSingleElement( match2[ "a" ] ) );
+ ASSERT( orOp.matchesSingleElement( match3[ "a" ] ) );
+ ASSERT( !orOp.matchesSingleElement( notMatch[ "a" ] ) );
+ }
+ */
+ TEST( OrOp, MatchesSingleClause ) {
+ BSONObj baseOperand = BSON( "$ne" << 5 );
+ auto_ptr<ComparisonMatchExpression> eq( new EqualityMatchExpression() );
+ ASSERT( eq->init( "a", baseOperand[ "$ne" ] ).isOK() );
+ auto_ptr<NotMatchExpression> ne( new NotMatchExpression() );
+ ASSERT( ne->init( eq.release() ).isOK() );
+
+ OrMatchExpression orOp;
+ orOp.add( ne.release() );
+
+ ASSERT( orOp.matchesBSON( BSON( "a" << 4 ), NULL ) );
+ ASSERT( orOp.matchesBSON( BSON( "a" << BSON_ARRAY( 4 << 6 ) ), NULL ) );
+ ASSERT( !orOp.matchesBSON( BSON( "a" << 5 ), NULL ) );
+ ASSERT( !orOp.matchesBSON( BSON( "a" << BSON_ARRAY( 4 << 5 ) ), NULL ) );
+ }
+
+ TEST( OrOp, MatchesThreeClauses ) {
+ BSONObj baseOperand1 = BSON( "$gt" << 10 );
+ BSONObj baseOperand2 = BSON( "$lt" << 0 );
+ BSONObj baseOperand3 = BSON( "b" << 100 );
+ auto_ptr<ComparisonMatchExpression> sub1( new GTMatchExpression() );
+ ASSERT( sub1->init( "a", baseOperand1[ "$gt" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub2( new LTMatchExpression() );
+ ASSERT( sub2->init( "a", baseOperand2[ "$lt" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub3( new EqualityMatchExpression() );
+ ASSERT( sub3->init( "b", baseOperand3[ "b" ] ).isOK() );
+
+ OrMatchExpression orOp;
+ orOp.add( sub1.release() );
+ orOp.add( sub2.release() );
+ orOp.add( sub3.release() );
+
+ ASSERT( orOp.matchesBSON( BSON( "a" << -1 ), NULL ) );
+ ASSERT( orOp.matchesBSON( BSON( "a" << 11 ), NULL ) );
+ ASSERT( !orOp.matchesBSON( BSON( "a" << 5 ), NULL ) );
+ ASSERT( orOp.matchesBSON( BSON( "b" << 100 ), NULL ) );
+ ASSERT( !orOp.matchesBSON( BSON( "b" << 101 ), NULL ) );
+ ASSERT( !orOp.matchesBSON( BSONObj(), NULL ) );
+ ASSERT( orOp.matchesBSON( BSON( "a" << 11 << "b" << 100 ), NULL ) );
+ }
+
+ TEST( OrOp, ElemMatchKey ) {
+ BSONObj baseOperand1 = BSON( "a" << 1 );
+ BSONObj baseOperand2 = BSON( "b" << 2 );
+ auto_ptr<ComparisonMatchExpression> sub1( new EqualityMatchExpression() );
+ ASSERT( sub1->init( "a", baseOperand1[ "a" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub2( new EqualityMatchExpression() );
+ ASSERT( sub2->init( "b", baseOperand2[ "b" ] ).isOK() );
+
+ OrMatchExpression orOp;
+ orOp.add( sub1.release() );
+ orOp.add( sub2.release() );
+
+ MatchDetails details;
+ details.requestElemMatchKey();
+ ASSERT( !orOp.matchesBSON( BSONObj(), &details ) );
+ ASSERT( !details.hasElemMatchKey() );
+ ASSERT( !orOp.matchesBSON( BSON( "a" << BSON_ARRAY( 10 ) << "b" << BSON_ARRAY( 10 ) ),
+ &details ) );
+ ASSERT( !details.hasElemMatchKey() );
+ ASSERT( orOp.matchesBSON( BSON( "a" << BSON_ARRAY( 1 ) << "b" << BSON_ARRAY( 1 << 2 ) ),
+ &details ) );
+ // The elem match key feature is not implemented for $or.
+ ASSERT( !details.hasElemMatchKey() );
+ }
+
+ /**
+ TEST( OrOp, MatchesIndexKeyWithoutUnknown ) {
+ BSONObj baseOperand1 = BSON( "$gt" << 5 );
+ BSONObj baseOperand2 = BSON( "$lt" << 1 );
+ auto_ptr<ComparisonMatchExpression> sub1( new ComparisonMatchExpression() );
+ ASSERT( sub1->init( "a", baseOperand1[ "$gt" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub2( new ComparisonMatchExpression() );
+ ASSERT( sub2->init( "a", baseOperand2[ "$lt" ] ).isOK() );
+ OwnedPointerVector<MatchMatchExpression> subMatchExpressions;
+ subMatchExpressions.mutableVector().push_back( sub1.release() );
+ subMatchExpressions.mutableVector().push_back( sub2.release() );
+ OrOp orOp;
+ ASSERT( orOp.init( &subMatchExpressions ).isOK() );
+ IndexSpec indexSpec( BSON( "a" << 1 ) );
+ ASSERT( MatchMatchExpression::PartialMatchResult_False ==
+ orOp.matchesIndexKey( BSON( "" << 3 ), indexSpec ) );
+ ASSERT( MatchMatchExpression::PartialMatchResult_True ==
+ orOp.matchesIndexKey( BSON( "" << 0 ), indexSpec ) );
+ ASSERT( MatchMatchExpression::PartialMatchResult_True ==
+ orOp.matchesIndexKey( BSON( "" << 6 ), indexSpec ) );
+ }
+
+ TEST( OrOp, MatchesIndexKeyWithUnknown ) {
+ BSONObj baseOperand1 = BSON( "$gt" << 5 );
+ BSONObj baseOperand2 = BSON( "$lt" << 1 );
+ // This part will return PartialMatchResult_Unknown.
+ BSONObj baseOperand3 = BSON( "$ne" << 5 );
+ auto_ptr<ComparisonMatchExpression> sub1( new ComparisonMatchExpression() );
+ ASSERT( sub1->init( "a", baseOperand1[ "$gt" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub2( new ComparisonMatchExpression() );
+ ASSERT( sub2->init( "a", baseOperand2[ "$lt" ] ).isOK() );
+ auto_ptr<NeOp> sub3( new NeOp() );
+ ASSERT( sub3->init( "a", baseOperand3[ "$ne" ] ).isOK() );
+ OwnedPointerVector<MatchMatchExpression> subMatchExpressions;
+ subMatchExpressions.mutableVector().push_back( sub1.release() );
+ subMatchExpressions.mutableVector().push_back( sub2.release() );
+ subMatchExpressions.mutableVector().push_back( sub3.release() );
+ OrOp orOp;
+ ASSERT( orOp.init( &subMatchExpressions ).isOK() );
+ IndexSpec indexSpec( BSON( "a" << 1 ) );
+ ASSERT( MatchMatchExpression::PartialMatchResult_Unknown ==
+ orOp.matchesIndexKey( BSON( "" << 3 ), indexSpec ) );
+ ASSERT( MatchMatchExpression::PartialMatchResult_True ==
+ orOp.matchesIndexKey( BSON( "" << 0 ), indexSpec ) );
+ ASSERT( MatchMatchExpression::PartialMatchResult_True ==
+ orOp.matchesIndexKey( BSON( "" << 6 ), indexSpec ) );
+ }
+ */
+
+ /**
+ TEST( NorOp, MatchesElementSingleClause ) {
+ BSONObj baseOperand = BSON( "$lt" << 5 );
+ BSONObj match = BSON( "a" << 5 );
+ BSONObj notMatch = BSON( "a" << 4 );
+ auto_ptr<ComparisonMatchExpression> lt( new ComparisonMatchExpression() );
+ ASSERT( lt->init( "a", baseOperand[ "$lt" ] ).isOK() );
+ OwnedPointerVector<MatchMatchExpression> subMatchExpressions;
+ subMatchExpressions.mutableVector().push_back( lt.release() );
+ NorOp norOp;
+ ASSERT( norOp.init( &subMatchExpressions ).isOK() );
+ ASSERT( norOp.matchesSingleElement( match[ "a" ] ) );
+ ASSERT( !norOp.matchesSingleElement( notMatch[ "a" ] ) );
+ }
+ */
+
+ TEST( NorOp, NoClauses ) {
+ NorMatchExpression norOp;
+ ASSERT( norOp.matchesBSON( BSONObj(), NULL ) );
+ }
+ /*
+ TEST( NorOp, MatchesElementThreeClauses ) {
+ BSONObj baseOperand1 = BSON( "$lt" << 0 );
+ BSONObj baseOperand2 = BSON( "$gt" << 10 );
+ BSONObj baseOperand3 = BSON( "a" << 5 );
+ BSONObj notMatch1 = BSON( "a" << -1 );
+ BSONObj notMatch2 = BSON( "a" << 11 );
+ BSONObj notMatch3 = BSON( "a" << 5 );
+ BSONObj match = BSON( "a" << "6" );
+ auto_ptr<ComparisonMatchExpression> sub1( new ComparisonMatchExpression() );
+ ASSERT( sub1->init( "a", baseOperand1[ "$lt" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub2( new ComparisonMatchExpression() );
+ ASSERT( sub2->init( "a", baseOperand2[ "$gt" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub3( new ComparisonMatchExpression() );
+ ASSERT( sub3->init( "a", baseOperand3[ "a" ] ).isOK() );
+ OwnedPointerVector<MatchMatchExpression> subMatchExpressions;
+ subMatchExpressions.mutableVector().push_back( sub1.release() );
+ subMatchExpressions.mutableVector().push_back( sub2.release() );
+ subMatchExpressions.mutableVector().push_back( sub3.release() );
+ NorOp norOp;
+ ASSERT( norOp.init( &subMatchExpressions ).isOK() );
+ ASSERT( !norOp.matchesSingleElement( notMatch1[ "a" ] ) );
+ ASSERT( !norOp.matchesSingleElement( notMatch2[ "a" ] ) );
+ ASSERT( !norOp.matchesSingleElement( notMatch3[ "a" ] ) );
+ ASSERT( norOp.matchesSingleElement( match[ "a" ] ) );
+ }
+ */
+
+ TEST( NorOp, MatchesSingleClause ) {
+ BSONObj baseOperand = BSON( "$ne" << 5 );
+ auto_ptr<ComparisonMatchExpression> eq( new EqualityMatchExpression() );
+ ASSERT( eq->init( "a", baseOperand[ "$ne" ] ).isOK() );
+ auto_ptr<NotMatchExpression> ne( new NotMatchExpression() );
+ ASSERT( ne->init( eq.release() ).isOK() );
+
+ NorMatchExpression norOp;
+ norOp.add( ne.release() );
+
+ ASSERT( !norOp.matchesBSON( BSON( "a" << 4 ), NULL ) );
+ ASSERT( !norOp.matchesBSON( BSON( "a" << BSON_ARRAY( 4 << 6 ) ), NULL ) );
+ ASSERT( norOp.matchesBSON( BSON( "a" << 5 ), NULL ) );
+ ASSERT( norOp.matchesBSON( BSON( "a" << BSON_ARRAY( 4 << 5 ) ), NULL ) );
+ }
+
+ TEST( NorOp, MatchesThreeClauses ) {
+ BSONObj baseOperand1 = BSON( "$gt" << 10 );
+ BSONObj baseOperand2 = BSON( "$lt" << 0 );
+ BSONObj baseOperand3 = BSON( "b" << 100 );
+
+ auto_ptr<ComparisonMatchExpression> sub1( new GTMatchExpression() );
+ ASSERT( sub1->init( "a", baseOperand1[ "$gt" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub2( new LTMatchExpression() );
+ ASSERT( sub2->init( "a", baseOperand2[ "$lt" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub3( new EqualityMatchExpression() );
+ ASSERT( sub3->init( "b", baseOperand3[ "b" ] ).isOK() );
+
+ NorMatchExpression norOp;
+ norOp.add( sub1.release() );
+ norOp.add( sub2.release() );
+ norOp.add( sub3.release() );
+
+ ASSERT( !norOp.matchesBSON( BSON( "a" << -1 ), NULL ) );
+ ASSERT( !norOp.matchesBSON( BSON( "a" << 11 ), NULL ) );
+ ASSERT( norOp.matchesBSON( BSON( "a" << 5 ), NULL ) );
+ ASSERT( !norOp.matchesBSON( BSON( "b" << 100 ), NULL ) );
+ ASSERT( norOp.matchesBSON( BSON( "b" << 101 ), NULL ) );
+ ASSERT( norOp.matchesBSON( BSONObj(), NULL ) );
+ ASSERT( !norOp.matchesBSON( BSON( "a" << 11 << "b" << 100 ), NULL ) );
+ }
+
+ TEST( NorOp, ElemMatchKey ) {
+ BSONObj baseOperand1 = BSON( "a" << 1 );
+ BSONObj baseOperand2 = BSON( "b" << 2 );
+ auto_ptr<ComparisonMatchExpression> sub1( new EqualityMatchExpression() );
+ ASSERT( sub1->init( "a", baseOperand1[ "a" ] ).isOK() );
+ auto_ptr<ComparisonMatchExpression> sub2( new EqualityMatchExpression() );
+ ASSERT( sub2->init( "b", baseOperand2[ "b" ] ).isOK() );
+
+ NorMatchExpression norOp;
+ norOp.add( sub1.release() );
+ norOp.add( sub2.release() );
+
+ MatchDetails details;
+ details.requestElemMatchKey();
+ ASSERT( !norOp.matchesBSON( BSON( "a" << 1 ), &details ) );
+ ASSERT( !details.hasElemMatchKey() );
+ ASSERT( !norOp.matchesBSON( BSON( "a" << BSON_ARRAY( 1 ) << "b" << BSON_ARRAY( 10 ) ),
+ &details ) );
+ ASSERT( !details.hasElemMatchKey() );
+ ASSERT( norOp.matchesBSON( BSON( "a" << BSON_ARRAY( 3 ) << "b" << BSON_ARRAY( 4 ) ),
+ &details ) );
+ // The elem match key feature is not implemented for $nor.
+ ASSERT( !details.hasElemMatchKey() );
+ }
+
+
+ TEST( NorOp, Equivalent ) {
+ BSONObj baseOperand1 = BSON( "a" << 1 );
+ BSONObj baseOperand2 = BSON( "b" << 2 );
+ EqualityMatchExpression sub1;
+ ASSERT( sub1.init( "a", baseOperand1[ "a" ] ).isOK() );
+ EqualityMatchExpression sub2;
+ ASSERT( sub2.init( "b", baseOperand2[ "b" ] ).isOK() );
+
+ NorMatchExpression e1;
+ e1.add( sub1.shallowClone() );
+ e1.add( sub2.shallowClone() );
+
+ NorMatchExpression e2;
+ e2.add( sub1.shallowClone() );
+
+ ASSERT( e1.equivalent( &e1 ) );
+ ASSERT( !e1.equivalent( &e2 ) );
+ }
+
+ /**
+ TEST( NorOp, MatchesIndexKey ) {
+ BSONObj baseOperand = BSON( "a" << 5 );
+ auto_ptr<ComparisonMatchExpression> eq( new ComparisonMatchExpression() );
+ ASSERT( eq->init( "a", baseOperand[ "a" ] ).isOK() );
+ OwnedPointerVector<MatchMatchExpression> subMatchExpressions;
+ subMatchExpressions.mutableVector().push_back( eq.release() );
+ NorOp norOp;
+ ASSERT( norOp.init( &subMatchExpressions ).isOK() );
+ IndexSpec indexSpec( BSON( "a" << 1 ) );
+ BSONObj indexKey = BSON( "" << "7" );
+ ASSERT( MatchMatchExpression::PartialMatchResult_Unknown ==
+ norOp.matchesIndexKey( indexKey, indexSpec ) );
+ }
+ */
+
+}