mirror of
https://github.com/mongodb/mongo.git
synced 2024-12-01 09:32:32 +01:00
Merge branch 'master' of git@github.com:mongodb/mongo
This commit is contained in:
commit
b911ce8b25
@ -278,15 +278,15 @@ namespace mongo {
|
||||
}
|
||||
} cmdclone;
|
||||
|
||||
/* Usage:
|
||||
mydb.$cmd.findOne( { cloneCollection: 1, fromhost: <hostname>, collection: <collectionname>, query: <query> } );
|
||||
*/
|
||||
class CmdCloneCollection : public Command {
|
||||
public:
|
||||
virtual bool slaveOk() {
|
||||
return false;
|
||||
}
|
||||
CmdCloneCollection() : Command("cloneCollection") { }
|
||||
virtual void help( stringstream &help ) const {
|
||||
help << " example: { cloneCollection: 1, fromhost: <hostname>, collection: <collectionname>, query: <query> }";
|
||||
}
|
||||
virtual bool run(const char *ns, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
|
||||
string fromhost = cmdObj.getStringField("fromhost");
|
||||
if ( fromhost.empty() )
|
||||
|
@ -837,11 +837,18 @@ namespace mongo {
|
||||
( c->slaveOverrideOk() && ( queryOptions & Option_SlaveOk ) ) ||
|
||||
fromRepl )
|
||||
{
|
||||
if( admin || logLevel >= 2 )
|
||||
log() << "command: " << jsobj.toString() << endl;
|
||||
ok = c->run(ns, jsobj, errmsg, anObjBuilder, fromRepl);
|
||||
if ( ok && c->logTheOp() && !fromRepl )
|
||||
logOp("c", ns, jsobj);
|
||||
if ( jsobj.getBoolField( "help" ) ) {
|
||||
stringstream help;
|
||||
help << "help for: " << e.fieldName() << " ";
|
||||
c->help( help );
|
||||
anObjBuilder.append( "help" , help.str() );
|
||||
} else {
|
||||
if( admin )
|
||||
log( 2 ) << "command: " << jsobj << endl;
|
||||
ok = c->run(ns, jsobj, errmsg, anObjBuilder, fromRepl);
|
||||
if ( ok && c->logTheOp() && !fromRepl )
|
||||
logOp("c", ns, jsobj);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ok = false;
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
namespace mongo {
|
||||
|
||||
FieldBound::FieldBound( BSONElement e ) :
|
||||
FieldBound::FieldBound( const BSONElement &e ) :
|
||||
lower_( minKey.firstElement() ),
|
||||
upper_( maxKey.firstElement() ) {
|
||||
if ( e.eoo() )
|
||||
@ -86,12 +86,12 @@ namespace mongo {
|
||||
return regex;
|
||||
}
|
||||
|
||||
BSONObj FieldBound::addObj( BSONObj o ) {
|
||||
BSONObj FieldBound::addObj( const BSONObj &o ) {
|
||||
objData_.push_back( o );
|
||||
return o;
|
||||
}
|
||||
|
||||
FieldBoundSet::FieldBoundSet( BSONObj query ) :
|
||||
FieldBoundSet::FieldBoundSet( const BSONObj &query ) :
|
||||
query_( query.copy() ) {
|
||||
BSONObjIterator i( query_ );
|
||||
while( i.more() ) {
|
||||
@ -120,11 +120,12 @@ namespace mongo {
|
||||
return *trivialBound_;
|
||||
}
|
||||
|
||||
QueryPlan::QueryPlan( const FieldBoundSet &fbs, BSONObj order, BSONObj idxKey ) :
|
||||
QueryPlan::QueryPlan( const FieldBoundSet &fbs, const BSONObj &order, const BSONObj &idxKey ) :
|
||||
optimal_( false ),
|
||||
scanAndOrderRequired_( true ),
|
||||
keyMatch_( false ),
|
||||
exactKeyMatch_( false ) {
|
||||
exactKeyMatch_( false ),
|
||||
direction_( 0 ) {
|
||||
// full table scan case
|
||||
if ( idxKey.isEmpty() ) {
|
||||
if ( order.isEmpty() )
|
||||
@ -133,7 +134,6 @@ namespace mongo {
|
||||
}
|
||||
BSONObjIterator o( order );
|
||||
BSONObjIterator k( idxKey );
|
||||
int direction = 0;
|
||||
if ( !o.more() )
|
||||
scanAndOrderRequired_ = false;
|
||||
while( o.more() ) {
|
||||
@ -155,12 +155,14 @@ namespace mongo {
|
||||
goto doneCheckOrder;
|
||||
}
|
||||
int d = oe.number() == ke.number() ? 1 : -1;
|
||||
if ( direction == 0 )
|
||||
direction = d;
|
||||
else if ( direction != d )
|
||||
break;
|
||||
if ( direction_ == 0 )
|
||||
direction_ = d;
|
||||
else if ( direction_ != d )
|
||||
break;
|
||||
}
|
||||
doneCheckOrder:
|
||||
if ( scanAndOrderRequired_ )
|
||||
direction_ = 0;
|
||||
BSONObjIterator i( idxKey );
|
||||
int indexedQueryCount = 0;
|
||||
int exactIndexedQueryCount = 0;
|
||||
@ -168,11 +170,15 @@ namespace mongo {
|
||||
bool stillOptimalIndexedQueryCount = true;
|
||||
set< string > orderFieldsUnindexed;
|
||||
order.getFieldNames( orderFieldsUnindexed );
|
||||
BSONObjBuilder lowKeyBuilder;
|
||||
BSONObjBuilder highKeyBuilder;
|
||||
while( i.more() ) {
|
||||
BSONElement e = i.next();
|
||||
if ( e.eoo() )
|
||||
break;
|
||||
const FieldBound &fb = fbs.bound( e.fieldName() );
|
||||
lowKeyBuilder.appendAs( fb.lower(), "" );
|
||||
highKeyBuilder.appendAs( fb.upper(), "" );
|
||||
if ( fb.nontrivial() )
|
||||
++indexedQueryCount;
|
||||
if ( stillOptimalIndexedQueryCount ) {
|
||||
@ -200,18 +206,48 @@ namespace mongo {
|
||||
if ( exactIndexedQueryCount == fbs.nNontrivialBounds() )
|
||||
exactKeyMatch_ = true;
|
||||
}
|
||||
BSONObj lowKey = lowKeyBuilder.obj();
|
||||
BSONObj highKey = highKeyBuilder.obj();
|
||||
startKey_ = ( direction_ >= 0 ) ? lowKey : highKey;
|
||||
endKey_ = ( direction_ >= 0 ) ? highKey : lowKey;
|
||||
}
|
||||
|
||||
QueryPlanSet::QueryPlanSet( const char *ns, BSONObj query, BSONObj order ) :
|
||||
QueryPlanSet::QueryPlanSet( const char *ns, const BSONObj &query, const BSONObj &order, const BSONElement *hint ) :
|
||||
fbs_( query ) {
|
||||
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 ) );
|
||||
|
||||
// If table scan is optimal
|
||||
if ( fbs_.nNontrivialBounds() == 0 && order.isEmpty() )
|
||||
return;
|
||||
|
||||
NamespaceDetails *d = nsdetails( ns );
|
||||
assert( d );
|
||||
vector< QueryPlan > plans;
|
||||
for( int i = 0; i < d->nIndexes; ++i ) {
|
||||
QueryPlan p( fbs_, order, d->indexes[ i ].keyPattern() );
|
||||
|
@ -25,7 +25,7 @@ namespace mongo {
|
||||
|
||||
class FieldBound {
|
||||
public:
|
||||
FieldBound( BSONElement e = emptyObj.firstElement() );
|
||||
FieldBound( const BSONElement &e = emptyObj.firstElement() );
|
||||
FieldBound &operator&=( const FieldBound &other );
|
||||
BSONElement lower() const { return lower_; }
|
||||
BSONElement upper() const { return upper_; }
|
||||
@ -36,7 +36,7 @@ namespace mongo {
|
||||
maxKey.firstElement().woCompare( upper_, false ) != 0;
|
||||
}
|
||||
private:
|
||||
BSONObj addObj( BSONObj o );
|
||||
BSONObj addObj( const BSONObj &o );
|
||||
string simpleRegexEnd( string regex );
|
||||
BSONElement lower_;
|
||||
BSONElement upper_;
|
||||
@ -45,7 +45,7 @@ namespace mongo {
|
||||
|
||||
class FieldBoundSet {
|
||||
public:
|
||||
FieldBoundSet( BSONObj query );
|
||||
FieldBoundSet( const BSONObj &query );
|
||||
const FieldBound &bound( const char *fieldName ) const {
|
||||
map< string, FieldBound >::const_iterator f = bounds_.find( fieldName );
|
||||
if ( f == bounds_.end() )
|
||||
@ -74,7 +74,7 @@ namespace mongo {
|
||||
|
||||
class QueryPlan {
|
||||
public:
|
||||
QueryPlan( const FieldBoundSet &fbs, BSONObj order, BSONObj idxKey );
|
||||
QueryPlan( const FieldBoundSet &fbs, const BSONObj &order, const BSONObj &idxKey );
|
||||
/* If true, no other index can do better. */
|
||||
bool optimal() const { return optimal_; }
|
||||
/* ScanAndOrder processing will be required if true */
|
||||
@ -85,16 +85,22 @@ namespace mongo {
|
||||
bool keyMatch() const { return keyMatch_; }
|
||||
/* True if keyMatch() is true, and all matches will be equal according to woEqual() */
|
||||
bool exactKeyMatch() const { return exactKeyMatch_; }
|
||||
int direction() const { return direction_; }
|
||||
BSONObj startKey() const { return startKey_; }
|
||||
BSONObj endKey() const { return endKey_; }
|
||||
private:
|
||||
bool optimal_;
|
||||
bool scanAndOrderRequired_;
|
||||
bool keyMatch_;
|
||||
bool exactKeyMatch_;
|
||||
int direction_;
|
||||
BSONObj startKey_;
|
||||
BSONObj endKey_;
|
||||
};
|
||||
|
||||
class QueryPlanSet {
|
||||
public:
|
||||
QueryPlanSet( const char *ns, BSONObj query, BSONObj order );
|
||||
QueryPlanSet( const char *ns, const BSONObj &query, const BSONObj &order, const BSONElement *hint = 0 );
|
||||
int nPlans() const { return plans_.size(); }
|
||||
private:
|
||||
FieldBoundSet fbs_;
|
||||
|
@ -177,12 +177,23 @@ namespace QueryOptimizerTests {
|
||||
class SimpleOrder {
|
||||
public:
|
||||
void run() {
|
||||
BSONObjBuilder b;
|
||||
b.appendMinKey( "" );
|
||||
BSONObj start = b.obj();
|
||||
BSONObjBuilder b2;
|
||||
b2.appendMaxKey( "" );
|
||||
BSONObj end = b2.obj();
|
||||
|
||||
QueryPlan p( FieldBoundSet( emptyObj ), BSON( "a" << 1 ), BSON( "a" << 1 ) );
|
||||
ASSERT( !p.scanAndOrderRequired() );
|
||||
ASSERT( !p.startKey().woCompare( start ) );
|
||||
ASSERT( !p.endKey().woCompare( end ) );
|
||||
QueryPlan p2( FieldBoundSet( emptyObj ), BSON( "a" << 1 << "b" << 1 ), BSON( "a" << 1 << "b" << 1 ) );
|
||||
ASSERT( !p2.scanAndOrderRequired() );
|
||||
QueryPlan p3( FieldBoundSet( emptyObj ), BSON( "b" << 1 ), BSON( "a" << 1 ) );
|
||||
ASSERT( p3.scanAndOrderRequired() );
|
||||
ASSERT( !p3.startKey().woCompare( start ) );
|
||||
ASSERT( !p3.endKey().woCompare( end ) );
|
||||
}
|
||||
};
|
||||
|
||||
@ -199,30 +210,57 @@ namespace QueryOptimizerTests {
|
||||
void run() {
|
||||
QueryPlan p( FieldBoundSet( emptyObj ), BSON( "a" << 1 << "b" << -1 ), BSON( "a" << 1 << "b" << -1 ) );
|
||||
ASSERT( !p.scanAndOrderRequired() );
|
||||
ASSERT_EQUALS( 1, p.direction() );
|
||||
QueryPlan p2( FieldBoundSet( emptyObj ), BSON( "a" << 1 << "b" << -1 ), BSON( "a" << 1 << "b" << 1 ) );
|
||||
ASSERT( p2.scanAndOrderRequired() );
|
||||
ASSERT_EQUALS( 0, p2.direction() );
|
||||
}
|
||||
};
|
||||
|
||||
class IndexReverse {
|
||||
public:
|
||||
void run() {
|
||||
BSONObjBuilder b;
|
||||
b.appendMinKey( "" );
|
||||
b.appendMinKey( "" );
|
||||
BSONObj low = b.obj();
|
||||
BSONObjBuilder b2;
|
||||
b2.appendMaxKey( "" );
|
||||
b2.appendMaxKey( "" );
|
||||
BSONObj high = b2.obj();
|
||||
QueryPlan p( FieldBoundSet( emptyObj ), BSON( "a" << 1 << "b" << -1 ), BSON( "a" << -1 << "b" << 1 ) );
|
||||
ASSERT( !p.scanAndOrderRequired() );
|
||||
ASSERT_EQUALS( -1, p.direction() );
|
||||
ASSERT( !p.endKey().woCompare( low ) );
|
||||
ASSERT( !p.startKey().woCompare( high ) );
|
||||
QueryPlan p2( FieldBoundSet( emptyObj ), BSON( "a" << -1 << "b" << -1 ), BSON( "a" << 1 << "b" << 1 ) );
|
||||
ASSERT( !p2.scanAndOrderRequired() );
|
||||
ASSERT_EQUALS( -1, p2.direction() );
|
||||
QueryPlan p3( FieldBoundSet( emptyObj ), BSON( "a" << -1 << "b" << -1 ), BSON( "a" << 1 << "b" << -1 ) );
|
||||
ASSERT( p3.scanAndOrderRequired() );
|
||||
ASSERT_EQUALS( 0, p3.direction() );
|
||||
}
|
||||
};
|
||||
|
||||
class NoOrder {
|
||||
public:
|
||||
void run() {
|
||||
BSONObjBuilder b;
|
||||
b.append( "", 3 );
|
||||
b.appendMinKey( "" );
|
||||
BSONObj start = b.obj();
|
||||
BSONObjBuilder b2;
|
||||
b2.append( "", 3 );
|
||||
b2.appendMaxKey( "" );
|
||||
BSONObj end = b2.obj();
|
||||
QueryPlan p( FieldBoundSet( BSON( "a" << 3 ) ), emptyObj, BSON( "a" << -1 << "b" << 1 ) );
|
||||
ASSERT( !p.scanAndOrderRequired() );
|
||||
ASSERT( !p.startKey().woCompare( start ) );
|
||||
ASSERT( !p.endKey().woCompare( end ) );
|
||||
QueryPlan p2( FieldBoundSet( BSON( "a" << 3 ) ), BSONObj(), BSON( "a" << -1 << "b" << 1 ) );
|
||||
ASSERT( !p2.scanAndOrderRequired() );
|
||||
ASSERT( !p.startKey().woCompare( start ) );
|
||||
ASSERT( !p.endKey().woCompare( end ) );
|
||||
}
|
||||
};
|
||||
|
||||
@ -379,6 +417,40 @@ namespace QueryOptimizerTests {
|
||||
}
|
||||
};
|
||||
|
||||
class HintSpec : public Base {
|
||||
public:
|
||||
void run() {
|
||||
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), "a_1" );
|
||||
Helpers::ensureIndex( ns(), BSON( "b" << 1 ), "b_1" );
|
||||
BSONObj b = BSON( "hint" << BSON( "a" << 1 ) );
|
||||
BSONElement e = b.firstElement();
|
||||
QueryPlanSet s( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ), &e );
|
||||
ASSERT_EQUALS( 1, s.nPlans() );
|
||||
}
|
||||
};
|
||||
|
||||
class HintName : public Base {
|
||||
public:
|
||||
void run() {
|
||||
Helpers::ensureIndex( ns(), BSON( "a" << 1 ), "a_1" );
|
||||
Helpers::ensureIndex( ns(), BSON( "b" << 1 ), "b_1" );
|
||||
BSONObj b = BSON( "hint" << "a_1" );
|
||||
BSONElement e = b.firstElement();
|
||||
QueryPlanSet s( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ), &e );
|
||||
ASSERT_EQUALS( 1, s.nPlans() );
|
||||
}
|
||||
};
|
||||
|
||||
class BadHint : public Base {
|
||||
public:
|
||||
void run() {
|
||||
BSONObj b = BSON( "hint" << "a_1" );
|
||||
BSONElement e = b.firstElement();
|
||||
ASSERT_EXCEPTION( QueryPlanSet s( ns(), BSON( "a" << 1 ), BSON( "b" << 1 ), &e ),
|
||||
AssertionException );
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace QueryPlanSetTests
|
||||
|
||||
class All : public UnitTest::Suite {
|
||||
@ -412,6 +484,9 @@ namespace QueryOptimizerTests {
|
||||
add< QueryPlanSetTests::Optimal >();
|
||||
add< QueryPlanSetTests::NoOptimal >();
|
||||
add< QueryPlanSetTests::NoSpec >();
|
||||
add< QueryPlanSetTests::HintSpec >();
|
||||
add< QueryPlanSetTests::HintName >();
|
||||
add< QueryPlanSetTests::BadHint >();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -27,8 +27,11 @@ namespace mongo {
|
||||
// static string toString( void * );
|
||||
class LazyString {
|
||||
public:
|
||||
// LazyString is designed to be used in situations where the lifespan of
|
||||
// a temporary object used to construct a LazyString completely includes
|
||||
// the lifespan of the LazyString object itself.
|
||||
template< class T >
|
||||
LazyString( T &t ) : obj_( &t ), fun_( &T::toString ) {}
|
||||
LazyString( const T &t ) : obj_( (void*)&t ), fun_( &T::toString ) {}
|
||||
string val() const { return (*fun_)(obj_); }
|
||||
private:
|
||||
void *obj_;
|
||||
|
Loading…
Reference in New Issue
Block a user