// expression_leaf.h
/**
* Copyright (C) 2013 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 .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsonmisc.h"
#include "mongo/db/matcher/expression.h"
namespace pcrecpp {
class RE;
} // namespace pcrecpp;
namespace mongo {
/**
* This file contains leaves in the parse tree that are not array-based.
*
* LeafMatchExpression: REGEX MOD EXISTS MATCH_IN
* ComparisonMatchExpression: EQ LTE LT GT GTE
* MatchExpression: TYPE_OPERATOR
*/
/**
* Many operators subclass from this:
* REGEX, MOD, EXISTS, IN
* Everything that inherits from ComparisonMatchExpression.
*/
class LeafMatchExpression : public MatchExpression {
public:
LeafMatchExpression( MatchType matchType )
: MatchExpression( matchType ) {
}
virtual ~LeafMatchExpression(){}
virtual bool matches( const MatchableDocument* doc, MatchDetails* details = 0 ) const;
virtual bool matchesSingleElement( const BSONElement& e ) const = 0;
virtual const StringData path() const { return _path; }
protected:
Status initPath( StringData path );
private:
StringData _path;
ElementPath _elementPath;
};
/**
* EQ, LTE, LT, GT, GTE subclass from ComparisonMatchExpression.
*/
class ComparisonMatchExpression : public LeafMatchExpression {
public:
ComparisonMatchExpression( MatchType type ) : LeafMatchExpression( type ){}
Status init( StringData path, const BSONElement& rhs );
virtual ~ComparisonMatchExpression(){}
virtual bool matchesSingleElement( const BSONElement& e ) const;
virtual const BSONElement& getRHS() const { return _rhs; }
virtual void debugString( StringBuilder& debug, int level = 0 ) const;
virtual void toBSON(BSONObjBuilder* out) const;
virtual bool equivalent( const MatchExpression* other ) const;
const BSONElement& getData() const { return _rhs; }
protected:
BSONElement _rhs;
};
//
// ComparisonMatchExpression inheritors
//
class EqualityMatchExpression : public ComparisonMatchExpression {
public:
EqualityMatchExpression() : ComparisonMatchExpression( EQ ){}
virtual LeafMatchExpression* shallowClone() const {
ComparisonMatchExpression* e = new EqualityMatchExpression();
e->init( path(), _rhs );
if ( getTag() ) {
e->setTag(getTag()->clone());
}
return e;
}
};
class LTEMatchExpression : public ComparisonMatchExpression {
public:
LTEMatchExpression() : ComparisonMatchExpression( LTE ){}
virtual LeafMatchExpression* shallowClone() const {
ComparisonMatchExpression* e = new LTEMatchExpression();
e->init( path(), _rhs );
if ( getTag() ) {
e->setTag(getTag()->clone());
}
return e;
}
};
class LTMatchExpression : public ComparisonMatchExpression {
public:
LTMatchExpression() : ComparisonMatchExpression( LT ){}
virtual LeafMatchExpression* shallowClone() const {
ComparisonMatchExpression* e = new LTMatchExpression();
e->init( path(), _rhs );
if ( getTag() ) {
e->setTag(getTag()->clone());
}
return e;
}
};
class GTMatchExpression : public ComparisonMatchExpression {
public:
GTMatchExpression() : ComparisonMatchExpression( GT ){}
virtual LeafMatchExpression* shallowClone() const {
ComparisonMatchExpression* e = new GTMatchExpression();
e->init( path(), _rhs );
if ( getTag() ) {
e->setTag(getTag()->clone());
}
return e;
}
};
class GTEMatchExpression : public ComparisonMatchExpression {
public:
GTEMatchExpression() : ComparisonMatchExpression( GTE ){}
virtual LeafMatchExpression* shallowClone() const {
ComparisonMatchExpression* e = new GTEMatchExpression();
e->init( path(), _rhs );
if ( getTag() ) {
e->setTag(getTag()->clone());
}
return e;
}
};
//
// LeafMatchExpression inheritors
//
class RegexMatchExpression : public LeafMatchExpression {
public:
/**
* Maximum pattern size which pcre v8.3 can do matches correctly with
* LINK_SIZE define macro set to 2 @ pcre's config.h (based on
* experiments)
*/
static const size_t MaxPatternSize = 32764;
RegexMatchExpression();
~RegexMatchExpression();
Status init( StringData path, StringData regex, StringData options );
Status init( StringData path, const BSONElement& e );
virtual LeafMatchExpression* shallowClone() const {
RegexMatchExpression* e = new RegexMatchExpression();
e->init( path(), _regex, _flags );
if ( getTag() ) {
e->setTag(getTag()->clone());
}
return e;
}
virtual bool matchesSingleElement( const BSONElement& e ) const;
virtual void debugString( StringBuilder& debug, int level ) const;
virtual void toBSON(BSONObjBuilder* out) const;
void shortDebugString( StringBuilder& debug ) const;
virtual bool equivalent( const MatchExpression* other ) const;
const std::string& getString() const { return _regex; }
const std::string& getFlags() const { return _flags; }
private:
std::string _regex;
std::string _flags;
std::unique_ptr _re;
};
class ModMatchExpression : public LeafMatchExpression {
public:
ModMatchExpression() : LeafMatchExpression( MOD ){}
Status init( StringData path, int divisor, int remainder );
virtual LeafMatchExpression* shallowClone() const {
ModMatchExpression* m = new ModMatchExpression();
m->init( path(), _divisor, _remainder );
if ( getTag() ) {
m->setTag(getTag()->clone());
}
return m;
}
virtual bool matchesSingleElement( const BSONElement& e ) const;
virtual void debugString( StringBuilder& debug, int level ) const;
virtual void toBSON(BSONObjBuilder* out) const;
virtual bool equivalent( const MatchExpression* other ) const;
int getDivisor() const { return _divisor; }
int getRemainder() const { return _remainder; }
private:
int _divisor;
int _remainder;
};
class ExistsMatchExpression : public LeafMatchExpression {
public:
ExistsMatchExpression() : LeafMatchExpression( EXISTS ){}
Status init( StringData path );
virtual LeafMatchExpression* shallowClone() const {
ExistsMatchExpression* e = new ExistsMatchExpression();
e->init( path() );
if ( getTag() ) {
e->setTag(getTag()->clone());
}
return e;
}
virtual bool matchesSingleElement( const BSONElement& e ) const;
virtual void debugString( StringBuilder& debug, int level ) const;
virtual void toBSON(BSONObjBuilder* out) const;
virtual bool equivalent( const MatchExpression* other ) const;
};
/**
* INTERNAL
* terrible name
* holds the entries of an $in or $all
* either scalars or regex
*/
class ArrayFilterEntries {
MONGO_DISALLOW_COPYING( ArrayFilterEntries );
public:
ArrayFilterEntries();
~ArrayFilterEntries();
Status addEquality( const BSONElement& e );
Status addRegex( RegexMatchExpression* expr );
const BSONElementSet& equalities() const { return _equalities; }
bool contains( const BSONElement& elem ) const { return _equalities.count(elem) > 0; }
size_t numRegexes() const { return _regexes.size(); }
RegexMatchExpression* regex( int idx ) const { return _regexes[idx]; }
bool hasNull() const { return _hasNull; }
bool singleNull() const { return size() == 1 && _hasNull; }
bool hasEmptyArray() const { return _hasEmptyArray; }
int size() const { return _equalities.size() + _regexes.size(); }
bool equivalent( const ArrayFilterEntries& other ) const;
void copyTo( ArrayFilterEntries& toFillIn ) const;
void debugString( StringBuilder& debug ) const;
void toBSON(BSONArrayBuilder* out) const;
private:
bool _hasNull; // if _equalities has a jstNULL element in it
bool _hasEmptyArray;
BSONElementSet _equalities;
std::vector _regexes;
};
/**
* query operator: $in
*/
class InMatchExpression : public LeafMatchExpression {
public:
InMatchExpression() : LeafMatchExpression( MATCH_IN ){}
Status init( StringData path );
virtual LeafMatchExpression* shallowClone() const;
ArrayFilterEntries* getArrayFilterEntries() { return &_arrayEntries; }
virtual bool matchesSingleElement( const BSONElement& e ) const;
virtual void debugString( StringBuilder& debug, int level ) const;
virtual void toBSON(BSONObjBuilder* out) const;
virtual bool equivalent( const MatchExpression* other ) const;
void copyTo( InMatchExpression* toFillIn ) const;
const ArrayFilterEntries& getData() const { return _arrayEntries; }
private:
bool _matchesRealElement( const BSONElement& e ) const;
ArrayFilterEntries _arrayEntries;
};
//
// The odd duck out, TYPE_OPERATOR.
//
/**
* Type has some odd semantics with arrays and as such it can't inherit from
* LeafMatchExpression.
*/
class TypeMatchExpression : public MatchExpression {
public:
TypeMatchExpression() : MatchExpression( TYPE_OPERATOR ){}
Status init( StringData path, int type );
virtual MatchExpression* shallowClone() const {
TypeMatchExpression* e = new TypeMatchExpression();
e->init(_path, _type);
if ( getTag() ) {
e->setTag(getTag()->clone());
}
return e;
}
virtual bool matchesSingleElement( const BSONElement& e ) const;
virtual bool matches( const MatchableDocument* doc, MatchDetails* details = 0 ) const;
virtual void debugString( StringBuilder& debug, int level ) const;
virtual void toBSON(BSONObjBuilder* out) const;
virtual bool equivalent( const MatchExpression* other ) const;
/**
* What is the type we're matching against?
*/
int getData() const { return _type; }
virtual const StringData path() const { return _path; }
private:
bool _matches( StringData path,
const MatchableDocument* doc,
MatchDetails* details = 0 ) const;
StringData _path;
ElementPath _elementPath;
int _type;
};
} // namespace mongo