diff options
author | Aaron Staple <aaron@10gen.com> | 2009-08-12 17:45:29 -0400 |
---|---|---|
committer | Aaron Staple <aaron@10gen.com> | 2009-08-12 17:45:29 -0400 |
commit | 2f3398e6cc1107f256762da03278afad1d5b73c9 (patch) | |
tree | 4960e22ddd4728e11c37b80775ce9b7c4fd9ab6c | |
parent | 9fd0168effad55570a39a4c4f3af6b5c8ed9bbe7 (diff) | |
download | mongo-2f3398e6cc1107f256762da03278afad1d5b73c9.tar.gz |
SERVER-149 implement $exists
-rw-r--r-- | db/jsobj.h | 1 | ||||
-rw-r--r-- | db/matcher.cpp | 66 | ||||
-rw-r--r-- | jstests/exists.js | 28 |
3 files changed, 65 insertions, 30 deletions
diff --git a/db/jsobj.h b/db/jsobj.h index 8d80c80144a..c3d87c83ff3 100644 --- a/db/jsobj.h +++ b/db/jsobj.h @@ -876,6 +876,7 @@ namespace mongo { opSIZE = 0x0A, opALL = 0x0B, NIN = 0x0C, + opEXISTS = 0x0D, }; }; ostream& operator<<( ostream &s, const BSONObj &o ); diff --git a/db/matcher.cpp b/db/matcher.cpp index 68e69a81654..bc6792c7e67 100644 --- a/db/matcher.cpp +++ b/db/matcher.cpp @@ -257,7 +257,7 @@ namespace mongo { ok = true; all = true; } - else if ( fn[1] == 's' && fn[2] == 'i' && fn[3] == 'z' && fn[4] == 'e' && fe.isNumber() ) { + else if ( fn[1] == 's' && fn[2] == 'i' && fn[3] == 'z' && fn[4] == 'e' && fn[5] == 0 && fe.isNumber() ) { shared_ptr< BSONObjBuilder > b( new BSONObjBuilder() ); builders_.push_back( b ); b->appendAs(fe, e.fieldName()); @@ -265,6 +265,13 @@ namespace mongo { haveSize = true; ok = true; } + else if ( fn[1] == 'e' && fn[2] == 'x' && fn[3] == 'i' && fn[4] == 's' && fn[5] == 't' && fn[6] == 's' && fn[7] == 0 && fe.isBoolean() ) { + shared_ptr< BSONObjBuilder > b( new BSONObjBuilder() ); + builders_.push_back( b ); + b->appendAs(fe, e.fieldName()); + addBasic(b->done().firstElement(), BSONObj::opEXISTS); + ok = true; + } else uassert( (string)"invalid $operator: " + fn , false); } @@ -326,6 +333,12 @@ namespace mongo { else return -ret; } + + int retMissing( const BasicMatcher &bm ) { + if ( bm.compareOp != BSONObj::opEXISTS ) + return 0; + return bm.toMatch.boolean() ? -1 : 1; + } /* Check if a particular field matches. @@ -349,25 +362,25 @@ namespace mongo { */ int JSMatcher::matchesDotted(const char *fieldName, const BSONElement& toMatch, const BSONObj& obj, int compareOp, const BasicMatcher& bm , bool *deep, bool isArr) { - if ( compareOp == BSONObj::opALL ) { - if ( bm.myset->size() == 0 ) - return -1; // is this desired? - BSONObjSetDefaultOrder actualKeys; - getKeysFromObject( BSON( fieldName << 1 ), obj, actualKeys ); - if ( actualKeys.size() == 0 ) - return 0; - for( set< BSONElement, element_lt >::const_iterator i = bm.myset->begin(); i != bm.myset->end(); ++i ) { - // ignore nulls - if ( i->type() == jstNULL ) - continue; - // parallel traversal would be faster worst case I guess - BSONObjBuilder b; - b.appendAs( *i, "" ); - if ( !actualKeys.count( b.done() ) ) - return -1; - } - return 1; - } + if ( compareOp == BSONObj::opALL ) { + if ( bm.myset->size() == 0 ) + return -1; // is this desired? + BSONObjSetDefaultOrder actualKeys; + getKeysFromObject( BSON( fieldName << 1 ), obj, actualKeys ); + if ( actualKeys.size() == 0 ) + return 0; + for( set< BSONElement, element_lt >::const_iterator i = bm.myset->begin(); i != bm.myset->end(); ++i ) { + // ignore nulls + if ( i->type() == jstNULL ) + continue; + // parallel traversal would be faster worst case I guess + BSONObjBuilder b; + b.appendAs( *i, "" ); + if ( !actualKeys.count( b.done() ) ) + return -1; + } + return 1; + } if ( compareOp == BSONObj::NE ) return matchesNe( fieldName, toMatch, obj, bm, deep ); @@ -376,7 +389,6 @@ namespace mongo { int ret = matchesNe( fieldName, *i, obj, bm, deep ); if ( ret != 1 ) return ret; - // code to handle 0 (missing) return value doesn't deal with nin yet } return 1; } @@ -403,7 +415,7 @@ namespace mongo { } } } - return found ? -1 : 0; + return found ? -1 : retMissing( bm ); } const char *p = strchr(fieldName, '.'); if ( p ) { @@ -411,9 +423,9 @@ namespace mongo { BSONElement se = obj.getField(left.c_str()); if ( se.eoo() ) - return 0; + return retMissing( bm ); if ( se.type() != Object && se.type() != Array ) - return 0; + return retMissing( bm ); BSONObj eo = se.embeddedObject(); return matchesDotted(p+1, toMatch, eo, compareOp, bm, deep, se.type() == Array); @@ -421,8 +433,10 @@ namespace mongo { e = obj.getField(fieldName); } } - - if ( ( e.type() != Array || indexed || compareOp == BSONObj::opSIZE ) && + + if ( compareOp == BSONObj::opEXISTS ) { + return ( e.eoo() ^ toMatch.boolean() ) ? 1 : -1; + } else if ( ( e.type() != Array || indexed || compareOp == BSONObj::opSIZE ) && valuesMatch(e, toMatch, compareOp, bm, deep) ) { return 1; } else if ( e.type() == Array && compareOp != BSONObj::opSIZE ) { diff --git a/jstests/exists.js b/jstests/exists.js index dd5a13d6dd5..20c3fe8dbd2 100644 --- a/jstests/exists.js +++ b/jstests/exists.js @@ -5,8 +5,28 @@ t.save( {} ); t.save( {a:1} ); t.save( {a:{b:1}} ); t.save( {a:{b:{c:1}}} ); +t.save( {a:{b:{c:{d:null}}}} ); -assert.eq( 4, t.count() ); -assert.eq( 3, t.count( {a:{$ne:null}} ) ); -assert.eq( 2, t.count( {'a.b':{$ne:null}} ) ); -assert.eq( 1, t.count( {'a.b.c':{$ne:null}} ) ); +assert.eq( 5, t.count() ); +assert.eq( 4, t.count( {a:{$ne:null}} ) ); +assert.eq( 3, t.count( {'a.b':{$ne:null}} ) ); +assert.eq( 2, t.count( {'a.b.c':{$ne:null}} ) ); +assert.eq( 0, t.count( {'a.b.c.d':{$ne:null}} ) ); + +assert.eq( 4, t.count( {a: {$exists:true}} ) ); +assert.eq( 3, t.count( {'a.b': {$exists:true}} ) ); +assert.eq( 2, t.count( {'a.b.c': {$exists:true}} ) ); +assert.eq( 1, t.count( {'a.b.c.d': {$exists:true}} ) ); + +assert.eq( 1, t.count( {a: {$exists:false}} ) ); +assert.eq( 2, t.count( {'a.b': {$exists:false}} ) ); +assert.eq( 3, t.count( {'a.b.c': {$exists:false}} ) ); +assert.eq( 4, t.count( {'a.b.c.d': {$exists:false}} ) ); + +t.drop(); + +t.save( {r:[{s:1}]} ); +assert( t.findOne( {'r.s':{$exists:true}} ) ); +assert( !t.findOne( {'r.s':{$exists:false}} ) ); +assert( !t.findOne( {'r.t':{$exists:true}} ) ); +assert( t.findOne( {'r.t':{$exists:false}} ) ); |