0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00

more flexibility and sanity checks for min/max query specs

This commit is contained in:
Aaron 2009-04-30 17:36:25 -04:00
parent af59c4ea9e
commit a229845ceb
5 changed files with 100 additions and 38 deletions

View File

@ -822,6 +822,14 @@ namespace mongo {
}
} cmdFileMD5;
const IndexDetails *cmdIndexDetailsForRange( const char *ns, string &errmsg, BSONObj &min, BSONObj &max, BSONObj &keyPattern ) {
if ( ns[ 0 ] == '\0' || min.isEmpty() || max.isEmpty() ) {
errmsg = "invalid command syntax (note: min and max are required)";
return 0;
}
return indexDetailsForRange( ns, errmsg, min, max, keyPattern );
}
class CmdMedianKey : public Command {
public:
CmdMedianKey() : Command( "medianKey" ) {}
@ -836,7 +844,7 @@ namespace mongo {
BSONObj max = jsobj.getObjectField( "max" );
BSONObj keyPattern = jsobj.getObjectField( "keyPattern" );
const IndexDetails *id = indexDetailsForRange( ns, errmsg, min, max, keyPattern );
const IndexDetails *id = cmdIndexDetailsForRange( ns, errmsg, min, max, keyPattern );
if ( id == 0 )
return false;
@ -883,7 +891,7 @@ namespace mongo {
errmsg = "only one of min or max specified";
return false;
} else {
const IndexDetails *id = indexDetailsForRange( ns, errmsg, min, max, keyPattern );
const IndexDetails *id = cmdIndexDetailsForRange( ns, errmsg, min, max, keyPattern );
if ( id == 0 )
return false;
c.reset( new BtreeCursor( *id, min, max, 1 ) );

View File

@ -199,9 +199,6 @@ namespace mongo {
}
void QueryPlanSet::init() {
// TEMP
uassert( "min and max must be specified together", ( min_.isEmpty() + max_.isEmpty() ) % 2 == 0 );
plans_.clear();
mayRecordPlan_ = true;
usingPrerecordedPlan_ = false;
@ -442,10 +439,66 @@ namespace mongo {
return false;
}
BSONObj extremeKeyForIndex( const BSONObj &idxPattern, int baseDirection ) {
BSONObjIterator i( idxPattern );
BSONObjBuilder b;
while( i.more() ) {
BSONElement e = i.next();
if ( e.eoo() )
break;
int idxDirection = e.number() >= 0 ? 1 : -1;
int direction = idxDirection * baseDirection;
switch( direction ) {
case 1:
b.appendMaxKey( e.fieldName() );
break;
case -1:
b.appendMinKey( e.fieldName() );
break;
default:
assert( false );
}
}
return b.obj();
}
pair< int, int > keyAudit( const BSONObj &min, const BSONObj &max ) {
int direction = 0;
int firstSignificantField = 0;
BSONObjIterator i( min );
BSONObjIterator a( max );
while( 1 ) {
BSONElement ie = i.next();
BSONElement ae = a.next();
if ( ie.eoo() && ae.eoo() )
break;
if ( ie.eoo() || ae.eoo() || strcmp( ie.fieldName(), ae.fieldName() ) != 0 ) {
return make_pair( -1, -1 );
}
int cmp = ie.woCompare( ae );
if ( cmp < 0 )
direction = 1;
if ( cmp > 0 )
direction = -1;
if ( direction != 0 )
break;
++firstSignificantField;
}
return make_pair( direction, firstSignificantField );
}
pair< int, int > flexibleKeyAudit( const BSONObj &min, const BSONObj &max ) {
if ( min.isEmpty() || max.isEmpty() ) {
return make_pair( 1, -1 );
} else {
return keyAudit( min, max );
}
}
// NOTE min, max, and keyPattern will be updated to be consistent with the selected index.
const IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSONObj &min, BSONObj &max, BSONObj &keyPattern ) {
if ( ns[ 0 ] == '\0' || min.isEmpty() || max.isEmpty() ) {
errmsg = "invalid command syntax (note: min and max are required)";
if ( min.isEmpty() && max.isEmpty() ) {
errmsg = "one of min or max must be specified";
return 0;
}
@ -457,32 +510,15 @@ namespace mongo {
return 0;
}
if ( keyPattern.isEmpty() ) {
BSONObjIterator i( min );
BSONObjIterator a( max );
int direction = 0;
int firstSignificantField = 0;
while( 1 ) {
BSONElement ie = i.next();
BSONElement ae = a.next();
if ( ie.eoo() && ae.eoo() )
break;
if ( ie.eoo() || ae.eoo() || strcmp( ie.fieldName(), ae.fieldName() ) != 0 ) {
pair< int, int > ret = flexibleKeyAudit( min, max );
if ( ret == make_pair( -1, -1 ) ) {
errmsg = "min and max keys do not share pattern";
return 0;
}
int cmp = ie.woCompare( ae );
if ( cmp < 0 )
direction = 1;
if ( cmp > 0 )
direction = -1;
if ( direction != 0 )
break;
++firstSignificantField;
}
if ( keyPattern.isEmpty() ) {
for (int i = 0; i < d->nIndexes; i++ ) {
IndexDetails& ii = d->indexes[i];
if ( indexWorks( ii.keyPattern(), min, direction, firstSignificantField ) ) {
if ( indexWorks( ii.keyPattern(), min.isEmpty() ? max : min, ret.first, ret.second ) ) {
id = &ii;
keyPattern = ii.keyPattern();
break;
@ -490,6 +526,10 @@ namespace mongo {
}
} else {
if ( !indexWorks( keyPattern, min.isEmpty() ? max : min, ret.first, ret.second ) ) {
errmsg = "requested keyPattern does not match specified keys";
return 0;
}
for (int i = 0; i < d->nIndexes; i++ ) {
IndexDetails& ii = d->indexes[i];
if( ii.keyPattern().woCompare(keyPattern) == 0 ) {
@ -499,6 +539,12 @@ namespace mongo {
}
}
if ( min.isEmpty() ) {
min = extremeKeyForIndex( keyPattern, -1 );
} else if ( max.isEmpty() ) {
max = extremeKeyForIndex( keyPattern, 1 );
}
if ( !id ) {
errmsg = "no index found for specified keyPattern";
return 0;

View File

@ -575,11 +575,6 @@ namespace QueryTests {
ASSERT_EQUALS( 1, obj.getIntField( "b" ) );
ASSERT( !c->more() );
}
// TEMP
ASSERT( !error() );
client().findOne( ns, Query().min( BSON( "a" << 1 << "b" << 1 ) ) );
ASSERT( error() );
}
private:
auto_ptr< DBClientCursor > query( int minA, int minB, int maxA, int maxB, const BSONObj &hint ) {

View File

@ -250,6 +250,7 @@
93B4A8290F1C024C000C862C /* cursor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cursor.cpp; sourceTree = "<group>"; };
93B4A82A0F1C0256000C862C /* pdfiletests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pdfiletests.cpp; sourceTree = "<group>"; };
93C38E940FA66622007D6E4A /* basictests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = basictests.cpp; sourceTree = "<group>"; };
93C392D00FAA4162007D6E4A /* minmax.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = minmax.js; sourceTree = "<group>"; };
93CCC87F0F8562E900E20FA0 /* datasize.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = datasize.js; sourceTree = "<group>"; };
93D0C1520EF1D377005253B7 /* jsobjtests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsobjtests.cpp; sourceTree = "<group>"; };
93D0C1FB0EF1E267005253B7 /* namespacetests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = namespacetests.cpp; sourceTree = "<group>"; };
@ -547,6 +548,7 @@
93A8D1D10F37544800C92B85 /* jstests */ = {
isa = PBXGroup;
children = (
93C392D00FAA4162007D6E4A /* minmax.js */,
9343373F0F9CD6900019D5C0 /* index8.js */,
93AE6FB10F9631A200857F1C /* disk */,
93DCDB5B0F93ED98005349BC /* nin.js */,

View File

@ -140,6 +140,17 @@ DBQuery.prototype.hint = function( hint ){
return this;
}
DBQuery.prototype.min = function( min ) {
this._ensureSpecial();
this._query["$min"] = min;
return this;
}
DBQuery.prototype.max = function( max ) {
this._ensureSpecial();
this._query["$max"] = max;
return this;
}
DBQuery.prototype.forEach = function( func ){
while ( this.hasNext() )