157 lines
5.2 KiB
C++
157 lines
5.2 KiB
C++
// distinct.cpp
|
|
|
|
/**
|
|
*
|
|
* 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.
|
|
*
|
|
* 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.
|
|
*
|
|
* 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 "pch.h"
|
|
#include "../commands.h"
|
|
#include "../instance.h"
|
|
#include "../queryoptimizer.h"
|
|
#include "../clientcursor.h"
|
|
|
|
namespace mongo {
|
|
|
|
class DistinctCommand : public Command {
|
|
public:
|
|
DistinctCommand() : Command("distinct") {}
|
|
virtual bool slaveOk() const { return true; }
|
|
virtual LockType locktype() const { return READ; }
|
|
virtual void help( stringstream &help ) const {
|
|
help << "{ distinct : 'collection name' , key : 'a.b' , query : {} }";
|
|
}
|
|
|
|
bool run(const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl ) {
|
|
Timer t;
|
|
string ns = dbname + '.' + cmdObj.firstElement().valuestr();
|
|
|
|
string key = cmdObj["key"].valuestrsafe();
|
|
BSONObj keyPattern = BSON( key << 1 );
|
|
|
|
BSONObj query = getQuery( cmdObj );
|
|
|
|
int bufSize = BSONObjMaxUserSize - 4096;
|
|
BufBuilder bb( bufSize );
|
|
char * start = bb.buf();
|
|
|
|
BSONArrayBuilder arr( bb );
|
|
BSONElementSet values;
|
|
|
|
long long nscanned = 0; // locations looked at
|
|
long long nscannedObjects = 0; // full objects looked at
|
|
long long n = 0; // matches
|
|
MatchDetails md;
|
|
|
|
NamespaceDetails * d = nsdetails( ns.c_str() );
|
|
|
|
if ( ! d ) {
|
|
result.appendArray( "values" , BSONObj() );
|
|
result.append( "stats" , BSON( "n" << 0 << "nscanned" << 0 << "nscannedObjects" << 0 ) );
|
|
return true;
|
|
}
|
|
|
|
shared_ptr<Cursor> cursor;
|
|
if ( ! query.isEmpty() ) {
|
|
cursor = NamespaceDetailsTransient::getCursor(ns.c_str() , query , BSONObj() );
|
|
}
|
|
else {
|
|
|
|
// query is empty, so lets see if we can find an index
|
|
// with the key so we don't have to hit the raw data
|
|
NamespaceDetails::IndexIterator ii = d->ii();
|
|
while ( ii.more() ) {
|
|
IndexDetails& idx = ii.next();
|
|
|
|
if ( d->isMultikey( ii.pos() - 1 ) )
|
|
continue;
|
|
|
|
if ( idx.inKeyPattern( key ) ) {
|
|
cursor = bestGuessCursor( ns.c_str() , BSONObj() , idx.keyPattern() );
|
|
if( cursor.get() ) break;
|
|
}
|
|
|
|
}
|
|
|
|
if ( ! cursor.get() )
|
|
cursor = NamespaceDetailsTransient::getCursor(ns.c_str() , query , BSONObj() );
|
|
|
|
}
|
|
|
|
|
|
assert( cursor );
|
|
string cursorName = cursor->toString();
|
|
|
|
auto_ptr<ClientCursor> cc (new ClientCursor(QueryOption_NoCursorTimeout, cursor, ns));
|
|
|
|
while ( cursor->ok() ) {
|
|
nscanned++;
|
|
bool loadedObject = false;
|
|
|
|
if ( ( !cursor->matcher() || cursor->matcher()->matchesCurrent( cursor.get() , &md ) ) &&
|
|
!cursor->getsetdup( cursor->currLoc() ) ) {
|
|
n++;
|
|
|
|
BSONObj holder;
|
|
BSONElementSet temp;
|
|
loadedObject = ! cc->getFieldsDotted( key , temp, holder );
|
|
|
|
for ( BSONElementSet::iterator i=temp.begin(); i!=temp.end(); ++i ) {
|
|
BSONElement e = *i;
|
|
if ( values.count( e ) )
|
|
continue;
|
|
|
|
int now = bb.len();
|
|
|
|
uassert(10044, "distinct too big, 16mb cap", ( now + e.size() + 1024 ) < bufSize );
|
|
|
|
arr.append( e );
|
|
BSONElement x( start + now );
|
|
|
|
values.insert( x );
|
|
}
|
|
}
|
|
|
|
if ( loadedObject || md._loadedObject )
|
|
nscannedObjects++;
|
|
|
|
cursor->advance();
|
|
|
|
if (!cc->yieldSometimes( ClientCursor::MaybeCovered )) {
|
|
cc.release();
|
|
break;
|
|
}
|
|
|
|
RARELY killCurrentOp.checkForInterrupt();
|
|
}
|
|
|
|
assert( start == bb.buf() );
|
|
|
|
result.appendArray( "values" , arr.done() );
|
|
|
|
{
|
|
BSONObjBuilder b;
|
|
b.appendNumber( "n" , n );
|
|
b.appendNumber( "nscanned" , nscanned );
|
|
b.appendNumber( "nscannedObjects" , nscannedObjects );
|
|
b.appendNumber( "timems" , t.millis() );
|
|
b.append( "cursor" , cursorName );
|
|
result.append( "stats" , b.obj() );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} distinctCmd;
|
|
|
|
}
|