From e327eff2618d2caa9afda5c3eacb440cef23e4cd Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 1 Jun 2010 16:21:21 -0700 Subject: [PATCH] SERVER-109 reinstate FieldRangeOrSet, make FieldRangeSet a parameter to QueryPlanSet --- db/queryoptimizer.cpp | 57 ++++++++++---------- db/queryoptimizer.h | 9 ++-- db/queryutil.cpp | 120 +++++++++++++++++++++--------------------- db/queryutil.h | 68 ++++++++++++------------ 4 files changed, 129 insertions(+), 125 deletions(-) diff --git a/db/queryoptimizer.cpp b/db/queryoptimizer.cpp index 0f4b209c0c9..6dbc5e61e92 100644 --- a/db/queryoptimizer.cpp +++ b/db/queryoptimizer.cpp @@ -228,10 +228,10 @@ namespace mongo { } } - QueryPlanSet::QueryPlanSet( const char *_ns, const BSONObj &query, const BSONObj &order, const BSONElement *hint, bool honorRecordedPlan, const BSONObj &min, const BSONObj &max ) : + QueryPlanSet::QueryPlanSet( const char *_ns, auto_ptr< FieldRangeSet > frs, const BSONObj &query, const BSONObj &order, const BSONElement *hint, bool honorRecordedPlan, const BSONObj &min, const BSONObj &max ) : ns(_ns), query_( query.getOwned() ), - fbs_( _ns, query ), + fbs_( frs ), mayRecordPlan_( true ), usingPrerecordedPlan_( false ), hint_( BSONObj() ), @@ -252,10 +252,10 @@ namespace mongo { string errmsg; BSONObj keyPattern = id.keyPattern(); // This reformats min_ and max_ to be used for index lookup. - massert( 10365 , errmsg, indexDetailsForRange( fbs_.ns(), errmsg, min_, max_, keyPattern ) ); + massert( 10365 , errmsg, indexDetailsForRange( fbs_->ns(), errmsg, min_, max_, keyPattern ) ); } NamespaceDetails *d = nsdetails(ns); - plans_.push_back( PlanPtr( new QueryPlan( d, d->idxNo(id), fbs_, order_, min_, max_ ) ) ); + plans_.push_back( PlanPtr( new QueryPlan( d, d->idxNo(id), *fbs_, order_, min_, max_ ) ) ); } void QueryPlanSet::init() { @@ -264,11 +264,11 @@ namespace mongo { mayRecordPlan_ = true; usingPrerecordedPlan_ = false; - const char *ns = fbs_.ns(); + const char *ns = fbs_->ns(); NamespaceDetails *d = nsdetails( ns ); - if ( !d || !fbs_.matchPossible() ) { + if ( !d || !fbs_->matchPossible() ) { // Table scan plan, when no matches are possible - plans_.push_back( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ) ); + plans_.push_back( PlanPtr( new QueryPlan( d, -1, *fbs_, order_ ) ) ); return; } @@ -292,7 +292,7 @@ namespace mongo { if ( !strcmp( hintobj.firstElement().fieldName(), "$natural" ) ) { massert( 10366 , "natural order cannot be specified with $min/$max", min_.isEmpty() && max_.isEmpty() ); // Table scan plan - plans_.push_back( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ) ); + plans_.push_back( PlanPtr( new QueryPlan( d, -1, *fbs_, order_ ) ) ); return; } NamespaceDetails::IndexIterator i = d->ii(); @@ -312,7 +312,7 @@ namespace mongo { BSONObj keyPattern; IndexDetails *idx = indexDetailsForRange( ns, errmsg, min_, max_, keyPattern ); massert( 10367 , errmsg, idx ); - plans_.push_back( PlanPtr( new QueryPlan( d, d->idxNo(*idx), fbs_, order_, min_, max_ ) ) ); + plans_.push_back( PlanPtr( new QueryPlan( d, d->idxNo(*idx), *fbs_, order_, min_, max_ ) ) ); return; } @@ -321,19 +321,19 @@ namespace mongo { if ( idx >= 0 ){ usingPrerecordedPlan_ = true; mayRecordPlan_ = false; - plans_.push_back( PlanPtr( new QueryPlan( d , idx , fbs_ , order_ ) ) ); + plans_.push_back( PlanPtr( new QueryPlan( d , idx , *fbs_ , order_ ) ) ); return; } } if ( query_.isEmpty() && order_.isEmpty() ){ - plans_.push_back( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ) ); + plans_.push_back( PlanPtr( new QueryPlan( d, -1, *fbs_, order_ ) ) ); return; } - DEBUGQO( "\t special : " << fbs_.getSpecial() ); - if ( fbs_.getSpecial().size() ){ - _special = fbs_.getSpecial(); + DEBUGQO( "\t special : " << fbs_->getSpecial() ); + if ( fbs_->getSpecial().size() ){ + _special = fbs_->getSpecial(); NamespaceDetails::IndexIterator i = d->ii(); while( i.more() ) { int j = i.pos(); @@ -342,7 +342,7 @@ namespace mongo { if ( spec.getTypeName() == _special && spec.suitability( query_ , order_ ) ){ usingPrerecordedPlan_ = true; mayRecordPlan_ = true; - plans_.push_back( PlanPtr( new QueryPlan( d , j , fbs_ , order_ , + plans_.push_back( PlanPtr( new QueryPlan( d , j , *fbs_ , order_ , BSONObj() , BSONObj() , _special ) ) ); return; } @@ -353,14 +353,14 @@ namespace mongo { if ( honorRecordedPlan_ ) { scoped_lock lk(NamespaceDetailsTransient::_qcMutex); NamespaceDetailsTransient& nsd = NamespaceDetailsTransient::get_inlock( ns ); - BSONObj bestIndex = nsd.indexForPattern( fbs_.pattern( order_ ) ); + BSONObj bestIndex = nsd.indexForPattern( fbs_->pattern( order_ ) ); if ( !bestIndex.isEmpty() ) { usingPrerecordedPlan_ = true; mayRecordPlan_ = false; - oldNScanned_ = nsd.nScannedForPattern( fbs_.pattern( order_ ) ); + oldNScanned_ = nsd.nScannedForPattern( fbs_->pattern( order_ ) ); if ( !strcmp( bestIndex.firstElement().fieldName(), "$natural" ) ) { // Table scan plan - plans_.push_back( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ) ); + plans_.push_back( PlanPtr( new QueryPlan( d, -1, *fbs_, order_ ) ) ); return; } @@ -369,7 +369,7 @@ namespace mongo { int j = i.pos(); IndexDetails& ii = i.next(); if( ii.keyPattern().woCompare(bestIndex) == 0 ) { - plans_.push_back( PlanPtr( new QueryPlan( d, j, fbs_, order_ ) ) ); + plans_.push_back( PlanPtr( new QueryPlan( d, j, *fbs_, order_ ) ) ); return; } } @@ -381,16 +381,16 @@ namespace mongo { } void QueryPlanSet::addOtherPlans( bool checkFirst ) { - const char *ns = fbs_.ns(); + const char *ns = fbs_->ns(); NamespaceDetails *d = nsdetails( ns ); if ( !d ) return; // If table scan is optimal or natural order requested or tailable cursor requested - if ( !fbs_.matchPossible() || ( fbs_.nNontrivialRanges() == 0 && order_.isEmpty() ) || + if ( !fbs_->matchPossible() || ( fbs_->nNontrivialRanges() == 0 && order_.isEmpty() ) || ( !order_.isEmpty() && !strcmp( order_.firstElement().fieldName(), "$natural" ) ) ) { // Table scan plan - addPlan( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ), checkFirst ); + addPlan( PlanPtr( new QueryPlan( d, -1, *fbs_, order_ ) ), checkFirst ); return; } @@ -407,7 +407,7 @@ namespace mongo { continue; } - PlanPtr p( new QueryPlan( d, i, fbs_, order_ ) ); + PlanPtr p( new QueryPlan( d, i, *fbs_, order_ ) ); if ( p->optimal() ) { addPlan( p, checkFirst ); return; @@ -419,7 +419,7 @@ namespace mongo { addPlan( *i, checkFirst ); // Table scan plan - addPlan( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ), checkFirst ); + addPlan( PlanPtr( new QueryPlan( d, -1, *fbs_, order_ ) ), checkFirst ); } shared_ptr< QueryOp > QueryPlanSet::runOp( QueryOp &op ) { @@ -431,7 +431,7 @@ namespace mongo { return res; { scoped_lock lk(NamespaceDetailsTransient::_qcMutex); - NamespaceDetailsTransient::get_inlock( fbs_.ns() ).registerIndexForPattern( fbs_.pattern( order_ ), BSONObj(), 0 ); + NamespaceDetailsTransient::get_inlock( fbs_->ns() ).registerIndexForPattern( fbs_->pattern( order_ ), BSONObj(), 0 ); } init(); } @@ -571,7 +571,8 @@ namespace mongo { _or = false; } if ( !_or ) { - _currentQps.reset( new QueryPlanSet( ns, query, order, hint, honorRecordedPlan, min, max ) ); + auto_ptr< FieldRangeSet > frs( new FieldRangeSet( ns, query ) ); + _currentQps.reset( new QueryPlanSet( ns, frs, query, order, hint, honorRecordedPlan, min, max ) ); _n = 1; // only one run } else { BSONElement e = query.getField( "$or" ); @@ -586,7 +587,9 @@ namespace mongo { ++_i; return _currentQps->runOp( op ); } - _currentQps.reset( new QueryPlanSet( _ns, nextSimpleQuery(), BSONObj(), 0, _honorRecordedPlan ) ); + BSONObj q = nextSimpleQuery(); + auto_ptr< FieldRangeSet > frs( new FieldRangeSet( _ns, q ) ); + _currentQps.reset( new QueryPlanSet( _ns, frs, q, BSONObj(), 0, _honorRecordedPlan ) ); return _currentQps->runOp( op ); } diff --git a/db/queryoptimizer.h b/db/queryoptimizer.h index 23ecc7488c5..8528729f9eb 100644 --- a/db/queryoptimizer.h +++ b/db/queryoptimizer.h @@ -128,6 +128,7 @@ namespace mongo { typedef vector< PlanPtr > PlanSet; QueryPlanSet( const char *ns, + auto_ptr< FieldRangeSet > frs, const BSONObj &query, const BSONObj &order, const BSONElement *hint = 0, @@ -145,7 +146,7 @@ namespace mongo { PlanPtr getBestGuess() const; void setBestGuessOnly() { _bestGuessOnly = true; } //for testing - const FieldRangeSet &fbs() const { return fbs_; } + const FieldRangeSet &fbs() const { return *fbs_; } private: void addOtherPlans( bool checkFirst ); void addPlan( PlanPtr plan, bool checkFirst ) { @@ -165,7 +166,7 @@ namespace mongo { }; const char *ns; BSONObj query_; - FieldRangeSet fbs_; + auto_ptr< FieldRangeSet > fbs_; PlanSet plans_; bool mayRecordPlan_; bool usingPrerecordedPlan_; @@ -229,7 +230,6 @@ namespace mongo { } void setBestGuessOnly() { _bestGuessOnly = true; } private: - //temp void assertNotOr() const { massert( 13266, "not implemented for $or query", !_or ); } @@ -373,7 +373,8 @@ namespace mongo { if( !query.getField( "$or" ).eoo() ) { return shared_ptr< Cursor >( new MultiCursor( ns, query, sort ) ); } else { - shared_ptr< Cursor > ret = QueryPlanSet( ns, query, sort ).getBestGuess()->newCursor(); + auto_ptr< FieldRangeSet > frs( new FieldRangeSet( ns, query ) ); + shared_ptr< Cursor > ret = QueryPlanSet( ns, frs, query, sort ).getBestGuess()->newCursor(); if ( !query.isEmpty() ) { auto_ptr< CoveredIndexMatcher > matcher( new CoveredIndexMatcher( query, ret->indexKeyPattern() ) ); ret->setMatcher( matcher ); diff --git a/db/queryutil.cpp b/db/queryutil.cpp index 9293ca6fc0c..0cd6fc74d03 100644 --- a/db/queryutil.cpp +++ b/db/queryutil.cpp @@ -593,66 +593,66 @@ namespace mongo { } } -// FieldRangeOrSet::FieldRangeOrSet( const char *ns, const BSONObj &query , bool optimize ) -// : _baseSet( ns, query, optimize ), _orFound() { -// -// BSONObjIterator i( query ); -// -// while( i.more() ) { -// BSONElement e = i.next(); -// // e could be x:1 or x:{$gt:1} -// -// if ( strcmp( e.fieldName(), "$where" ) == 0 ) -// continue; -// -// if ( strcmp( e.fieldName(), "$or" ) == 0 ) { -// massert( 13262, "$or requires nonempty array", e.type() == Array && e.embeddedObject().nFields() > 0 ); -// BSONObjIterator j( e.embeddedObject() ); -// while( j.more() ) { -// BSONElement f = j.next(); -// massert( 13263, "$or array must contain objects", f.type() == Object ); -// _orSets.push_back( FieldRangeSet( ns, f.embeddedObject(), optimize ) ); -// } -// _orFound = true; -// continue; -// } -// -// bool equality = ( getGtLtOp( e ) == BSONObj::Equality ); -// if ( equality && e.type() == Object ) { -// equality = ( strcmp( e.embeddedObject().firstElement().fieldName(), "$not" ) != 0 ); -// } -// -// if ( equality || ( e.type() == Object && !e.embeddedObject()[ "$regex" ].eoo() ) ) { -// _baseSet._ranges[ e.fieldName() ] &= FieldRange( e , false , optimize ); -// } -// if ( !equality ) { -// BSONObjIterator j( e.embeddedObject() ); -// while( j.more() ) { -// BSONElement f = j.next(); -// if ( strcmp( f.fieldName(), "$not" ) == 0 ) { -// switch( f.type() ) { -// case Object: { -// BSONObjIterator k( f.embeddedObject() ); -// while( k.more() ) { -// BSONElement g = k.next(); -// uassert( 13264, "invalid use of $not", g.getGtLtOp() != BSONObj::Equality ); -// _baseSet.processOpElement( e.fieldName(), g, true, optimize ); -// } -// break; -// } -// case RegEx: -// _baseSet.processOpElement( e.fieldName(), f, true, optimize ); -// break; -// default: -// uassert( 13265, "invalid use of $not", false ); -// } -// } else { -// _baseSet.processOpElement( e.fieldName(), f, false, optimize ); -// } -// } -// } -// } -// } + FieldRangeOrSet::FieldRangeOrSet( const char *ns, const BSONObj &query , bool optimize ) + : _baseSet( ns, query, optimize ), _orFound() { + + BSONObjIterator i( query ); + + while( i.more() ) { + BSONElement e = i.next(); + // e could be x:1 or x:{$gt:1} + + if ( strcmp( e.fieldName(), "$where" ) == 0 ) + continue; + + if ( strcmp( e.fieldName(), "$or" ) == 0 ) { + massert( 13262, "$or requires nonempty array", e.type() == Array && e.embeddedObject().nFields() > 0 ); + BSONObjIterator j( e.embeddedObject() ); + while( j.more() ) { + BSONElement f = j.next(); + massert( 13263, "$or array must contain objects", f.type() == Object ); + _orSets.push_back( FieldRangeSet( ns, f.embeddedObject(), optimize ) ); + } + _orFound = true; + continue; + } + + bool equality = ( getGtLtOp( e ) == BSONObj::Equality ); + if ( equality && e.type() == Object ) { + equality = ( strcmp( e.embeddedObject().firstElement().fieldName(), "$not" ) != 0 ); + } + + if ( equality || ( e.type() == Object && !e.embeddedObject()[ "$regex" ].eoo() ) ) { + _baseSet._ranges[ e.fieldName() ] &= FieldRange( e , false , optimize ); + } + if ( !equality ) { + BSONObjIterator j( e.embeddedObject() ); + while( j.more() ) { + BSONElement f = j.next(); + if ( strcmp( f.fieldName(), "$not" ) == 0 ) { + switch( f.type() ) { + case Object: { + BSONObjIterator k( f.embeddedObject() ); + while( k.more() ) { + BSONElement g = k.next(); + uassert( 13264, "invalid use of $not", g.getGtLtOp() != BSONObj::Equality ); + _baseSet.processOpElement( e.fieldName(), g, true, optimize ); + } + break; + } + case RegEx: + _baseSet.processOpElement( e.fieldName(), f, true, optimize ); + break; + default: + uassert( 13265, "invalid use of $not", false ); + } + } else { + _baseSet.processOpElement( e.fieldName(), f, false, optimize ); + } + } + } + } + } FieldRange *FieldRangeSet::trivialRange_ = 0; FieldRange &FieldRangeSet::trivialRange() { diff --git a/db/queryutil.h b/db/queryutil.h index f872b69465a..87f8b0750b5 100644 --- a/db/queryutil.h +++ b/db/queryutil.h @@ -208,40 +208,40 @@ namespace mongo { }; // generages FieldRangeSet objects, accounting for or clauses -// class FieldRangeOrSet { -// public: -// FieldRangeOrSet( const char *ns, const BSONObj &query , bool optimize=true ); -// // if there's a trivial or clause, we won't use or ranges to help with scanning -// bool trivialOr() const { -// for( list< FieldRangeSet >::const_iterator i = _orSets.begin(); i != _orSets.end(); ++i ) { -// if ( i->nNontrivialRanges() == 0 ) { -// return true; -// } -// } -// return false; -// } -// bool orFinished() const { return _orFound && _orSets.empty(); } -// // removes first or clause, and removes the field ranges it covers from all subsequent or clauses -// void popOrClause() { -// massert( 13274, "no or clause to pop", !orFinished() ); -// const FieldRangeSet &toPop = _orSets.front(); -// list< FieldRangeSet >::iterator i = _orSets.begin(); -// ++i; -// while( i != _orSets.end() ) { -// *i -= toPop; -// if( !i->matchPossible() ) { -// i = _orSets.erase( i ); -// } else { -// ++i; -// } -// } -// _orSets.pop_front(); -// } -// private: -// FieldRangeSet _baseSet; -// list< FieldRangeSet > _orSets; -// bool _orFound; -// }; + class FieldRangeOrSet { + public: + FieldRangeOrSet( const char *ns, const BSONObj &query , bool optimize=true ); + // if there's a trivial or clause, we won't use or ranges to help with scanning + bool trivialOr() const { + for( list< FieldRangeSet >::const_iterator i = _orSets.begin(); i != _orSets.end(); ++i ) { + if ( i->nNontrivialRanges() == 0 ) { + return true; + } + } + return false; + } + bool orFinished() const { return _orFound && _orSets.empty(); } + // removes first or clause, and removes the field ranges it covers from all subsequent or clauses + void popOrClause() { + massert( 13274, "no or clause to pop", !orFinished() ); + const FieldRangeSet &toPop = _orSets.front(); + list< FieldRangeSet >::iterator i = _orSets.begin(); + ++i; + while( i != _orSets.end() ) { + *i -= toPop; + if( !i->matchPossible() ) { + i = _orSets.erase( i ); + } else { + ++i; + } + } + _orSets.pop_front(); + } + private: + FieldRangeSet _baseSet; + list< FieldRangeSet > _orSets; + bool _orFound; + }; /** used for doing field limiting