0
0
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:
Eliot Horowitz 2009-02-19 17:32:31 -05:00
commit b911ce8b25
6 changed files with 154 additions and 27 deletions

View File

@ -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() )

View File

@ -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;

View File

@ -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() );

View File

@ -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_;

View File

@ -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 >();
}
};

View File

@ -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_;