Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

SERVER-4180 Fixes and cleanups for $elemMatch matching, including fix…

… for SERVER-6001.
  • Loading branch information...
commit 68775795d16f6a3854341f16d4cab32d1bc76830 1 parent 5de67f3
astaple astaple authored
7 jstests/array4.js
View
@@ -28,3 +28,10 @@ t.insert({"a" : ["2", "1"]});
x = {"a.12" : /2/};
assert.eq(t.count(x), 1);
assert.eq(t.findOne(x).a[0], 0);
+
+// A query for an undefined value should not match an array.
+t.drop();
+t.insert( { a:[ 5 ] } );
+t.insert( { a:[ 5, 6 ] } );
+t.insert( { a:[ 5, "foo" ] } );
+assert.eq( 0, t.count( { a:{ $in:[ undefined ] } } ) );
24 jstests/arrayfind9.js
View
@@ -0,0 +1,24 @@
+// Assorted $elemMatch behavior checks.
+
+t = db.jstests_arrayfind9;
+t.drop();
+
+// Top level field $elemMatch:$not matching
+t.save( { a:[ 1 ] } );
+assert.eq( 1, t.count( { a:{ $elemMatch:{ $not:{ $ne:1 } } } } ) );
+
+// Top level field object $elemMatch matching.
+t.drop();
+t.save( { a:[ {} ] } );
+assert.eq( 1, t.count( { a:{ $elemMatch:{ $gte:{} } } } ) );
+
+// Top level field array $elemMatch matching.
+t.drop();
+t.save( { a:[ [] ] } );
+assert.eq( 1, t.count( { a:{ $elemMatch:{ $in:[ [] ] } } } ) );
+
+// Matching by array index.
+t.drop();
+t.save( { a:[ [ 'x' ] ] } );
+assert.eq( 1, t.count( { a:{ $elemMatch:{ '0':'x' } } } ) );
+
21 jstests/arrayfinda.js
View
@@ -0,0 +1,21 @@
+// Assorted $elemMatch matching behavior checks.
+
+t = db.jstests_arrayfinda;
+t.drop();
+
+// $elemMatch only matches elements within arrays (a descriptive, not a normative test).
+t.save( { a:[ { b:1 } ] } );
+t.save( { a:{ b:1 } } );
+
+function assertExpectedMatch( cursor ) {
+ assert.eq( [ { b:1 } ], cursor.next().a );
+ assert( !cursor.hasNext() );
+}
+
+assertExpectedMatch( t.find( { a:{ $elemMatch:{ b:{ $gte:1 } } } } ) );
+assertExpectedMatch( t.find( { a:{ $elemMatch:{ b:1 } } } ) );
+
+// $elemMatch is not used to perform key matching. SERVER-6001
+t.ensureIndex( { a:1 } );
+assertExpectedMatch( t.find( { a:{ $elemMatch:{ b:{ $gte:1 } } } } ).hint( { a:1 } ) );
+assertExpectedMatch( t.find( { a:{ $elemMatch:{ b:1 } } } ).hint( { a:1 } ) );
8 jstests/not2.js
View
@@ -71,10 +71,10 @@ check( {i:{$not:{$all:["a","b"]}}}, null, 0 );
check( {i:{$not:{$all:["c"]}}}, ["a","b"] );
t.remove( {} );
-t.save( {i:{j:"a"}} );
-t.save( {i:{j:"b"}} );
-check( {i:{$not:{$elemMatch:{j:"a"}}}}, {j:"b"} );
-check( {i:{$not:{$elemMatch:{j:"f"}}}}, {j:"a"}, 2 );
+t.save( {i:[{j:"a"}]} );
+t.save( {i:[{j:"b"}]} );
+check( {i:{$not:{$elemMatch:{j:"a"}}}}, [{j:"b"}] );
+check( {i:{$not:{$elemMatch:{j:"f"}}}}, [{j:"a"}], 2 );
}
46 src/mongo/db/matcher.cpp
View
@@ -161,7 +161,8 @@ namespace mongo {
BSONElement m = e;
uassert( 12517 , "$elemMatch needs an Object" , m.type() == Object );
BSONObj x = m.embeddedObject();
- if ( x.firstElement().getGtLtOp() == 0 ) {
+ if ( x.firstElement().getGtLtOp() == BSONObj::Equality &&
+ !str::equals( x.firstElement().fieldName(), "$not" ) ) {
_subMatcher.reset( new Matcher( x ) );
_subMatcherOnPrimitives = false;
}
@@ -544,8 +545,11 @@ namespace mongo {
case BSONObj::opALL:
case BSONObj::NE:
case BSONObj::NIN:
- case BSONObj::opEXISTS: // We can't match on index in this case.
- case BSONObj::opTYPE: // For $type:10 (null), a null key could be a missing field or a null value field.
+ case BSONObj::opEXISTS: // We can't match on index in this case.
+ case BSONObj::opTYPE: // For $type:10 (null), a null key could be a missing
+ // field or a null value field.
+ case BSONObj::opELEM_MATCH: // $elemMatch operates on arrays and no key match
+ // version has been implemented.
break;
case BSONObj::opIN: {
bool inContainsArray = false;
@@ -602,7 +606,7 @@ namespace mongo {
}
inline int Matcher::valuesMatch(const BSONElement& l, const BSONElement& r, int op, const ElementMatcher& bm) const {
- verify( op != BSONObj::NE && op != BSONObj::NIN );
+ verify( op != BSONObj::NE && op != BSONObj::NIN && op != BSONObj::opELEM_MATCH );
if ( op == BSONObj::Equality ) {
return l.valuesEqual(r);
@@ -800,7 +804,8 @@ namespace mongo {
if ( compareOp == BSONObj::opEXISTS ) {
return retExistsFound( em );
}
- if (valuesMatch(z, toMatch, compareOp, em) ) {
+ if ( compareOp != BSONObj::opELEM_MATCH &&
+ valuesMatch(z, toMatch, compareOp, em) ) {
// "field.<n>" array notation was used
if ( details )
details->setElemMatchKey( z.fieldName() );
@@ -841,39 +846,34 @@ namespace mongo {
}
}
else if ( ( e.type() != Array || indexed || compareOp == BSONObj::opSIZE ) &&
+ compareOp != BSONObj::opELEM_MATCH &&
valuesMatch(e, toMatch, compareOp, em ) ) {
return 1;
}
else if ( e.type() == Array && compareOp != BSONObj::opSIZE ) {
BSONObjIterator ai(e.embeddedObject());
- while ( ai.moreWithEOO() ) {
+ while ( ai.more() ) {
BSONElement z = ai.next();
+ bool match = false;
if ( compareOp == BSONObj::opELEM_MATCH ) {
- if ( z.type() == Object ) {
- if ( em._subMatcher->matches( z.embeddedObject() ) ) {
- if ( details )
- details->setElemMatchKey( z.fieldName() );
- return 1;
- }
+ if ( em._subMatcherOnPrimitives ) {
+ match = em._subMatcher->matches( z.wrap( "" ) );
}
- else if ( em._subMatcherOnPrimitives ) {
- if ( z.type() && em._subMatcher->matches( z.wrap( "" ) ) ) {
- if ( details )
- details->setElemMatchKey( z.fieldName() );
- return 1;
- }
+ else {
+ match = ( z.isABSONObj() && em._subMatcher->matches( z.embeddedObject() ) );
}
}
else {
- if ( valuesMatch( z, toMatch, compareOp, em) ) {
- if ( details )
- details->setElemMatchKey( z.fieldName() );
- return 1;
+ match = valuesMatch( z, toMatch, compareOp, em );
+ }
+ if ( match ) {
+ if ( details ) {
+ details->setElemMatchKey( z.fieldName() );
}
+ return 1;
}
-
}
// match an entire array to itself
Please sign in to comment.
Something went wrong with that request. Please try again.