diff --git a/db/index.h b/db/index.h index 5d393f7c1d9..6665521542a 100644 --- a/db/index.h +++ b/db/index.h @@ -46,6 +46,8 @@ namespace mongo { const IndexPlugin * getPlugin() const { return _plugin; } + virtual auto_ptr newCursor( const BSONObj& query , const BSONObj& order ) const = 0; + virtual BSONObj fixKey( const BSONObj& in ) { return in; } protected: diff --git a/db/index_geo2d.cpp b/db/index_geo2d.cpp index e9a8d3d6a61..2434168b8ac 100644 --- a/db/index_geo2d.cpp +++ b/db/index_geo2d.cpp @@ -327,6 +327,12 @@ namespace mongo { return _spec->getDetails(); } + virtual auto_ptr newCursor( const BSONObj& query , const BSONObj& order ) const { + auto_ptr c; + assert(0); + return c; + } + const IndexSpec* _spec; string _geo; vector _other; diff --git a/db/jsobj.cpp b/db/jsobj.cpp index ec08c25b453..d2fe7a46564 100644 --- a/db/jsobj.cpp +++ b/db/jsobj.cpp @@ -436,8 +436,12 @@ namespace mongo { else if ( fn[3] == 'e' && fn[4] == 0 ) return BSONObj::LTE; } } - else if ( fn[1] == 'n' && fn[2] == 'e' && fn[3] == 0) - return BSONObj::NE; + else if ( fn[1] == 'n' && fn[2] == 'e' ){ + if ( fn[3] == 0 ) + return BSONObj::NE; + if ( fn[3] == 'a' && fn[4] == 'r' && fn[5] == 0 ) + return BSONObj::opNEAR; + } else if ( fn[1] == 'm' && fn[2] == 'o' && fn[3] == 'd' && fn[4] == 0 ) return BSONObj::opMOD; else if ( fn[1] == 't' && fn[2] == 'y' && fn[3] == 'p' && fn[4] == 'e' && fn[5] == 0 ) diff --git a/db/jsobj.h b/db/jsobj.h index f3ecca6081f..c93e7274034 100644 --- a/db/jsobj.h +++ b/db/jsobj.h @@ -993,7 +993,8 @@ namespace mongo { opTYPE = 0x0F, opREGEX = 0x10, opOPTIONS = 0x11, - opELEM_MATCH = 0x12 + opELEM_MATCH = 0x12, + opNEAR = 0x13 }; }; ostream& operator<<( ostream &s, const BSONObj &o ); diff --git a/db/matcher.cpp b/db/matcher.cpp index ba57b1d5303..cc675257a47 100644 --- a/db/matcher.cpp +++ b/db/matcher.cpp @@ -248,6 +248,8 @@ namespace mongo { flags = fe.valuestrsafe(); break; } + case BSONObj::opNEAR: + break; default: uassert( 10069 , (string)"BUG - can't operator for: " + fn , 0 ); } diff --git a/db/queryoptimizer.cpp b/db/queryoptimizer.cpp index 616dc5a72b1..aeecadfb03f 100644 --- a/db/queryoptimizer.cpp +++ b/db/queryoptimizer.cpp @@ -63,7 +63,7 @@ namespace mongo { QueryPlan::QueryPlan( NamespaceDetails *_d, int _idxNo, - const FieldRangeSet &fbs, const BSONObj &order, const BSONObj &startKey, const BSONObj &endKey ) : + const FieldRangeSet &fbs, const BSONObj &order, const BSONObj &startKey, const BSONObj &endKey , string special ) : d(_d), idxNo(_idxNo), fbs_( fbs ), order_( order ), @@ -73,7 +73,8 @@ namespace mongo { exactKeyMatch_( false ), direction_( 0 ), endKeyInclusive_( endKey.isEmpty() ), - unhelpful_( false ) { + unhelpful_( false ), + _special( special ){ if ( !fbs_.matchPossible() ) { unhelpful_ = true; @@ -90,6 +91,12 @@ namespace mongo { return; } + if ( _special.size() ){ + optimal_ = true; + scanAndOrderRequired_ = false; // TODO: maybe part of key spec? + return; + } + BSONObj idxKey = index_->keyPattern(); BSONObjIterator o( order ); BSONObjIterator k( idxKey ); @@ -179,6 +186,13 @@ namespace mongo { } auto_ptr< Cursor > QueryPlan::newCursor( const DiskLoc &startLoc ) const { + + if ( _special.size() ){ + IndexType * type = index_->getSpec().getType(); + massert( 13040 , (string)"no type for special: " + _special , type ); + return type->newCursor( fbs_.query() , order_ ); + } + if ( !fbs_.matchPossible() ){ if ( fbs_.nNontrivialRanges() ) checkTableScanAllowed( fbs_.ns() ); @@ -322,6 +336,23 @@ namespace mongo { } } + if ( fbs_.getSpecial().size() ){ + string special = fbs_.getSpecial(); + NamespaceDetails::IndexIterator i = d->ii(); + while( i.more() ) { + int j = i.pos(); + IndexDetails& ii = i.next(); + if ( ii.getSpec().getTypeName() == special ){ + usingPrerecordedPlan_ = true; + mayRecordPlan_ = true; + plans_.push_back( PlanPtr( new QueryPlan( d , j , fbs_ , order_ , + BSONObj() , BSONObj() , special ) ) ); + return; + } + } + uassert( 13038 , (string)"can't find special index: " + special , 0 ); + } + if ( honorRecordedPlan_ ) { boostlock lk(NamespaceDetailsTransient::_qcMutex); NamespaceDetailsTransient& nsd = NamespaceDetailsTransient::get_inlock( ns ); diff --git a/db/queryoptimizer.h b/db/queryoptimizer.h index 5a8ba3efab5..9ca89a20ca0 100644 --- a/db/queryoptimizer.h +++ b/db/queryoptimizer.h @@ -32,7 +32,8 @@ namespace mongo { const FieldRangeSet &fbs, const BSONObj &order, const BSONObj &startKey = BSONObj(), - const BSONObj &endKey = BSONObj() ); + const BSONObj &endKey = BSONObj() , + string special="" ); /* If true, no other index can do better. */ bool optimal() const { return optimal_; } @@ -70,6 +71,7 @@ namespace mongo { BoundList indexBounds_; bool endKeyInclusive_; bool unhelpful_; + string _special; }; // Inherit from this interface to implement a new query operation. diff --git a/db/queryutil.cpp b/db/queryutil.cpp index 02fe2ae42dd..7b67fe303f7 100644 --- a/db/queryutil.cpp +++ b/db/queryutil.cpp @@ -253,6 +253,9 @@ namespace mongo { log() << "warning: shouldn't get here?" << endl; break; } + case BSONObj::opNEAR: + _special = "2d"; + break; default: break; } @@ -312,6 +315,8 @@ namespace mongo { intervals_ = newIntervals; for( vector< BSONObj >::const_iterator i = other.objData_.begin(); i != other.objData_.end(); ++i ) objData_.push_back( *i ); + if ( _special.size() == 0 && other._special.size() ) + _special = other._special; return *this; } @@ -325,6 +330,17 @@ namespace mongo { return o; } + string FieldRangeSet::getSpecial() const { + string s = ""; + for ( map::iterator i=ranges_.begin(); i!=ranges_.end(); i++ ){ + if ( i->second.getSpecial().size() == 0 ) + continue; + uassert( 13033 , "can't have 2 special fields" , s.size() == 0 ); + s = i->second.getSpecial(); + } + return s; + } + void FieldRangeSet::processOpElement( const char *fieldName, const BSONElement &f, bool isNot, bool optimize ) { int op2 = f.getGtLtOp(); if ( op2 == BSONObj::opELEM_MATCH ) { @@ -389,7 +405,7 @@ namespace mongo { processOpElement( e.fieldName(), f, true, optimize ); break; default: - uassert( 13033, "invalid use of $not", false ); + uassert( 13041, "invalid use of $not", false ); } } else { processOpElement( e.fieldName(), f, false, optimize ); diff --git a/db/queryutil.h b/db/queryutil.h index 9e2abecad24..a819e5a4003 100644 --- a/db/queryutil.h +++ b/db/queryutil.h @@ -69,11 +69,14 @@ namespace mongo { } bool empty() const { return intervals_.empty(); } const vector< FieldInterval > &intervals() const { return intervals_; } + string getSpecial() const { return _special; } + private: BSONObj addObj( const BSONObj &o ); string simpleRegexEnd( string regex ); vector< FieldInterval > intervals_; vector< BSONObj > objData_; + string _special; }; // implements query pattern matching, used to determine if a query is @@ -171,6 +174,7 @@ namespace mongo { } QueryPattern pattern( const BSONObj &sort = BSONObj() ) const; BoundList indexBounds( const BSONObj &keyPattern, int direction ) const; + string getSpecial() const; private: void processOpElement( const char *fieldName, const BSONElement &f, bool isNot, bool optimize ); static FieldRange *trivialRange_; diff --git a/jstests/geo2.js b/jstests/geo2.js index 2d8f17d2b5d..81d807d5758 100644 --- a/jstests/geo2.js +++ b/jstests/geo2.js @@ -34,3 +34,6 @@ printjson( slow.stats ) slow.results.forEach( p ) */ + + +//t.find( { loc : { $near : [ 50 , 50 ] } } ).itcount();