mirror of
https://github.com/mongodb/mongo.git
synced 2024-11-30 00:56:44 +01:00
183 lines
6.4 KiB
C++
183 lines
6.4 KiB
C++
// queryutil.cpp
|
|
|
|
/**
|
|
* Copyright (C) 2008 10gen Inc.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License, version 3,
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "btree.h"
|
|
#include "pdfile.h"
|
|
#include "queryoptimizer.h"
|
|
|
|
namespace mongo {
|
|
|
|
FieldBound::FieldBound( const BSONElement &e ) :
|
|
lower_( minKey.firstElement() ),
|
|
lowerInclusive_( true ),
|
|
upper_( maxKey.firstElement() ),
|
|
upperInclusive_( true ) {
|
|
if ( e.eoo() )
|
|
return;
|
|
if ( e.type() == RegEx ) {
|
|
const char *r = e.simpleRegex();
|
|
if ( r ) {
|
|
lower_ = addObj( BSON( "" << r ) ).firstElement();
|
|
upper_ = addObj( BSON( "" << simpleRegexEnd( r ) ) ).firstElement();
|
|
upperInclusive_ = false;
|
|
}
|
|
return;
|
|
}
|
|
switch( e.getGtLtOp() ) {
|
|
case JSMatcher::Equality:
|
|
lower_ = e;
|
|
upper_ = e;
|
|
break;
|
|
case JSMatcher::LT:
|
|
upperInclusive_ = false;
|
|
case JSMatcher::LTE:
|
|
upper_ = e;
|
|
break;
|
|
case JSMatcher::GT:
|
|
lowerInclusive_ = false;
|
|
case JSMatcher::GTE:
|
|
lower_ = e;
|
|
break;
|
|
case JSMatcher::opIN: {
|
|
massert( "$in requires array", e.type() == Array );
|
|
BSONElement max = minKey.firstElement();
|
|
BSONElement min = maxKey.firstElement();
|
|
BSONObjIterator i( e.embeddedObject() );
|
|
while( i.more() ) {
|
|
BSONElement f = i.next();
|
|
if ( f.eoo() )
|
|
break;
|
|
if ( max.woCompare( f, false ) < 0 )
|
|
max = f;
|
|
if ( min.woCompare( f, false ) > 0 )
|
|
min = f;
|
|
}
|
|
lower_ = min;
|
|
upper_ = max;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
const FieldBound &FieldBound::operator&=( const FieldBound &other ) {
|
|
int cmp;
|
|
cmp = other.upper_.woCompare( upper_, false );
|
|
if ( cmp == 0 )
|
|
if ( !other.upperInclusive_ )
|
|
upperInclusive_ = false;
|
|
if ( cmp < 0 ) {
|
|
upper_ = other.upper_;
|
|
upperInclusive_ = other.upperInclusive_;
|
|
}
|
|
cmp = other.lower_.woCompare( lower_, false );
|
|
if ( cmp == 0 )
|
|
if ( !other.lowerInclusive_ )
|
|
lowerInclusive_ = false;
|
|
if ( cmp > 0 ) {
|
|
lower_ = other.lower_;
|
|
lowerInclusive_ = other.lowerInclusive_;
|
|
}
|
|
for( vector< BSONObj >::const_iterator i = other.objData_.begin(); i != other.objData_.end(); ++i )
|
|
objData_.push_back( *i );
|
|
return *this;
|
|
}
|
|
|
|
string FieldBound::simpleRegexEnd( string regex ) {
|
|
++regex[ regex.length() - 1 ];
|
|
return regex;
|
|
}
|
|
|
|
BSONObj FieldBound::addObj( const BSONObj &o ) {
|
|
objData_.push_back( o );
|
|
return o;
|
|
}
|
|
|
|
FieldBoundSet::FieldBoundSet( const char *ns, const BSONObj &query ) :
|
|
ns_( ns ),
|
|
query_( query.getOwned() ) {
|
|
BSONObjIterator i( query_ );
|
|
while( i.more() ) {
|
|
BSONElement e = i.next();
|
|
if ( e.eoo() )
|
|
break;
|
|
if ( strcmp( e.fieldName(), "$where" ) == 0 )
|
|
continue;
|
|
if ( getGtLtOp( e ) == JSMatcher::Equality ) {
|
|
bounds_[ e.fieldName() ] &= FieldBound( e );
|
|
}
|
|
else {
|
|
BSONObjIterator i( e.embeddedObject() );
|
|
while( i.more() ) {
|
|
BSONElement f = i.next();
|
|
if ( f.eoo() )
|
|
break;
|
|
bounds_[ e.fieldName() ] &= FieldBound( f );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FieldBound *FieldBoundSet::trivialBound_ = 0;
|
|
FieldBound &FieldBoundSet::trivialBound() {
|
|
if ( trivialBound_ == 0 )
|
|
trivialBound_ = new FieldBound();
|
|
return *trivialBound_;
|
|
}
|
|
|
|
BSONObj FieldBoundSet::simplifiedQuery() const {
|
|
BSONObjBuilder b;
|
|
for( map< string, FieldBound >::const_iterator i = bounds_.begin(); i != bounds_.end(); ++i ) {
|
|
if ( i->second.equality() )
|
|
b.appendAs( i->second.lower(), i->first.c_str() );
|
|
else if ( i->second.nontrivial() ) {
|
|
BSONObjBuilder c;
|
|
if ( i->second.lower().type() != MinKey )
|
|
c.appendAs( i->second.lower(), i->second.lowerInclusive() ? "$gte" : "$gt" );
|
|
if ( i->second.upper().type() != MaxKey )
|
|
c.appendAs( i->second.upper(), i->second.upperInclusive() ? "$lte" : "$lt" );
|
|
b.append( i->first.c_str(), c.done() );
|
|
}
|
|
}
|
|
return b.obj();
|
|
}
|
|
|
|
QueryPattern FieldBoundSet::pattern( const BSONObj &sort ) const {
|
|
QueryPattern qp;
|
|
for( map< string, FieldBound >::const_iterator i = bounds_.begin(); i != bounds_.end(); ++i ) {
|
|
if ( i->second.equality() ) {
|
|
qp.fieldTypes_[ i->first ] = QueryPattern::Equality;
|
|
} else if ( i->second.nontrivial() ) {
|
|
bool upper = i->second.upper().type() != MaxKey;
|
|
bool lower = i->second.lower().type() != MinKey;
|
|
if ( upper && lower )
|
|
qp.fieldTypes_[ i->first ] = QueryPattern::UpperAndLowerBound;
|
|
else if ( upper )
|
|
qp.fieldTypes_[ i->first ] = QueryPattern::UpperBound;
|
|
else if ( lower )
|
|
qp.fieldTypes_[ i->first ] = QueryPattern::LowerBound;
|
|
}
|
|
}
|
|
qp.setSort( sort );
|
|
return qp;
|
|
}
|
|
|
|
} // namespace mongo
|