Files
mongo/db/queryoptimizer.cpp

270 lines
9.1 KiB
C++
Raw Normal View History

2008-11-15 19:04:01 -05:00
/* queryoptimizer.cpp */
2008-11-14 16:19:47 -05:00
/**
* Copyright (C) 2008 10gen Inc.
2008-12-28 20:28:49 -05:00
*
2008-11-14 16:19:47 -05:00
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
2008-12-28 20:28:49 -05:00
*
2008-11-14 16:19:47 -05:00
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
2008-12-28 20:28:49 -05:00
*
2008-11-14 16:19:47 -05:00
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stdafx.h"
#include "pdfile.h"
#include "queryoptimizer.h"
2009-01-14 17:09:51 -05:00
namespace mongo {
2009-02-19 14:23:07 -05:00
FieldBound::FieldBound( const BSONElement &e ) :
2009-02-17 15:53:19 -05:00
lower_( minKey.firstElement() ),
upper_( maxKey.firstElement() ) {
if ( e.eoo() )
return;
if ( e.type() == RegEx ) {
const char *r = e.simpleRegex();
if ( r ) {
lower_ = addObj( BSON( "" << r ) ).firstElement();
upper_ = addObj( BSON( "" << simpleRegexEnd( r ) ) ).firstElement();
}
return;
}
switch( e.getGtLtOp() ) {
case JSMatcher::Equality:
lower_ = e;
upper_ = e;
break;
case JSMatcher::LT:
case JSMatcher::LTE:
upper_ = e;
break;
case JSMatcher::GT:
case JSMatcher::GTE:
lower_ = e;
break;
2009-02-17 19:39:35 -05:00
case JSMatcher::opIN: {
massert( "$in requires array", e.type() == Array );
BSONElement max = minKey.firstElement();
BSONElement min = maxKey.firstElement();
BSONObjIterator i( e.embeddedObject() );
while( i.more() ) {
BSONElement f = i.next();
if ( f.eoo() )
break;
if ( max.woCompare( f, false ) < 0 )
max = f;
if ( min.woCompare( f, false ) > 0 )
min = f;
}
lower_ = min;
upper_ = max;
}
2009-02-17 15:53:19 -05:00
default:
break;
}
}
FieldBound &FieldBound::operator&=( const FieldBound &other ) {
if ( other.upper_.woCompare( upper_, false ) < 0 )
upper_ = other.upper_;
if ( other.lower_.woCompare( lower_, false ) > 0 )
lower_ = other.lower_;
2009-02-17 19:39:35 -05:00
for( vector< BSONObj >::const_iterator i = other.objData_.begin(); i != other.objData_.end(); ++i )
objData_.push_back( *i );
2009-02-17 19:46:37 -05:00
massert( "Incompatible bounds", lower_.woCompare( upper_, false ) <= 0 );
2009-02-17 15:53:19 -05:00
return *this;
}
string FieldBound::simpleRegexEnd( string regex ) {
++regex[ regex.length() - 1 ];
return regex;
}
2009-02-19 14:23:07 -05:00
BSONObj FieldBound::addObj( const BSONObj &o ) {
2009-02-17 15:53:19 -05:00
objData_.push_back( o );
return o;
}
2009-02-19 14:23:07 -05:00
FieldBoundSet::FieldBoundSet( const BSONObj &query ) :
2009-02-17 15:53:19 -05:00
query_( query.copy() ) {
BSONObjIterator i( query_ );
while( i.more() ) {
BSONElement e = i.next();
if ( e.eoo() )
break;
2009-02-17 19:39:35 -05:00
if ( getGtLtOp( e ) == JSMatcher::Equality ) {
2009-02-17 15:53:19 -05:00
bounds_[ e.fieldName() ] &= FieldBound( e );
2009-02-17 19:39:35 -05:00
}
2009-02-17 15:53:19 -05:00
else {
BSONObjIterator i( e.embeddedObject() );
while( i.more() ) {
BSONElement f = i.next();
if ( f.eoo() )
break;
bounds_[ e.fieldName() ] &= FieldBound( f );
}
}
}
}
FieldBound *FieldBoundSet::trivialBound_ = 0;
FieldBound &FieldBoundSet::trivialBound() {
if ( trivialBound_ == 0 )
trivialBound_ = new FieldBound();
return *trivialBound_;
}
2009-02-17 20:57:36 -05:00
2009-02-19 14:23:07 -05:00
QueryPlan::QueryPlan( const FieldBoundSet &fbs, const BSONObj &order, const BSONObj &idxKey ) :
2009-02-17 20:57:36 -05:00
optimal_( false ),
scanAndOrderRequired_( true ),
keyMatch_( false ),
2009-02-19 13:58:22 -05:00
exactKeyMatch_( false ),
direction_( 0 ) {
// full table scan case
if ( idxKey.isEmpty() ) {
if ( order.isEmpty() )
scanAndOrderRequired_ = false;
return;
}
2009-02-17 20:57:36 -05:00
BSONObjIterator o( order );
BSONObjIterator k( idxKey );
2009-02-18 15:25:50 -05:00
if ( !o.more() )
scanAndOrderRequired_ = false;
2009-02-17 20:57:36 -05:00
while( o.more() ) {
BSONElement oe = o.next();
if ( oe.eoo() ) {
scanAndOrderRequired_ = false;
break;
}
2009-02-18 15:25:50 -05:00
if ( !k.more() )
2009-02-17 20:57:36 -05:00
break;
2009-02-18 15:25:50 -05:00
BSONElement ke;
while( 1 ) {
ke = k.next();
if ( ke.eoo() )
goto doneCheckOrder;
if ( strcmp( oe.fieldName(), ke.fieldName() ) == 0 )
break;
if ( !fbs.bound( ke.fieldName() ).equality() )
goto doneCheckOrder;
}
2009-02-17 20:57:36 -05:00
int d = oe.number() == ke.number() ? 1 : -1;
2009-02-19 13:58:22 -05:00
if ( direction_ == 0 )
direction_ = d;
else if ( direction_ != d )
break;
2009-02-17 20:57:36 -05:00
}
2009-02-18 15:25:50 -05:00
doneCheckOrder:
2009-02-19 13:58:22 -05:00
if ( scanAndOrderRequired_ )
direction_ = 0;
2009-02-18 15:25:50 -05:00
BSONObjIterator i( idxKey );
int indexedQueryCount = 0;
int exactIndexedQueryCount = 0;
2009-02-19 10:26:46 -05:00
int optimalIndexedQueryCount = 0;
bool stillOptimalIndexedQueryCount = true;
2009-02-18 15:25:50 -05:00
set< string > orderFieldsUnindexed;
order.getFieldNames( orderFieldsUnindexed );
while( i.more() ) {
BSONElement e = i.next();
if ( e.eoo() )
break;
const FieldBound &fb = fbs.bound( e.fieldName() );
if ( fb.nontrivial() )
2009-02-18 15:25:50 -05:00
++indexedQueryCount;
2009-02-19 10:26:46 -05:00
if ( stillOptimalIndexedQueryCount ) {
if ( fb.nontrivial() )
++optimalIndexedQueryCount;
if ( !fb.equality() )
stillOptimalIndexedQueryCount = false;
} else {
if ( fb.nontrivial() )
optimalIndexedQueryCount = -1;
}
if ( fb.equality() ) {
BSONElement e = fb.upper();
if ( !e.isNumber() && !e.mayEncapsulate() && e.type() != RegEx )
++exactIndexedQueryCount;
}
2009-02-18 15:25:50 -05:00
orderFieldsUnindexed.erase( e.fieldName() );
}
if ( !scanAndOrderRequired_ &&
2009-02-19 10:26:46 -05:00
( optimalIndexedQueryCount == fbs.nNontrivialBounds() ) )
optimal_ = true;
2009-02-18 15:25:50 -05:00
if ( indexedQueryCount == fbs.nNontrivialBounds() &&
orderFieldsUnindexed.size() == 0 ) {
keyMatch_ = true;
if ( exactIndexedQueryCount == fbs.nNontrivialBounds() )
2009-02-18 15:25:50 -05:00
exactKeyMatch_ = true;
}
}
2009-02-17 20:57:36 -05:00
2009-02-19 14:23:07 -05:00
QueryPlanSet::QueryPlanSet( const char *ns, const BSONObj &query, const BSONObj &order, const BSONElement *hint ) :
fbs_( query ) {
2009-02-19 13:58:22 -05:00
NamespaceDetails *d = nsdetails( ns );
assert( d );
if ( hint && !hint->eoo() ) {
if( hint->type() == String ) {
string hintstr = hint->valuestr();
for (int i = 0; i < d->nIndexes; i++ ) {
IndexDetails& ii = d->indexes[i];
if ( ii.indexName() == hintstr ) {
plans_.push_back( QueryPlan( fbs_, order, ii.keyPattern() ) );
return;
}
}
}
else if( hint->type() == Object ) {
BSONObj hintobj = hint->embeddedObject();
for (int i = 0; i < d->nIndexes; i++ ) {
IndexDetails& ii = d->indexes[i];
if( ii.keyPattern().woCompare(hintobj) == 0 ) {
plans_.push_back( QueryPlan( fbs_, order, ii.keyPattern() ) );
return;
}
}
}
uassert( "bad hint", false );
}
// Table scan plan
plans_.push_back( QueryPlan( fbs_, order, emptyObj ) );
2009-02-19 13:58:22 -05:00
// If table scan is optimal
if ( fbs_.nNontrivialBounds() == 0 && order.isEmpty() )
return;
vector< QueryPlan > plans;
for( int i = 0; i < d->nIndexes; ++i ) {
QueryPlan p( fbs_, order, d->indexes[ i ].keyPattern() );
if ( p.optimal() ) {
plans_.push_back( p );
return;
}
plans.push_back( p );
}
for( vector< QueryPlan >::iterator i = plans.begin(); i != plans.end(); ++i )
plans_.push_back( *i );
}
2009-02-17 20:57:36 -05:00
// QueryPlan QueryOptimizer::getPlan(
// const char *ns,
// BSONObj* query,
// BSONObj* order,
// BSONObj* hint)
// {
// QueryPlan plan;
//
//
//
// return plan;
// }
2009-01-14 17:09:51 -05:00
} // namespace mongo