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:
parent
af59c4ea9e
commit
a229845ceb
@ -821,7 +821,15 @@ namespace mongo {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} cmdFileMD5;
|
} 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 {
|
class CmdMedianKey : public Command {
|
||||||
public:
|
public:
|
||||||
CmdMedianKey() : Command( "medianKey" ) {}
|
CmdMedianKey() : Command( "medianKey" ) {}
|
||||||
@ -836,7 +844,7 @@ namespace mongo {
|
|||||||
BSONObj max = jsobj.getObjectField( "max" );
|
BSONObj max = jsobj.getObjectField( "max" );
|
||||||
BSONObj keyPattern = jsobj.getObjectField( "keyPattern" );
|
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 )
|
if ( id == 0 )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -883,7 +891,7 @@ namespace mongo {
|
|||||||
errmsg = "only one of min or max specified";
|
errmsg = "only one of min or max specified";
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
const IndexDetails *id = indexDetailsForRange( ns, errmsg, min, max, keyPattern );
|
const IndexDetails *id = cmdIndexDetailsForRange( ns, errmsg, min, max, keyPattern );
|
||||||
if ( id == 0 )
|
if ( id == 0 )
|
||||||
return false;
|
return false;
|
||||||
c.reset( new BtreeCursor( *id, min, max, 1 ) );
|
c.reset( new BtreeCursor( *id, min, max, 1 ) );
|
||||||
|
@ -199,9 +199,6 @@ namespace mongo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void QueryPlanSet::init() {
|
void QueryPlanSet::init() {
|
||||||
// TEMP
|
|
||||||
uassert( "min and max must be specified together", ( min_.isEmpty() + max_.isEmpty() ) % 2 == 0 );
|
|
||||||
|
|
||||||
plans_.clear();
|
plans_.clear();
|
||||||
mayRecordPlan_ = true;
|
mayRecordPlan_ = true;
|
||||||
usingPrerecordedPlan_ = false;
|
usingPrerecordedPlan_ = false;
|
||||||
@ -441,11 +438,67 @@ namespace mongo {
|
|||||||
}
|
}
|
||||||
return false;
|
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.
|
// 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 ) {
|
const IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSONObj &min, BSONObj &max, BSONObj &keyPattern ) {
|
||||||
if ( ns[ 0 ] == '\0' || min.isEmpty() || max.isEmpty() ) {
|
if ( min.isEmpty() && max.isEmpty() ) {
|
||||||
errmsg = "invalid command syntax (note: min and max are required)";
|
errmsg = "one of min or max must be specified";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,32 +510,15 @@ namespace mongo {
|
|||||||
return 0;
|
return 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;
|
||||||
|
}
|
||||||
if ( keyPattern.isEmpty() ) {
|
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 ) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < d->nIndexes; i++ ) {
|
for (int i = 0; i < d->nIndexes; i++ ) {
|
||||||
IndexDetails& ii = d->indexes[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 = ⅈ
|
id = ⅈ
|
||||||
keyPattern = ii.keyPattern();
|
keyPattern = ii.keyPattern();
|
||||||
break;
|
break;
|
||||||
@ -490,6 +526,10 @@ namespace mongo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} 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++ ) {
|
for (int i = 0; i < d->nIndexes; i++ ) {
|
||||||
IndexDetails& ii = d->indexes[i];
|
IndexDetails& ii = d->indexes[i];
|
||||||
if( ii.keyPattern().woCompare(keyPattern) == 0 ) {
|
if( ii.keyPattern().woCompare(keyPattern) == 0 ) {
|
||||||
@ -498,7 +538,13 @@ namespace mongo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( min.isEmpty() ) {
|
||||||
|
min = extremeKeyForIndex( keyPattern, -1 );
|
||||||
|
} else if ( max.isEmpty() ) {
|
||||||
|
max = extremeKeyForIndex( keyPattern, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
if ( !id ) {
|
if ( !id ) {
|
||||||
errmsg = "no index found for specified keyPattern";
|
errmsg = "no index found for specified keyPattern";
|
||||||
return 0;
|
return 0;
|
||||||
@ -506,7 +552,7 @@ namespace mongo {
|
|||||||
|
|
||||||
min = min.extractFieldsUnDotted( keyPattern );
|
min = min.extractFieldsUnDotted( keyPattern );
|
||||||
max = max.extractFieldsUnDotted( keyPattern );
|
max = max.extractFieldsUnDotted( keyPattern );
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,11 +575,6 @@ namespace QueryTests {
|
|||||||
ASSERT_EQUALS( 1, obj.getIntField( "b" ) );
|
ASSERT_EQUALS( 1, obj.getIntField( "b" ) );
|
||||||
ASSERT( !c->more() );
|
ASSERT( !c->more() );
|
||||||
}
|
}
|
||||||
|
|
||||||
// TEMP
|
|
||||||
ASSERT( !error() );
|
|
||||||
client().findOne( ns, Query().min( BSON( "a" << 1 << "b" << 1 ) ) );
|
|
||||||
ASSERT( error() );
|
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
auto_ptr< DBClientCursor > query( int minA, int minB, int maxA, int maxB, const BSONObj &hint ) {
|
auto_ptr< DBClientCursor > query( int minA, int minB, int maxA, int maxB, const BSONObj &hint ) {
|
||||||
|
@ -250,6 +250,7 @@
|
|||||||
93B4A8290F1C024C000C862C /* cursor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cursor.cpp; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
93D0C1FB0EF1E267005253B7 /* namespacetests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = namespacetests.cpp; sourceTree = "<group>"; };
|
||||||
@ -547,6 +548,7 @@
|
|||||||
93A8D1D10F37544800C92B85 /* jstests */ = {
|
93A8D1D10F37544800C92B85 /* jstests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
93C392D00FAA4162007D6E4A /* minmax.js */,
|
||||||
9343373F0F9CD6900019D5C0 /* index8.js */,
|
9343373F0F9CD6900019D5C0 /* index8.js */,
|
||||||
93AE6FB10F9631A200857F1C /* disk */,
|
93AE6FB10F9631A200857F1C /* disk */,
|
||||||
93DCDB5B0F93ED98005349BC /* nin.js */,
|
93DCDB5B0F93ED98005349BC /* nin.js */,
|
||||||
|
@ -140,6 +140,17 @@ DBQuery.prototype.hint = function( hint ){
|
|||||||
return this;
|
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 ){
|
DBQuery.prototype.forEach = function( func ){
|
||||||
while ( this.hasNext() )
|
while ( this.hasNext() )
|
||||||
|
Loading…
Reference in New Issue
Block a user