diff --git a/db/dbcommands.cpp b/db/dbcommands.cpp index 95088346b5a..bc5ad99956a 100644 --- a/db/dbcommands.cpp +++ b/db/dbcommands.cpp @@ -1472,13 +1472,25 @@ namespace mongo { set map; long long size = 0; - - auto_ptr cursor = db.query( ns , query , 0 , 0 , &keyPattern ); - while ( cursor->more() ){ - BSONObj o = cursor->next(); + + auto_ptr cursor = QueryPlanSet(ns.c_str() , query , BSONObj() ).getBestGuess()->newCursor(); + auto_ptr matcher; + if ( ! query.isEmpty() ) + matcher.reset( new CoveredIndexMatcher( query , cursor->indexKeyPattern() ) ); + + while ( cursor->ok() ){ + if ( matcher.get() && ! matcher->matchesCurrent( cursor.get() ) ){ + cursor->advance(); + continue; + } + + BSONObj o = cursor->current(); + cursor->advance(); + BSONElement e = o.getFieldDotted( key.c_str() ); if ( ! e.type() ) continue; + if ( map.insert( e ).second ){ size += o.objsize() + 20; uassert( 10044 , "distinct too big, 4mb cap" , size < 4 * 1024 * 1024 ); diff --git a/db/matcher.cpp b/db/matcher.cpp index 8c904e36638..d77ac94431f 100644 --- a/db/matcher.cpp +++ b/db/matcher.cpp @@ -153,6 +153,10 @@ namespace mongo { ); } + + bool CoveredIndexMatcher::matchesCurrent( Cursor * cursor , MatchDetails * details ){ + return matches( cursor->currKey() , cursor->currLoc() , details ); + } bool CoveredIndexMatcher::matches(const BSONObj &key, const DiskLoc &recLoc , MatchDetails * details ) { if ( details ) diff --git a/db/matcher.h b/db/matcher.h index 3839b68aab5..6e2cad7233e 100644 --- a/db/matcher.h +++ b/db/matcher.h @@ -24,7 +24,8 @@ #include namespace mongo { - + + class Cursor; class CoveredIndexMatcher; class Matcher; @@ -190,6 +191,7 @@ namespace mongo { CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKeyPattern); bool matches(const BSONObj &o){ return _docMatcher.matches( o ); } bool matches(const BSONObj &key, const DiskLoc &recLoc , MatchDetails * details = 0 ); + bool matchesCurrent( Cursor * cursor , MatchDetails * details = 0 ); bool needRecord(){ return _needRecord; } Matcher& docMatcher() { return _docMatcher; } diff --git a/db/queryoptimizer.cpp b/db/queryoptimizer.cpp index fa08323a973..a71db42d236 100644 --- a/db/queryoptimizer.cpp +++ b/db/queryoptimizer.cpp @@ -451,6 +451,11 @@ namespace mongo { b.append( "allPlans", arr ); return b.obj(); } + + QueryPlanSet::PlanPtr QueryPlanSet::getBestGuess() const { + assert( plans_.size() ); + return plans_[0]; + } QueryPlanSet::Runner::Runner( QueryPlanSet &plans, QueryOp &op ) : op_( op ), diff --git a/db/queryoptimizer.h b/db/queryoptimizer.h index 1cb50528253..1d00f8a0b8e 100644 --- a/db/queryoptimizer.h +++ b/db/queryoptimizer.h @@ -117,6 +117,10 @@ namespace mongo { // a QueryOp on these plans. class QueryPlanSet { public: + + typedef boost::shared_ptr< QueryPlan > PlanPtr; + typedef vector< PlanPtr > PlanSet; + QueryPlanSet( const char *ns, const BSONObj &query, const BSONObj &order, @@ -133,10 +137,9 @@ namespace mongo { const FieldRangeSet &fbs() const { return fbs_; } BSONObj explain() const; bool usingPrerecordedPlan() const { return usingPrerecordedPlan_; } + PlanPtr getBestGuess() const; private: void addOtherPlans( bool checkFirst ); - typedef boost::shared_ptr< QueryPlan > PlanPtr; - typedef vector< PlanPtr > PlanSet; void addPlan( PlanPtr plan, bool checkFirst ) { if ( checkFirst && plan->indexKey().woCompare( plans_[ 0 ]->indexKey() ) == 0 ) return; diff --git a/jstests/distinct_speed1.js b/jstests/distinct_speed1.js index fa2f3b9ab67..4cae5b0ae06 100644 --- a/jstests/distinct_speed1.js +++ b/jstests/distinct_speed1.js @@ -20,3 +20,7 @@ for ( i=0; i<3; i++ ){ print( "it: " + Date.timeFunc( fast ) ); print( "di: " + Date.timeFunc( slow ) ); } + + +t.ensureIndex( { x : 1 } ); +t.distinct( "x" , { x : 5 } )