Permalink
Browse files

SERVER-109 make simplifiedQuery match actual btree cursor bounds when…

… necessary
  • Loading branch information...
1 parent 8b0b628 commit a8fd0b6ac537541f516c5d80bab6ce7fcc7f0083 @astaple astaple committed Jun 10, 2010
Showing with 86 additions and 24 deletions.
  1. +55 −5 db/queryutil.cpp
  2. +4 −14 db/queryutil.h
  3. +1 −1 dbtests/queryoptimizertests.cpp
  4. +26 −4 jstests/or7.js
View
@@ -495,6 +495,57 @@ namespace mongo {
return o;
}
+ BSONObj FieldRange::simplifiedComplex() const {
+ BSONObjBuilder bb;
+ BSONArrayBuilder a;
+ set< string > regexLow;
+ set< string > regexHigh;
+ for( vector< FieldInterval >::const_iterator i = _intervals.begin(); i != _intervals.end(); ++i ) {
+ // this recovers exact $in fields and regexes - should be everything for equality
+ if ( i->equality() ) {
+ a << i->_upper._bound;
+ // right now btree cursor doesn't do exclusive bounds so we need to match end of the regex range
+ if ( i->_upper._bound.type() == RegEx ) {
+ string r = simpleRegex( i->_upper._bound );
+ regexLow.insert( r );
+ string re = simpleRegexEnd( r );
+ regexHigh.insert( re );
+ a << re;
+ }
+ }
+ }
+ BSONArray in = a.arr();
+ if ( !in.isEmpty() ) {
+ bb << "$in" << in;
+ }
+ BSONObj low;
+ BSONObj high;
+ // should only be one non regex ineq range
+ for( vector< FieldInterval >::const_iterator i = _intervals.begin(); i != _intervals.end(); ++i ) {
+ if ( !i->equality() ) {
+ if ( !i->_lower._inclusive || i->_lower._bound.type() != String || !regexLow.count( i->_lower._bound.valuestr() ) ) {
+ BSONObjBuilder b;
+ // in btree impl lower bound always inclusive
+ b.appendAs( i->_lower._bound, "$gte" );
+ low = b.obj();
+ }
+ if ( i->_upper._inclusive || i->_upper._bound.type() != String || !regexHigh.count( i->_upper._bound.valuestr() ) ) {
+ BSONObjBuilder b;
+ // in btree impl upper bound always
+ b.appendAs( i->_upper._bound, "$lte" );
+ high = b.obj();
+ }
+ }
+ }
+ if ( !low.isEmpty() ) {
+ bb.appendElements( low );
+ }
+ if ( !high.isEmpty() ) {
+ bb.appendElements( high );
+ }
+ return bb.obj();
+ }
+
string FieldRangeSet::getSpecial() const {
string s = "";
for ( map<string,FieldRange>::iterator i=_ranges.begin(); i!=_ranges.end(); i++ ){
@@ -655,18 +706,17 @@ namespace mongo {
b.appendAs( range.min(), name );
else if ( range.nontrivial() ) {
BSONObj o;
- if ( range.intervals().size() > 1 ) {
- o = range.simplifiedIn();
- }
- if ( o.isEmpty() ) {
+ if ( expandIn ) {
+ o = range.simplifiedComplex();
+ } else {
BSONObjBuilder c;
if ( range.min().type() != MinKey )
c.appendAs( range.min(), range.minInclusive() ? "$gte" : "$gt" );
if ( range.max().type() != MaxKey )
c.appendAs( range.max(), range.maxInclusive() ? "$lte" : "$lt" );
o = c.obj();
}
- b.append( name, o );
+ b.append( name, o );
}
}
return b.obj();
View
@@ -43,6 +43,7 @@ namespace mongo {
int cmp = _lower._bound.woCompare( _upper._bound, false );
return ( cmp < 0 || ( cmp == 0 && _lower._inclusive && _upper._inclusive ) );
}
+ bool equality() const { return _lower._inclusive && _upper._inclusive && _lower._bound.woCompare( _upper._bound, false ) == 0; }
};
// range of a field's value that may be determined from query -- used to
@@ -81,20 +82,9 @@ namespace mongo {
i->_upper._inclusive = false;
}
}
- // if cannot construct an $in query, BSONObj() is returned
- BSONObj simplifiedIn() const {
- BSONObjBuilder b;
- BSONArrayBuilder a( b.subarrayStart("$in") );
- for( vector< FieldInterval >::const_iterator i = _intervals.begin(); i != _intervals.end(); ++i ) {
- if ( !i->_upper._inclusive || !i->_lower._inclusive ||
- i->_upper._bound.woCompare( i->_lower._bound, false ) != 0 ) {
- return BSONObj();
- }
- a << i->_upper._bound;
- }
- a.done();
- return b.obj();
- }
+ // reconstructs $in, regex, inequality matches
+ // this is a hack - we should submit FieldRange directly to a Matcher instead
+ BSONObj simplifiedComplex() const;
private:
BSONObj addObj( const BSONObj &o );
void finishOperation( const vector< FieldInterval > &newIntervals, const FieldRange &other );
@@ -705,7 +705,7 @@ namespace QueryOptimizerTests {
FieldRangeSet frs1( "", fromjson( "{b:{$in:[5,6]},c:7,d:{$in:[8,9]}}" ) );
FieldRangeSet frs2( "", fromjson( "{a:1,b:5,c:{$in:[7,8]},d:{$in:[8,9]},e:10}" ) );
frs1 &= frs2;
- ASSERT_EQUALS( fromjson( "{a:1,b:5,c:7,d:{$in:[8,9]},e:10}" ), frs1.simplifiedQuery() );
+ ASSERT_EQUALS( fromjson( "{a:1,b:5,c:7,d:{$in:[8,9]},e:10}" ), frs1.simplifiedQuery( BSONObj(), true ) );
}
};
View
@@ -6,14 +6,36 @@ t.save( {a:2} );
assert.eq.automsg( "1", "t.count( {$or:[{a:{$in:[1,3]}},{a:2}]} )" );
+//SERVER-1201 ...
+
+t.remove();
+
+t.save( {a:"aa"} );
+t.save( {a:"ab"} );
+t.save( {a:"ad"} );
+
+assert.eq.automsg( "3", "t.count( {$or:[{a:/^ab/},{a:/^a/}]} )" );
+
+t.remove();
+
+t.save( {a:"aa"} );
+t.save( {a:"ad"} );
+
+assert.eq.automsg( "2", "t.count( {$or:[{a:/^ab/},{a:/^a/}]} )" );
+
t.remove();
t.save( {a:"aa"} );
t.save( {a:"ac"} );
-//SERVER-1201
-if ( 0 ) {
-printjson( t.find( {$or:[{a:/^ab/},{a:/^a/}]} ).explain() );
assert.eq.automsg( "2", "t.count( {$or:[{a:/^ab/},{a:/^a/}]} )" );
-}
+assert.eq.automsg( "2", "t.count( {$or:[{a:/^ab/},{a:/^a/}]} )" );
+
+t.save( {a:"ab"} );
+assert.eq.automsg( "3", "t.count( {$or:[{a:{$in:[/^ab/],$gte:'abc'}},{a:/^a/}]} )" );
+
+t.remove();
+t.save( {a:"a"} );
+t.save( {a:"b"} );
+assert.eq.automsg( "2", "t.count( {$or:[{a:{$gt:'a',$lt:'b'}},{a:{$gte:'a',$lte:'b'}}]} )" );

0 comments on commit a8fd0b6

Please sign in to comment.