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"
|
2009-02-20 11:08:22 -05:00
|
|
|
|
|
|
|
|
#include "btree.h"
|
2008-11-14 16:19:47 -05:00
|
|
|
#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-20 11:08:22 -05:00
|
|
|
FieldBoundSet::FieldBoundSet( const char *ns, const BSONObj &query ) :
|
|
|
|
|
ns_( ns ),
|
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 );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-17 23:09:37 -05:00
|
|
|
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-20 11:08:22 -05:00
|
|
|
QueryPlan::QueryPlan( const FieldBoundSet &fbs, const BSONObj &order, const IndexDetails *index ) :
|
|
|
|
|
fbs_( fbs ),
|
|
|
|
|
order_( order ),
|
|
|
|
|
index_( index ),
|
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 ) {
|
2009-02-19 10:01:20 -05:00
|
|
|
// full table scan case
|
2009-02-20 11:08:22 -05:00
|
|
|
if ( !index_ ) {
|
|
|
|
|
if ( order_.isEmpty() )
|
2009-02-19 10:01:20 -05:00
|
|
|
scanAndOrderRequired_ = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-02-20 11:08:22 -05:00
|
|
|
|
|
|
|
|
BSONObj idxKey = index->keyPattern();
|
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;
|
2009-02-19 10:01:20 -05:00
|
|
|
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 );
|
2009-02-19 15:04:10 -05:00
|
|
|
BSONObjBuilder lowKeyBuilder;
|
|
|
|
|
BSONObjBuilder highKeyBuilder;
|
2009-02-18 15:25:50 -05:00
|
|
|
while( i.more() ) {
|
|
|
|
|
BSONElement e = i.next();
|
|
|
|
|
if ( e.eoo() )
|
|
|
|
|
break;
|
2009-02-19 10:01:20 -05:00
|
|
|
const FieldBound &fb = fbs.bound( e.fieldName() );
|
2009-02-19 15:04:10 -05:00
|
|
|
lowKeyBuilder.appendAs( fb.lower(), "" );
|
|
|
|
|
highKeyBuilder.appendAs( fb.upper(), "" );
|
2009-02-19 10:01:20 -05:00
|
|
|
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;
|
2009-02-19 10:01:20 -05:00
|
|
|
}
|
|
|
|
|
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() );
|
|
|
|
|
}
|
2009-02-19 10:01:20 -05:00
|
|
|
if ( !scanAndOrderRequired_ &&
|
2009-02-19 10:26:46 -05:00
|
|
|
( optimalIndexedQueryCount == fbs.nNontrivialBounds() ) )
|
2009-02-19 10:01:20 -05:00
|
|
|
optimal_ = true;
|
2009-02-18 15:25:50 -05:00
|
|
|
if ( indexedQueryCount == fbs.nNontrivialBounds() &&
|
|
|
|
|
orderFieldsUnindexed.size() == 0 ) {
|
|
|
|
|
keyMatch_ = true;
|
2009-02-19 10:01:20 -05:00
|
|
|
if ( exactIndexedQueryCount == fbs.nNontrivialBounds() )
|
2009-02-18 15:25:50 -05:00
|
|
|
exactKeyMatch_ = true;
|
|
|
|
|
}
|
2009-02-19 15:04:10 -05:00
|
|
|
BSONObj lowKey = lowKeyBuilder.obj();
|
|
|
|
|
BSONObj highKey = highKeyBuilder.obj();
|
|
|
|
|
startKey_ = ( direction_ >= 0 ) ? lowKey : highKey;
|
|
|
|
|
endKey_ = ( direction_ >= 0 ) ? highKey : lowKey;
|
2009-01-15 10:17:11 -05:00
|
|
|
}
|
2009-02-17 20:57:36 -05:00
|
|
|
|
2009-02-20 11:08:22 -05:00
|
|
|
auto_ptr< Cursor > QueryPlan::newCursor() const {
|
|
|
|
|
if ( !index_ )
|
|
|
|
|
return theDataFileMgr.findAll( fbs_.ns() );
|
|
|
|
|
else
|
2009-02-20 15:19:41 -05:00
|
|
|
return auto_ptr< Cursor >( new BtreeCursor( *const_cast< IndexDetails* >( index_ ), startKey_, endKey_, direction_ >= 0 ? 1 : -1 ) );
|
2009-02-20 11:08:22 -05:00
|
|
|
//TODO This constructor should really take a const ref to the index details.
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-20 15:19:41 -05:00
|
|
|
BSONObj QueryPlan::indexKey() const {
|
|
|
|
|
return index_->keyPattern();
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-19 14:23:07 -05:00
|
|
|
QueryPlanSet::QueryPlanSet( const char *ns, const BSONObj &query, const BSONObj &order, const BSONElement *hint ) :
|
2009-02-20 11:08:22 -05:00
|
|
|
fbs_( ns, 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 ) {
|
2009-02-20 11:08:22 -05:00
|
|
|
plans_.push_back( PlanPtr( new QueryPlan( fbs_, order, &ii ) ) );
|
2009-02-19 13:58:22 -05:00
|
|
|
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 ) {
|
2009-02-20 11:08:22 -05:00
|
|
|
plans_.push_back( PlanPtr( new QueryPlan( fbs_, order, &ii ) ) );
|
2009-02-19 13:58:22 -05:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
uassert( "bad hint", false );
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-19 10:01:20 -05:00
|
|
|
// Table scan plan
|
2009-02-20 11:08:22 -05:00
|
|
|
plans_.push_back( PlanPtr( new QueryPlan( fbs_, order ) ) );
|
2009-02-19 10:01:20 -05:00
|
|
|
|
2009-02-19 13:58:22 -05:00
|
|
|
// If table scan is optimal
|
2009-02-19 10:01:20 -05:00
|
|
|
if ( fbs_.nNontrivialBounds() == 0 && order.isEmpty() )
|
|
|
|
|
return;
|
|
|
|
|
|
2009-02-20 11:08:22 -05:00
|
|
|
PlanSet plans;
|
2009-02-19 10:01:20 -05:00
|
|
|
for( int i = 0; i < d->nIndexes; ++i ) {
|
2009-02-20 11:08:22 -05:00
|
|
|
PlanPtr p( new QueryPlan( fbs_, order, &d->indexes[ i ] ) );
|
|
|
|
|
if ( p->optimal() ) {
|
2009-02-19 10:01:20 -05:00
|
|
|
plans_.push_back( p );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
plans.push_back( p );
|
|
|
|
|
}
|
2009-02-20 11:08:22 -05:00
|
|
|
for( PlanSet::iterator i = plans.begin(); i != plans.end(); ++i )
|
2009-02-19 10:01:20 -05:00
|
|
|
plans_.push_back( *i );
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-20 11:08:22 -05:00
|
|
|
auto_ptr< QueryOp > QueryPlanSet::runOp( QueryOp &op ) {
|
|
|
|
|
RunnerSet s( *this, op );
|
|
|
|
|
return s.run();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QueryPlanSet::RunnerSet::RunnerSet( QueryPlanSet &plans, QueryOp &op ) :
|
|
|
|
|
op_( op ),
|
|
|
|
|
plans_( plans ),
|
|
|
|
|
startBarrier_( plans_.nPlans() ),
|
|
|
|
|
firstDone_( false ) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto_ptr< QueryOp > QueryPlanSet::RunnerSet::run() {
|
|
|
|
|
boost::thread_group threads;
|
|
|
|
|
auto_ptr< QueryOp > ops[ plans_.nPlans() ];
|
|
|
|
|
for( int i = 0; i < plans_.nPlans(); ++i ) {
|
|
|
|
|
ops[ i ] = auto_ptr< QueryOp >( op_.clone() );
|
|
|
|
|
Runner r( *plans_.plans_[ i ], *this, *ops[ i ] );
|
|
|
|
|
threads.create_thread( r );
|
|
|
|
|
}
|
|
|
|
|
threads.join_all();
|
|
|
|
|
for( int i = 0; i < plans_.nPlans(); ++i )
|
|
|
|
|
if ( ops[ i ]->done() )
|
|
|
|
|
return ops[ i ];
|
|
|
|
|
assert( false );
|
|
|
|
|
return auto_ptr< QueryOp >( 0 );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class CountOp : public QueryOp {
|
|
|
|
|
public:
|
2009-02-20 15:19:41 -05:00
|
|
|
CountOp( const BSONObj &spec ) : spec_( spec ), count_() {}
|
2009-02-20 11:08:22 -05:00
|
|
|
virtual void run( const QueryPlan &qp, QueryAborter &qa ) {
|
2009-02-20 15:19:41 -05:00
|
|
|
BSONObj query = spec_.getObjectField("query");
|
|
|
|
|
set< string > fields;
|
|
|
|
|
spec_.getObjectField("fields").getFieldNames( fields );
|
|
|
|
|
|
|
|
|
|
auto_ptr<Cursor> c = qp.newCursor();
|
|
|
|
|
|
|
|
|
|
// TODO We could check if all fields in the key are in 'fields'
|
|
|
|
|
if ( qp.exactKeyMatch() && fields.empty() ) {
|
|
|
|
|
/* Here we only look at the btree keys to determine if a match, instead of looking
|
|
|
|
|
into the records, which would be much slower.
|
|
|
|
|
*/
|
|
|
|
|
BtreeCursor *bc = dynamic_cast<BtreeCursor *>(c.get());
|
|
|
|
|
if ( c->ok() && !query.woCompare( bc->currKeyNode().key, BSONObj(), false ) ) {
|
|
|
|
|
BSONObj firstMatch = bc->currKeyNode().key;
|
|
|
|
|
count_++;
|
|
|
|
|
while ( c->advance() ) {
|
|
|
|
|
qa.mayAbort();
|
|
|
|
|
if ( !firstMatch.woEqual( bc->currKeyNode().key ) )
|
|
|
|
|
break;
|
|
|
|
|
count_++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto_ptr<JSMatcher> matcher(new JSMatcher(query, c->indexKeyPattern()));
|
|
|
|
|
while ( c->ok() ) {
|
2009-02-20 11:08:22 -05:00
|
|
|
qa.mayAbort();
|
2009-02-20 15:19:41 -05:00
|
|
|
BSONObj js = c->current();
|
|
|
|
|
bool deep;
|
|
|
|
|
if ( !matcher->matches(js, &deep) ) {
|
|
|
|
|
}
|
|
|
|
|
else if ( !deep || !c->getsetdup(c->currLoc()) ) { // i.e., check for dups on deep items only
|
|
|
|
|
bool match = true;
|
|
|
|
|
for( set< string >::iterator i = fields.begin(); i != fields.end(); ++i ) {
|
|
|
|
|
if ( js.getFieldDotted( i->c_str() ).eoo() ) {
|
|
|
|
|
match = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( match )
|
|
|
|
|
++count_;
|
|
|
|
|
}
|
|
|
|
|
c->advance();
|
|
|
|
|
}
|
2009-02-20 11:08:22 -05:00
|
|
|
}
|
|
|
|
|
virtual QueryOp *clone() const {
|
|
|
|
|
return new CountOp( *this );
|
|
|
|
|
}
|
2009-02-20 15:19:41 -05:00
|
|
|
int count() const { return count_; }
|
|
|
|
|
private:
|
|
|
|
|
BSONObj spec_;
|
|
|
|
|
int count_;
|
2009-02-20 11:08:22 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int doCount( const char *ns, const BSONObj &cmd, string &err ) {
|
|
|
|
|
BSONObj query = cmd.getObjectField("query");
|
2009-02-20 15:19:41 -05:00
|
|
|
BSONObj fields = cmd.getObjectField("fields");
|
|
|
|
|
// count of all objects
|
|
|
|
|
if ( query.isEmpty() && fields.isEmpty() ) {
|
|
|
|
|
NamespaceDetails *d = nsdetails( ns );
|
|
|
|
|
massert( "ns missing", d );
|
|
|
|
|
return d->nrecords;
|
|
|
|
|
}
|
2009-02-20 11:08:22 -05:00
|
|
|
QueryPlanSet qps( ns, query, emptyObj );
|
2009-02-20 15:19:41 -05:00
|
|
|
auto_ptr< QueryOp > original( new CountOp( cmd ) );
|
2009-02-20 11:08:22 -05:00
|
|
|
auto_ptr< QueryOp > o = qps.runOp( *original );
|
|
|
|
|
return dynamic_cast< CountOp* >( o.get() )->count();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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
|