// expression_parser_array_test.cpp /** * Copyright (C) 2013 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 . * * 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 "mongo/unittest/unittest.h" #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/jsobj.h" #include "mongo/db/json.h" #include "mongo/db/matcher/expression.h" #include "mongo/db/matcher/expression_array.h" namespace mongo { TEST( MatchExpressionParserArrayTest, Size1 ) { BSONObj query = BSON( "x" << BSON( "$size" << 2 ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 << 2 ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 << 2 << 3 ) ) ) ); } TEST( MatchExpressionParserArrayTest, SizeAsString ) { BSONObj query = BSON( "x" << BSON( "$size" << "a" ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 << 2 ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSONArray() ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 ) ) ) ); } TEST( MatchExpressionParserArrayTest, SizeWithDouble ) { BSONObj query = BSON( "x" << BSON( "$size" << 2.5 ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 << 2 ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSONArray() ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 << 2 << 3 ) ) ) ); } TEST( MatchExpressionParserArrayTest, SizeBad ) { BSONObj query = BSON( "x" << BSON( "$size" << BSONNULL ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_FALSE( result.isOK() ); } // --------- TEST( MatchExpressionParserArrayTest, ElemMatchArr1 ) { BSONObj query = BSON( "x" << BSON( "$elemMatch" << BSON( "x" << 1 << "y" << 2 ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 << 2 ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "x" << 1 ) ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "x" << 1 << "y" << 2 ) ) ) ) ); } TEST( MatchExpressionParserArrayTest, ElemMatchAnd ) { BSONObj query = BSON( "x" << BSON( "$elemMatch" << BSON( "$and" << BSON_ARRAY( BSON( "x" << 1 << "y" << 2 ) ) ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 << 2 ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "x" << 1 ) ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "x" << 1 << "y" << 2 ) ) ) ) ); } TEST( MatchExpressionParserArrayTest, ElemMatchNor ) { BSONObj query = BSON( "x" << BSON( "$elemMatch" << BSON( "$nor" << BSON_ARRAY( BSON( "x" << 1 ) ) ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 << 2 ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "x" << 1 ) ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "x" << 2 << "y" << 2 ) ) ) ) ); } TEST( MatchExpressionParserArrayTest, ElemMatchOr ) { BSONObj query = BSON( "x" << BSON( "$elemMatch" << BSON( "$or" << BSON_ARRAY( BSON( "x" << 1 << "y" << 2 ) ) ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 << 2 ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "x" << 1 ) ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "x" << 1 << "y" << 2 ) ) ) ) ); } TEST( MatchExpressionParserArrayTest, ElemMatchVal1 ) { BSONObj query = BSON( "x" << BSON( "$elemMatch" << BSON( "$gt" << 5 ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 4 ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 6 ) ) ) ); } // with explicit $eq TEST( MatchExpressionParserArrayTest, ElemMatchDBRef1 ) { OID oid = OID::gen(); BSONObj match = BSON( "$ref" << "coll" << "$id" << oid << "$db" << "db" ); OID oidx = OID::gen(); BSONObj notMatch = BSON( "$ref" << "coll" << "$id" << oidx << "$db" << "db" ); BSONObj query = BSON( "x" << BSON( "$elemMatch" << BSON( "$eq" << match ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << match ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( notMatch ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( match ) ) ) ); } TEST( MatchExpressionParserArrayTest, ElemMatchDBRef2 ) { OID oid = OID::gen(); BSONObj match = BSON( "$ref" << "coll" << "$id" << oid << "$db" << "db" ); OID oidx = OID::gen(); BSONObj notMatch = BSON( "$ref" << "coll" << "$id" << oidx << "$db" << "db" ); BSONObj query = BSON( "x" << BSON( "$elemMatch" << match ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << match ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( notMatch ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( match ) ) ) ); } // Additional fields after $ref and $id. TEST( MatchExpressionParserArrayTest, ElemMatchDBRef3 ) { OID oid = OID::gen(); BSONObj match = BSON( "$ref" << "coll" << "$id" << oid << "foo" << 12345 ); OID oidx = OID::gen(); BSONObj notMatch = BSON( "$ref" << "coll" << "$id" << oidx << "foo" << 12345 ); BSONObj query = BSON( "x" << BSON( "$elemMatch" << match ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << match ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( notMatch ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( match ) ) ) ); // Document contains fields not referred to in $elemMatch query. ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "$ref" << "coll" << "$id" << oid << "foo" << 12345 << "bar" << 678 ) ) ) ) ); } // Query with DBRef fields out of order. TEST( MatchExpressionParserArrayTest, ElemMatchDBRef4 ) { OID oid = OID::gen(); BSONObj match = BSON( "$ref" << "coll" << "$id" << oid << "$db" << "db" ); BSONObj matchOutOfOrder = BSON( "$db" << "db" << "$id" << oid << "$ref" << "coll" ); OID oidx = OID::gen(); BSONObj notMatch = BSON( "$ref" << "coll" << "$id" << oidx << "$db" << "db" ); BSONObj query = BSON( "x" << BSON( "$elemMatch" << matchOutOfOrder ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << match ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( notMatch ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( match ) ) ) ); } // Query with DBRef fields out of order. // Additional fields besides $ref and $id. TEST( MatchExpressionParserArrayTest, ElemMatchDBRef5 ) { OID oid = OID::gen(); BSONObj match = BSON( "$ref" << "coll" << "$id" << oid << "foo" << 12345 ); BSONObj matchOutOfOrder = BSON( "foo" << 12345 << "$id" << oid << "$ref" << "coll" ); OID oidx = OID::gen(); BSONObj notMatch = BSON( "$ref" << "coll" << "$id" << oidx << "foo" << 12345 ); BSONObj query = BSON( "x" << BSON( "$elemMatch" << matchOutOfOrder ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << match ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( notMatch ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( match ) ) ) ); // Document contains fields not referred to in $elemMatch query. ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "$ref" << "coll" << "$id" << oid << "foo" << 12345 << "bar" << 678 ) ) ) ) ); } // Incomplete DBRef - $id missing. TEST( MatchExpressionParserArrayTest, ElemMatchDBRef6 ) { OID oid = OID::gen(); BSONObj match = BSON( "$ref" << "coll" << "$id" << oid << "foo" << 12345 ); BSONObj matchMissingID = BSON( "$ref" << "coll" << "foo" << 12345 ); BSONObj notMatch = BSON( "$ref" << "collx" << "$id" << oid << "foo" << 12345 ); BSONObj query = BSON( "x" << BSON( "$elemMatch" << matchMissingID ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << match ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( notMatch ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( match ) ) ) ); // Document contains fields not referred to in $elemMatch query. ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "$ref" << "coll" << "$id" << oid << "foo" << 12345 << "bar" << 678 ) ) ) ) ); } // Incomplete DBRef - $ref missing. TEST( MatchExpressionParserArrayTest, ElemMatchDBRef7 ) { OID oid = OID::gen(); BSONObj match = BSON( "$ref" << "coll" << "$id" << oid << "foo" << 12345 ); BSONObj matchMissingRef = BSON( "$id" << oid << "foo" << 12345 ); OID oidx = OID::gen(); BSONObj notMatch = BSON( "$ref" << "coll" << "$id" << oidx << "foo" << 12345 ); BSONObj query = BSON( "x" << BSON( "$elemMatch" << matchMissingRef ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << match ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( notMatch ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( match ) ) ) ); // Document contains fields not referred to in $elemMatch query. ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "$ref" << "coll" << "$id" << oid << "foo" << 12345 << "bar" << 678 ) ) ) ) ); } // Incomplete DBRef - $db only. TEST( MatchExpressionParserArrayTest, ElemMatchDBRef8 ) { OID oid = OID::gen(); BSONObj match = BSON( "$ref" << "coll" << "$id" << oid << "$db" << "db" << "foo" << 12345 ); BSONObj matchDBOnly = BSON( "$db" << "db" << "foo" << 12345 ); BSONObj notMatch = BSON( "$ref" << "coll" << "$id" << oid << "$db" << "dbx" << "foo" << 12345 ); BSONObj query = BSON( "x" << BSON( "$elemMatch" << matchDBOnly ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << match ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( notMatch ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( match ) ) ) ); // Document contains fields not referred to in $elemMatch query. ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "$ref" << "coll" << "$id" << oid << "$db" << "db" << "foo" << 12345 << "bar" << 678 ) ) ) ) ); } TEST( MatchExpressionParserArrayTest, All1 ) { BSONObj query = BSON( "x" << BSON( "$all" << BSON_ARRAY( 1 << 2 ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 2 ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 << 2 ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 << 2 << 3 ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 2 << 3 ) ) ) ); } TEST( MatchExpressionParserArrayTest, AllNull ) { BSONObj query = BSON( "x" << BSON( "$all" << BSON_ARRAY( BSONNULL ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSONNULL ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONNULL ) ) ) ); } TEST( MatchExpressionParserArrayTest, AllBadArg ) { BSONObj query = BSON( "x" << BSON( "$all" << 1 ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_FALSE( result.isOK() ); } TEST( MatchExpressionParserArrayTest, AllBadRegexArg ) { string tooLargePattern( 50 * 1000, 'z' ); BSONObjBuilder allArray; allArray.appendRegex( "0", tooLargePattern, "" ); BSONObjBuilder operand; operand.appendArray( "$all", allArray.obj() ); BSONObj query = BSON( "x" << operand.obj() ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_FALSE( result.isOK() ); } TEST( MatchExpressionParserArrayTest, AllRegex1 ) { BSONObjBuilder allArray; allArray.appendRegex( "0", "^a", "" ); allArray.appendRegex( "1", "B", "i" ); BSONObjBuilder all; all.appendArray( "$all", allArray.obj() ); BSONObj query = BSON( "a" << all.obj() ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); BSONObj notMatchFirst = BSON( "a" << "ax" ); BSONObj notMatchSecond = BSON( "a" << "qqb" ); BSONObj matchesBoth = BSON( "a" << "ab" ); ASSERT( !result.getValue()->matchesSingleElement( notMatchFirst[ "a" ] ) ); ASSERT( !result.getValue()->matchesSingleElement( notMatchSecond[ "a" ] ) ); ASSERT( result.getValue()->matchesSingleElement( matchesBoth[ "a" ] ) ); } TEST( MatchExpressionParserArrayTest, AllRegex2 ) { BSONObjBuilder allArray; allArray.appendRegex( "0", "^a", "" ); allArray.append( "1", "abc" ); BSONObjBuilder all; all.appendArray( "$all", allArray.obj() ); BSONObj query = BSON( "a" << all.obj() ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); BSONObj notMatchFirst = BSON( "a" << "ax" ); BSONObj matchesBoth = BSON( "a" << "abc" ); ASSERT( !result.getValue()->matchesSingleElement( notMatchFirst[ "a" ] ) ); ASSERT( result.getValue()->matchesSingleElement( matchesBoth[ "a" ] ) ); } TEST( MatchExpressionParserArrayTest, AllNonArray ) { BSONObj query = BSON( "x" << BSON( "$all" << BSON_ARRAY( 5 ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << 5 ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 5 ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 4 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 4 ) ) ) ); } TEST( MatchExpressionParserArrayTest, AllElemMatch1 ) { BSONObj internal = BSON( "x" << 1 << "y" << 2 ); BSONObj query = BSON( "x" << BSON( "$all" << BSON_ARRAY( BSON( "$elemMatch" << internal ) ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( 1 << 2 ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "x" << 1 ) ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "x" << 1 << "y" << 2 ) ) ) ) ); } // $all and $elemMatch on dotted field. // Top level field can be either document or array. TEST( MatchExpressionParserArrayTest, AllElemMatch2 ) { BSONObj internal = BSON( "z" << 1 ); BSONObj query = BSON( "x.y" << BSON( "$all" << BSON_ARRAY( BSON( "$elemMatch" << internal ) ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON( "y" << 1 ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON( "y" << BSON_ARRAY( 1 << 2 ) ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON( "y" << BSON_ARRAY( BSON( "x" << 1 ) ) ) ) ) ); // x is a document. Internal document does not contain z. ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON( "y" << BSON_ARRAY( BSON( "x" << 1 << "y" << 1 ) ) ) ) ) ); // x is an array. Internal document does not contain z. ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "y" << BSON_ARRAY( BSON( "x" << 1 << "y" << 1 ) ) ) ) ) ) ); // x is a document but y is not an array. ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON( "y" << BSON( "x" << 1 << "z" << 1 ) ) ) ) ); // x is an array but y is not an array. ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "y" << BSON( "x" << 1 << "z" << 1 ) ) ) ) ) ); // x is a document. ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON( "y" << BSON_ARRAY( BSON( "x" << 1 << "z" << 1 ) ) ) ) ) ); // x is an array. ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSON( "y" << BSON_ARRAY( BSON( "x" << 1 << "z" << 1 ) ) ) ) ) ) ); } TEST( MatchExpressionParserArrayTest, AllElemMatchBad ) { BSONObj internal = BSON( "x" << 1 << "y" << 2 ); BSONObj query = BSON( "x" << BSON( "$all" << BSON_ARRAY( BSON( "$elemMatch" << internal ) << 5 ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_FALSE( result.isOK() ); query = BSON( "x" << BSON( "$all" << BSON_ARRAY( 5 << BSON( "$elemMatch" << internal ) ) ) ); result = MatchExpressionParser::parse( query ); ASSERT_FALSE( result.isOK() ); } // $all with empty string. TEST( MatchExpressionParserArrayTest, AllEmptyString ) { BSONObj query = BSON( "x" << BSON( "$all" << BSON_ARRAY( "" ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << "a" ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONNULL << "a" ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONObj() << "a" ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSONArray() ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << "" ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONNULL << "" ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONObj() << "" ) ) ) ); } // $all with ISO date. TEST( MatchExpressionParserArrayTest, AllISODate ) { StatusWith matchResult = dateFromISOString("2014-12-31T00:00:00.000Z"); ASSERT_TRUE( matchResult.isOK() ); const Date_t& match = matchResult.getValue(); StatusWith notMatchResult = dateFromISOString("2014-12-30T00:00:00.000Z"); ASSERT_TRUE( notMatchResult.isOK() ); const Date_t& notMatch = notMatchResult.getValue(); BSONObj query = BSON( "x" << BSON( "$all" << BSON_ARRAY( match ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << notMatch ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONNULL << notMatch ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONObj() << notMatch ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSONArray() ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << match ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONNULL << match ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONObj() << match ) ) ) ); } // $all on array element with empty string. TEST( MatchExpressionParserArrayTest, AllDottedEmptyString ) { BSONObj query = BSON( "x.1" << BSON( "$all" << BSON_ARRAY( "" ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << "a" ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONNULL << "a" ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONObj() << "a" ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( "" << BSONNULL ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( "" << BSONObj() ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSONArray() ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << "" ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONNULL << "" ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONObj() << "" ) ) ) ); } // $all on array element with ISO date. TEST( MatchExpressionParserArrayTest, AllDottedISODate ) { StatusWith matchResult = dateFromISOString("2014-12-31T00:00:00.000Z"); ASSERT_TRUE( matchResult.isOK() ); const Date_t& match = matchResult.getValue(); StatusWith notMatchResult = dateFromISOString("2014-12-30T00:00:00.000Z"); ASSERT_TRUE( notMatchResult.isOK() ); const Date_t& notMatch = notMatchResult.getValue(); BSONObj query = BSON( "x.1" << BSON( "$all" << BSON_ARRAY( match ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << notMatch ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONNULL << notMatch ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONObj() << notMatch ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( match << BSONNULL ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( match << BSONObj() ) ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << BSONArray() ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << match ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONNULL << match ) ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << BSON_ARRAY( BSONObj() << match ) ) ) ); } }