// projection.h
/* Copyright 2009 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/pch.h"
#include "mongo/util/string_map.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/matcher/matcher.h"
namespace mongo {
/**
* given a document and a projection specification
* can transform the document
* currently supports specifying which fields and $slice
*/
class Projection {
public:
class KeyOnly {
public:
KeyOnly() : _stringSize(0) {}
BSONObj hydrate( const BSONObj& key ) const;
void addNo() { _add( false , "" ); }
void addYes( const string& name ) { _add( true , name ); }
private:
void _add( bool b , const string& name ) {
_include.push_back( b );
_names.push_back( name );
_stringSize += name.size();
}
vector _include; // one entry per field in key. true iff should be in output
vector _names; // name of field since key doesn't have names
int _stringSize;
};
enum ArrayOpType {
ARRAY_OP_NORMAL = 0,
ARRAY_OP_ELEM_MATCH,
ARRAY_OP_POSITIONAL
};
Projection() :
_include(true) ,
_special(false) ,
_includeID(true) ,
_skip(0) ,
_limit(-1) ,
_arrayOpType(ARRAY_OP_NORMAL),
_hasNonSimple(false) {
}
/**
* called once per lifetime
* e.g. { "x" : 1 , "a.y" : 1 }
*/
void init(const BSONObj& spec, const MatchExpressionParser::WhereCallback& whereCallback);
/**
* @return the spec init was called with
*/
BSONObj getSpec() const { return _source; }
/**
* transforms in according to spec
*/
BSONObj transform( const BSONObj& in, const MatchDetails* details = NULL ) const;
/**
* transforms in according to spec
*/
void transform( const BSONObj& in , BSONObjBuilder& b, const MatchDetails* details = NULL ) const;
/**
* @return if the keyPattern has all the information needed to return then
* return a new KeyOnly otherwise null
* NOTE: a key may have modified the actual data
* which has to be handled above this (arrays, geo)
*/
KeyOnly* checkKey( const BSONObj& keyPattern ) const;
bool includeID() const { return _includeID; }
/**
* get the type of array operator for the projection
* @return ARRAY_OP_NORMAL if no array projection modifier,
* ARRAY_OP_ELEM_MATCH if one or more $elemMatch specifier,
* ARRAY_OP_POSITIONAL if one '.$' projection specified
*/
ArrayOpType getArrayOpType() const;
/**
* Validate the given query satisfies this projection's positional operator.
* NOTE: this function is only used to validate projections with a positional operator.
* @param query User-supplied query specifier
* @return Field name if found, empty string otherwise.
*/
void validateQuery( const BSONObj query ) const;
private:
/**
* appends e to b if user wants it
* will descend into e if needed
*/
void append( BSONObjBuilder& b , const BSONElement& e, const MatchDetails* details = NULL,
const ArrayOpType arrayOpType = ARRAY_OP_NORMAL ) const;
void add( const string& field, bool include );
void add( const string& field, int skip, int limit );
void appendArray( BSONObjBuilder& b , const BSONObj& a , bool nested=false) const;
bool _include; // true if default at this level is to include
bool _special; // true if this level can't be skipped or included without recursing
//TODO: benchmark vector vs map
typedef StringMap > FieldMap;
FieldMap _fields;
BSONObj _source;
bool _includeID;
// used for $slice operator
int _skip;
int _limit;
// used for $elemMatch and positional operator ($)
typedef StringMap > Matchers;
Matchers _matchers;
ArrayOpType _arrayOpType;
bool _hasNonSimple;
};
}