Permalink
Browse files

SERVER-2189

  • Loading branch information...
1 parent bbfcf22 commit 80bfc4b182a430d380c48660b77f84f08c38ced6 @astaple astaple committed Dec 8, 2010
Showing with 70 additions and 26 deletions.
  1. +2 −1 db/matcher.h
  2. +1 −1 db/queryoptimizer.cpp
  3. +1 −0 db/queryoptimizer.h
  4. +39 −1 db/queryutil.cpp
  5. +19 −23 db/queryutil.h
  6. +8 −0 jstests/or6.js
View
@@ -227,8 +227,9 @@ namespace mongo {
// once this is called, shouldn't use this matcher for matching any more
void advanceOrClause( const shared_ptr< FieldRangeVector > &frv ) {
_docMatcher->addOrConstraint( frv );
- // TODO this is not an optimal optimization, since we could skip an entire
+ // TODO this is not yet optimal. Since we could skip an entire
// or clause (if a match is impossible) between calls to advanceOrClause()
+ // we may not pop all the clauses we can.
_docMatcher->popOrClause();
}
View
@@ -714,7 +714,7 @@ namespace mongo {
if ( ret->qp().willScanTable() ) {
_tableScanned = true;
}
- _fros.popOrClause();
+ _fros.popOrClause( ret->qp().indexed() ? ret->qp().indexKey() : BSONObj() );
return ret;
}
View
@@ -57,6 +57,7 @@ namespace mongo {
shared_ptr<Cursor> newCursor( const DiskLoc &startLoc = DiskLoc() , int numWanted=0 ) const;
shared_ptr<Cursor> newReverseCursor() const;
BSONObj indexKey() const;
+ bool indexed() const { return _index; }
bool willScanTable() const { return !_index && _fbs.matchPossible(); }
const char *ns() const { return _fbs.ns(); }
NamespaceDetails *nsd() const { return _d; }
View
@@ -667,6 +667,33 @@ namespace mongo {
}
}
}
+
+ void FieldRangeOrSet::popOrClause( const BSONObj &indexSpec ) {
+ massert( 13274, "no or clause to pop", !orFinished() );
+ auto_ptr< FieldRangeSet > holder;
+ FieldRangeSet *toDiff = &_originalOrSets.front();
+ if ( toDiff->matchPossible() && !indexSpec.isEmpty() ) {
+ holder.reset( toDiff->subset( indexSpec ) );
+ toDiff = holder.get();
+ }
+ list< FieldRangeSet >::iterator i = _orSets.begin();
+ list< FieldRangeSet >::iterator j = _originalOrSets.begin();
+ ++i;
+ ++j;
+ while( i != _orSets.end() ) {
+ *i -= *toDiff;
+ if( !i->matchPossible() ) {
+ i = _orSets.erase( i );
+ j = _originalOrSets.erase( j );
+ } else {
+ ++i;
+ ++j;
+ }
+ }
+ _oldOrSets.push_front( _orSets.front() );
+ _orSets.pop_front();
+ _originalOrSets.pop_front();
+ }
FieldRange *FieldRangeSet::trivialRange_ = 0;
FieldRange &FieldRangeSet::trivialRange() {
@@ -790,7 +817,18 @@ namespace mongo {
return ret;
}
-
+ FieldRangeSet *FieldRangeSet::subset( const BSONObj &fields ) const {
+ FieldRangeSet *ret = new FieldRangeSet( _ns, BSONObj() );
+ BSONObjIterator i( fields );
+ while( i.more() ) {
+ BSONElement e = i.next();
+ if ( _ranges[ e.fieldName() ].nontrivial() ) {
+ ret->_ranges[ e.fieldName() ] = _ranges[ e.fieldName() ];
+ }
+ }
+ ret->_queries = _queries;
+ return ret;
+ }
bool FieldRangeVector::matchesElement( const BSONElement &e, int i, bool forward ) const {
bool eq;
View
@@ -312,6 +312,14 @@ namespace mongo {
}
// TODO get rid of this
BoundList indexBounds( const BSONObj &keyPattern, int direction ) const;
+
+ /**
+ * @return - A new FieldRangeSet based on this FieldRangeSet, but with only
+ * a subset of the fields.
+ * @fields - Only fields which are represented as field names in this object
+ * will be included in the returned FieldRangeSet.
+ */
+ FieldRangeSet *subset( const BSONObj &fields ) const;
private:
void appendQueries( const FieldRangeSet &other ) {
for( vector< BSONObj >::const_iterator i = other._queries.begin(); i != other._queries.end(); ++i ) {
@@ -482,29 +490,17 @@ namespace mongo {
FieldRangeOrSet( const char *ns, const BSONObj &query , bool optimize=true );
// if there's a useless or clause, we won't use or ranges to help with scanning
bool orFinished() const { return _orFound && _orSets.empty(); }
- // removes first or clause, and removes the field ranges it covers from all subsequent or clauses
- // this could invalidate the result of the last topFrs()
- void popOrClause() {
- massert( 13274, "no or clause to pop", !orFinished() );
- const FieldRangeSet &toDiff = _originalOrSets.front();
- list< FieldRangeSet >::iterator i = _orSets.begin();
- list< FieldRangeSet >::iterator j = _originalOrSets.begin();
- ++i;
- ++j;
- while( i != _orSets.end() ) {
- *i -= toDiff;
- if( !i->matchPossible() ) {
- i = _orSets.erase( i );
- j = _originalOrSets.erase( j );
- } else {
- ++i;
- ++j;
- }
- }
- _oldOrSets.push_front( _orSets.front() );
- _orSets.pop_front();
- _originalOrSets.pop_front();
- }
+ /**
+ * Removes the top or clause, which would have been recently scanned, and
+ * removes the field ranges it covers from all subsequent or clauses. As a
+ * side effect, this function may invalidate the return values of topFrs()
+ * calls made before this function was called.
+ * @indexSpec - Keys of the index that was used to satisfy the last or
+ * clause. Used to determine the range of keys that were scanned. If
+ * empty we do not constrain the previous clause's ranges using index keys,
+ * which may reduce opportunities for range elimination.
+ */
+ void popOrClause( const BSONObj &indexSpec = BSONObj() );
FieldRangeSet *topFrs() const {
FieldRangeSet *ret = new FieldRangeSet( _baseSet );
if (_orSets.size()){
View
@@ -19,6 +19,8 @@ assert.eq.automsg( "null", "t.find( {$or:[{a:1},{b:2}]} ).hint( {a:1} ).explain(
assert.eq.automsg( "2", "t.find( {$or:[{a:1},{a:3}]} ).hint( {a:1} ).explain().clauses.length" );
assert.eq.automsg( "'BasicCursor'", "t.find( {$or:[{a:1},{a:3}]} ).hint( {$natural:1} ).explain().cursor" );
+assert.eq( null, t.find( {$or:[{a:{$gt:1,$lt:5},b:6}, {a:3,b:{$gt:0,$lt:10}}]} ).explain().clauses );
+
t.ensureIndex( {b:1} );
assert.eq.automsg( "2", "t.find( {$or:[{a:1,b:5},{a:3,b:5}]} ).hint( {a:1} ).explain().clauses.length" );
@@ -29,3 +31,9 @@ assert.eq.automsg( "2", "t.find( {$or:[{a:{$in:[1,2]},b:5}, {a:2,b:6}]} ).explai
assert.eq.automsg( "2", "t.find( {$or:[{a:{$gt:1,$lte:2},b:5}, {a:2,b:6}]} ).explain().clauses.length" );
assert.eq.automsg( "2", "t.find( {$or:[{a:{$gt:1,$lte:3},b:5}, {a:2,b:6}]} ).explain().clauses.length" );
assert.eq.automsg( "null", "t.find( {$or:[{a:{$in:[1,2]}}, {a:2}]} ).explain().clauses" );
+
+assert.eq( null, t.find( {$or:[{a:{$gt:1,$lt:5},b:{$gt:0,$lt:3},c:6}, {a:3,b:{$gt:1,$lt:2},c:{$gt:0,$lt:10}}]} ).explain().clauses );
+assert.eq( null, t.find( {$or:[{a:{$gt:1,$lt:5},c:6}, {a:3,b:{$gt:1,$lt:2},c:{$gt:0,$lt:10}}]} ).explain().clauses );
+exp = t.find( {$or:[{a:{$gt:1,$lt:5},b:{$gt:0,$lt:3},c:6}, {a:3,b:{$gt:1,$lt:4},c:{$gt:0,$lt:10}}]} ).explain();
+assert.eq( 3, exp.clauses[ 1 ].indexBounds.b[ 0 ][ 0 ] );
+assert.eq( 4, exp.clauses[ 1 ].indexBounds.b[ 0 ][ 1 ] );

0 comments on commit 80bfc4b

Please sign in to comment.