diff options
Diffstat (limited to 'db/matcher.cpp')
-rw-r--r-- | db/matcher.cpp | 218 |
1 files changed, 175 insertions, 43 deletions
diff --git a/db/matcher.cpp b/db/matcher.cpp index 8c904e3..681a6dc 100644 --- a/db/matcher.cpp +++ b/db/matcher.cpp @@ -18,7 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "stdafx.h" +#include "pch.h" #include "matcher.h" #include "../util/goodies.h" #include "../util/unittest.h" @@ -50,7 +50,9 @@ namespace { #define DEBUGMATCHER(x) namespace mongo { - + + extern BSONObj staticNull; + class Where { public: Where() { @@ -141,39 +143,6 @@ namespace mongo { } - CoveredIndexMatcher::CoveredIndexMatcher(const BSONObj &jsobj, const BSONObj &indexKeyPattern) : - _keyMatcher(jsobj.filterFieldsUndotted(indexKeyPattern, true), - indexKeyPattern), - _docMatcher(jsobj) - { - _needRecord = ! ( - _docMatcher.keyMatch() && - _keyMatcher.jsobj.nFields() == _docMatcher.jsobj.nFields() && - ! _keyMatcher.hasType( BSONObj::opEXISTS ) - ); - - } - - bool CoveredIndexMatcher::matches(const BSONObj &key, const DiskLoc &recLoc , MatchDetails * details ) { - if ( details ) - details->reset(); - - if ( _keyMatcher.keyMatch() ) { - if ( !_keyMatcher.matches(key, details ) ){ - return false; - } - } - - if ( ! _needRecord ){ - return true; - } - - if ( details ) - details->loadedObject = true; - - return _docMatcher.matches(recLoc.rec() , details ); - } - void Matcher::addRegex(const char *fieldName, const char *regex, const char *flags, bool isNot){ @@ -230,9 +199,11 @@ namespace mongo { case BSONObj::opALL: all = true; case BSONObj::opIN: + uassert( 13276 , "$in needs an array" , fe.isABSONObj() ); basics.push_back( ElementMatcher( e , op , fe.embeddedObject(), isNot ) ); break; case BSONObj::NIN: + uassert( 13277 , "$nin needs an array" , fe.isABSONObj() ); haveNeg = true; basics.push_back( ElementMatcher( e , op , fe.embeddedObject(), isNot ) ); break; @@ -279,6 +250,7 @@ namespace mongo { } case BSONObj::opNEAR: case BSONObj::opWITHIN: + case BSONObj::opMAX_DISTANCE: break; default: uassert( 10069 , (string)"BUG - can't operator for: " + fn , 0 ); @@ -286,19 +258,50 @@ namespace mongo { return true; } + void Matcher::parseOr( const BSONElement &e, bool subMatcher, list< shared_ptr< Matcher > > &matchers ) { + uassert( 13090, "recursive $or/$nor not allowed", !subMatcher ); + uassert( 13086, "$or/$nor must be a nonempty array", e.type() == Array && e.embeddedObject().nFields() > 0 ); + BSONObjIterator j( e.embeddedObject() ); + while( j.more() ) { + BSONElement f = j.next(); + uassert( 13087, "$or/$nor match element must be an object", f.type() == Object ); + // until SERVER-109 this is never a covered index match, so don't constrain index key for $or matchers + matchers.push_back( shared_ptr< Matcher >( new Matcher( f.embeddedObject(), true ) ) ); + } + } + + bool Matcher::parseOrNor( const BSONElement &e, bool subMatcher ) { + const char *ef = e.fieldName(); + if ( ef[ 0 ] != '$' ) + return false; + if ( ef[ 1 ] == 'o' && ef[ 2 ] == 'r' && ef[ 3 ] == 0 ) { + parseOr( e, subMatcher, _orMatchers ); + } else if ( ef[ 1 ] == 'n' && ef[ 2 ] == 'o' && ef[ 3 ] == 'r' && ef[ 4 ] == 0 ) { + parseOr( e, subMatcher, _norMatchers ); + } else { + return false; + } + return true; + } + /* _jsobj - the query pattern */ - Matcher::Matcher(const BSONObj &_jsobj, const BSONObj &constrainIndexKey) : + Matcher::Matcher(const BSONObj &_jsobj, bool subMatcher) : where(0), jsobj(_jsobj), haveSize(), all(), hasArray(0), haveNeg(), _atomic(false), nRegex(0) { BSONObjIterator i(jsobj); while ( i.more() ) { BSONElement e = i.next(); + + if ( parseOrNor( e, subMatcher ) ) { + continue; + } if ( ( e.type() == CodeWScope || e.type() == Code || e.type() == String ) && strcmp(e.fieldName(), "$where")==0 ) { // $where: function()... - uassert( 10066 , "$where occurs twice?", where == 0 ); - uassert( 10067 , "$where query, but no script engine", globalScriptEngine ); + uassert( 10066 , "$where occurs twice?", where == 0 ); + uassert( 10067 , "$where query, but no script engine", globalScriptEngine ); + massert( 13089 , "no current client needed for $where" , haveClient() ); where = new Where(); where->scope = globalScriptEngine->getPooledScope( cc().ns() ); where->scope->localConnect( cc().database()->name.c_str() ); @@ -348,7 +351,7 @@ namespace mongo { BSONObjIterator k( fe.embeddedObject() ); uassert( 13030, "$not cannot be empty", k.more() ); while( k.more() ) { - addOp( e, k.next(), true, regex, flags ); + addOp( e, k.next(), true, regex, flags ); } break; } @@ -388,8 +391,35 @@ namespace mongo { // normal, simple case e.g. { a : "foo" } addBasic(e, BSONObj::Equality, false); } - - constrainIndexKey_ = constrainIndexKey; + } + + Matcher::Matcher( const Matcher &other, const BSONObj &key ) : + where(0), constrainIndexKey_( key ), haveSize(), all(), hasArray(0), haveNeg(), _atomic(false), nRegex(0) { + // do not include fields which would make keyMatch() false + for( vector< ElementMatcher >::const_iterator i = other.basics.begin(); i != other.basics.end(); ++i ) { + if ( key.hasField( i->toMatch.fieldName() ) ) { + switch( i->compareOp ) { + case BSONObj::opSIZE: + case BSONObj::opALL: + case BSONObj::NE: + case BSONObj::NIN: + break; + default: { + if ( !i->isNot && i->toMatch.type() != Array ) { + basics.push_back( *i ); + } + } + } + } + } + for( int i = 0; i < other.nRegex; ++i ) { + if ( !other.regexs[ i ].isNot && key.hasField( other.regexs[ i ].fieldName ) ) { + regexs[ nRegex++ ] = other.regexs[ i ]; + } + } + for( list< shared_ptr< Matcher > >::const_iterator i = other._orMatchers.begin(); i != other._orMatchers.end(); ++i ) { + _orMatchers.push_back( shared_ptr< Matcher >( new Matcher( **i, key ) ) ); + } } inline bool regexMatches(const RegexMatcher& rm, const BSONElement& e) { @@ -711,7 +741,7 @@ namespace mongo { return false; if ( cmp == 0 ) { /* missing is ok iff we were looking for null */ - if ( m.type() == jstNULL || m.type() == Undefined ) { + if ( m.type() == jstNULL || m.type() == Undefined || ( bm.compareOp == BSONObj::opIN && bm.myset->count( staticNull.firstElement() ) > 0 ) ) { if ( ( bm.compareOp == BSONObj::NE ) ^ bm.isNot ) { return false; } @@ -741,6 +771,42 @@ namespace mongo { return false; } + if ( _orMatchers.size() > 0 ) { + bool match = false; + for( list< shared_ptr< Matcher > >::const_iterator i = _orMatchers.begin(); + i != _orMatchers.end(); ++i ) { + // SERVER-205 don't submit details - we don't want to track field + // matched within $or, and at this point we've already loaded the + // whole document + if ( (*i)->matches( jsobj ) ) { + match = true; + break; + } + } + if ( !match ) { + return false; + } + } + + if ( _norMatchers.size() > 0 ) { + for( list< shared_ptr< Matcher > >::const_iterator i = _norMatchers.begin(); + i != _norMatchers.end(); ++i ) { + // SERVER-205 don't submit details - we don't want to track field + // matched within $nor, and at this point we've already loaded the + // whole document + if ( (*i)->matches( jsobj ) ) { + return false; + } + } + } + + for( vector< shared_ptr< FieldRangeVector > >::const_iterator i = _orConstraints.begin(); + i != _orConstraints.end(); ++i ) { + if ( (*i)->matches( jsobj ) ) { + return false; + } + } + if ( where ) { if ( where->func == 0 ) { uassert( 10070 , "$where compile error", false); @@ -769,7 +835,7 @@ namespace mongo { return where->scope->getBoolean( "return" ) != 0; } - + return true; } @@ -780,6 +846,72 @@ namespace mongo { return false; } + bool Matcher::sameCriteriaCount( const Matcher &other ) const { + if ( !( basics.size() == other.basics.size() && nRegex == other.nRegex && !where == !other.where ) ) { + return false; + } + if ( _norMatchers.size() != other._norMatchers.size() ) { + return false; + } + if ( _orMatchers.size() != other._orMatchers.size() ) { + return false; + } + if ( _orConstraints.size() != other._orConstraints.size() ) { + return false; + } + { + list< shared_ptr< Matcher > >::const_iterator i = _norMatchers.begin(); + list< shared_ptr< Matcher > >::const_iterator j = other._norMatchers.begin(); + while( i != _norMatchers.end() ) { + if ( !(*i)->sameCriteriaCount( **j ) ) { + return false; + } + ++i; ++j; + } + } + { + list< shared_ptr< Matcher > >::const_iterator i = _orMatchers.begin(); + list< shared_ptr< Matcher > >::const_iterator j = other._orMatchers.begin(); + while( i != _orMatchers.end() ) { + if ( !(*i)->sameCriteriaCount( **j ) ) { + return false; + } + ++i; ++j; + } + } + return true; + } + + + /*- just for testing -- */ +#pragma pack(1) + struct JSObj1 { + JSObj1() { + totsize=sizeof(JSObj1); + n = NumberDouble; + strcpy_s(nname, 5, "abcd"); + N = 3.1; + s = String; + strcpy_s(sname, 7, "abcdef"); + slen = 10; + strcpy_s(sval, 10, "123456789"); + eoo = EOO; + } + unsigned totsize; + + char n; + char nname[5]; + double N; + + char s; + char sname[7]; + unsigned slen; + char sval[10]; + + char eoo; + }; +#pragma pack() + struct JSObj1 js1; #pragma pack(1) |