diff options
Diffstat (limited to 'src/mongo/bson/bsonobjbuilder.h')
-rw-r--r-- | src/mongo/bson/bsonobjbuilder.h | 842 |
1 files changed, 842 insertions, 0 deletions
diff --git a/src/mongo/bson/bsonobjbuilder.h b/src/mongo/bson/bsonobjbuilder.h new file mode 100644 index 00000000000..1fdbcba18a6 --- /dev/null +++ b/src/mongo/bson/bsonobjbuilder.h @@ -0,0 +1,842 @@ +/* bsonobjbuilder.h + + Classes in this file: + BSONObjBuilder + BSONArrayBuilder +*/ + +/* Copyright 2009 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <limits> +#include <cmath> +#include <boost/static_assert.hpp> +#include "bsonelement.h" +#include "bsonobj.h" +#include "bsonmisc.h" + +namespace mongo { + + using namespace std; + +#if defined(_WIN32) +// warning: 'this' : used in base member initializer list +#pragma warning( disable : 4355 ) +#endif + + template<typename T> + class BSONFieldValue { + public: + BSONFieldValue( const string& name , const T& t ) { + _name = name; + _t = t; + } + + const T& value() const { return _t; } + const string& name() const { return _name; } + + private: + string _name; + T _t; + }; + + template<typename T> + class BSONField { + public: + BSONField( const string& name , const string& longName="" ) + : _name(name), _longName(longName) {} + const string& name() const { return _name; } + operator string() const { return _name; } + + BSONFieldValue<T> make( const T& t ) const { + return BSONFieldValue<T>( _name , t ); + } + + BSONFieldValue<BSONObj> gt( const T& t ) const { return query( "$gt" , t ); } + BSONFieldValue<BSONObj> lt( const T& t ) const { return query( "$lt" , t ); } + + BSONFieldValue<BSONObj> query( const char * q , const T& t ) const; + + BSONFieldValue<T> operator()( const T& t ) const { + return BSONFieldValue<T>( _name , t ); + } + + private: + string _name; + string _longName; + }; + + /** Utility for creating a BSONObj. + See also the BSON() and BSON_ARRAY() macros. + */ + class BSONObjBuilder : boost::noncopyable { + public: + /** @param initsize this is just a hint as to the final size of the object */ + BSONObjBuilder(int initsize=512) : _b(_buf), _buf(initsize + sizeof(unsigned)), _offset( sizeof(unsigned) ), _s( this ) , _tracker(0) , _doneCalled(false) { + _b.appendNum((unsigned)0); // ref-count + _b.skip(4); /*leave room for size field and ref-count*/ + } + + /** @param baseBuilder construct a BSONObjBuilder using an existing BufBuilder + * This is for more efficient adding of subobjects/arrays. See docs for subobjStart for example. + */ + BSONObjBuilder( BufBuilder &baseBuilder ) : _b( baseBuilder ), _buf( 0 ), _offset( baseBuilder.len() ), _s( this ) , _tracker(0) , _doneCalled(false) { + _b.skip( 4 ); + } + + BSONObjBuilder( const BSONSizeTracker & tracker ) : _b(_buf) , _buf(tracker.getSize() + sizeof(unsigned) ), _offset( sizeof(unsigned) ), _s( this ) , _tracker( (BSONSizeTracker*)(&tracker) ) , _doneCalled(false) { + _b.appendNum((unsigned)0); // ref-count + _b.skip(4); + } + + ~BSONObjBuilder() { + if ( !_doneCalled && _b.buf() && _buf.getSize() == 0 ) { + _done(); + } + } + + /** add all the fields from the object specified to this object */ + BSONObjBuilder& appendElements(BSONObj x); + + /** add all the fields from the object specified to this object if they don't exist already */ + BSONObjBuilder& appendElementsUnique( BSONObj x ); + + /** append element to the object we are building */ + BSONObjBuilder& append( const BSONElement& e) { + assert( !e.eoo() ); // do not append eoo, that would corrupt us. the builder auto appends when done() is called. + _b.appendBuf((void*) e.rawdata(), e.size()); + return *this; + } + + /** append an element but with a new name */ + BSONObjBuilder& appendAs(const BSONElement& e, const StringData& fieldName) { + assert( !e.eoo() ); // do not append eoo, that would corrupt us. the builder auto appends when done() is called. + _b.appendNum((char) e.type()); + _b.appendStr(fieldName); + _b.appendBuf((void *) e.value(), e.valuesize()); + return *this; + } + + /** add a subobject as a member */ + BSONObjBuilder& append(const StringData& fieldName, BSONObj subObj) { + _b.appendNum((char) Object); + _b.appendStr(fieldName); + _b.appendBuf((void *) subObj.objdata(), subObj.objsize()); + return *this; + } + + /** add a subobject as a member */ + BSONObjBuilder& appendObject(const StringData& fieldName, const char * objdata , int size = 0 ) { + assert( objdata ); + if ( size == 0 ) { + size = *((int*)objdata); + } + + assert( size > 4 && size < 100000000 ); + + _b.appendNum((char) Object); + _b.appendStr(fieldName); + _b.appendBuf((void*)objdata, size ); + return *this; + } + + /** add header for a new subobject and return bufbuilder for writing to + * the subobject's body + * + * example: + * + * BSONObjBuilder b; + * BSONObjBuilder sub (b.subobjStart("fieldName")); + * // use sub + * sub.done() + * // use b and convert to object + */ + BufBuilder &subobjStart(const StringData& fieldName) { + _b.appendNum((char) Object); + _b.appendStr(fieldName); + return _b; + } + + /** add a subobject as a member with type Array. Thus arr object should have "0", "1", ... + style fields in it. + */ + BSONObjBuilder& appendArray(const StringData& fieldName, const BSONObj &subObj) { + _b.appendNum((char) Array); + _b.appendStr(fieldName); + _b.appendBuf((void *) subObj.objdata(), subObj.objsize()); + return *this; + } + BSONObjBuilder& append(const StringData& fieldName, BSONArray arr) { + return appendArray(fieldName, arr); + } + + /** add header for a new subarray and return bufbuilder for writing to + the subarray's body */ + BufBuilder &subarrayStart(const StringData& fieldName) { + _b.appendNum((char) Array); + _b.appendStr(fieldName); + return _b; + } + + /** Append a boolean element */ + BSONObjBuilder& appendBool(const StringData& fieldName, int val) { + _b.appendNum((char) Bool); + _b.appendStr(fieldName); + _b.appendNum((char) (val?1:0)); + return *this; + } + + /** Append a boolean element */ + BSONObjBuilder& append(const StringData& fieldName, bool val) { + _b.appendNum((char) Bool); + _b.appendStr(fieldName); + _b.appendNum((char) (val?1:0)); + return *this; + } + + /** Append a 32 bit integer element */ + BSONObjBuilder& append(const StringData& fieldName, int n) { + _b.appendNum((char) NumberInt); + _b.appendStr(fieldName); + _b.appendNum(n); + return *this; + } + + /** Append a 32 bit unsigned element - cast to a signed int. */ + BSONObjBuilder& append(const StringData& fieldName, unsigned n) { + return append(fieldName, (int) n); + } + + /** Append a NumberLong */ + BSONObjBuilder& append(const StringData& fieldName, long long n) { + _b.appendNum((char) NumberLong); + _b.appendStr(fieldName); + _b.appendNum(n); + return *this; + } + + /** appends a number. if n < max(int)/2 then uses int, otherwise long long */ + BSONObjBuilder& appendIntOrLL( const StringData& fieldName , long long n ) { + long long x = n; + if ( x < 0 ) + x = x * -1; + if ( x < ( (numeric_limits<int>::max)() / 2 ) ) // extra () to avoid max macro on windows + append( fieldName , (int)n ); + else + append( fieldName , n ); + return *this; + } + + /** + * appendNumber is a series of method for appending the smallest sensible type + * mostly for JS + */ + BSONObjBuilder& appendNumber( const StringData& fieldName , int n ) { + return append( fieldName , n ); + } + + BSONObjBuilder& appendNumber( const StringData& fieldName , double d ) { + return append( fieldName , d ); + } + + BSONObjBuilder& appendNumber( const StringData& fieldName , size_t n ) { + static size_t maxInt = (size_t)pow( 2.0 , 30.0 ); + + if ( n < maxInt ) + append( fieldName , (int)n ); + else + append( fieldName , (long long)n ); + return *this; + } + + BSONObjBuilder& appendNumber( const StringData& fieldName , long long l ) { + static long long maxInt = (int)pow( 2.0 , 30.0 ); + static long long maxDouble = (long long)pow( 2.0 , 40.0 ); + long long x = l >= 0 ? l : -l; + if ( x < maxInt ) + append( fieldName , (int)l ); + else if ( x < maxDouble ) + append( fieldName , (double)l ); + else + append( fieldName , l ); + return *this; + } + + /** Append a double element */ + BSONObjBuilder& append(const StringData& fieldName, double n) { + _b.appendNum((char) NumberDouble); + _b.appendStr(fieldName); + _b.appendNum(n); + return *this; + } + + /** tries to append the data as a number + * @return true if the data was able to be converted to a number + */ + bool appendAsNumber( const StringData& fieldName , const string& data ); + + /** Append a BSON Object ID (OID type). + @deprecated Generally, it is preferred to use the append append(name, oid) + method for this. + */ + BSONObjBuilder& appendOID(const StringData& fieldName, OID *oid = 0 , bool generateIfBlank = false ) { + _b.appendNum((char) jstOID); + _b.appendStr(fieldName); + if ( oid ) + _b.appendBuf( (void *) oid, 12 ); + else { + OID tmp; + if ( generateIfBlank ) + tmp.init(); + else + tmp.clear(); + _b.appendBuf( (void *) &tmp, 12 ); + } + return *this; + } + + /** + Append a BSON Object ID. + @param fieldName Field name, e.g., "_id". + @returns the builder object + */ + BSONObjBuilder& append( const StringData& fieldName, OID oid ) { + _b.appendNum((char) jstOID); + _b.appendStr(fieldName); + _b.appendBuf( (void *) &oid, 12 ); + return *this; + } + + /** + Generate and assign an object id for the _id field. + _id should be the first element in the object for good performance. + */ + BSONObjBuilder& genOID() { + return append("_id", OID::gen()); + } + + /** Append a time_t date. + @param dt a C-style 32 bit date value, that is + the number of seconds since January 1, 1970, 00:00:00 GMT + */ + BSONObjBuilder& appendTimeT(const StringData& fieldName, time_t dt) { + _b.appendNum((char) Date); + _b.appendStr(fieldName); + _b.appendNum(static_cast<unsigned long long>(dt) * 1000); + return *this; + } + /** Append a date. + @param dt a Java-style 64 bit date value, that is + the number of milliseconds since January 1, 1970, 00:00:00 GMT + */ + BSONObjBuilder& appendDate(const StringData& fieldName, Date_t dt) { + /* easy to pass a time_t to this and get a bad result. thus this warning. */ +#if defined(_DEBUG) && defined(MONGO_EXPOSE_MACROS) + if( dt > 0 && dt <= 0xffffffff ) { + static int n; + if( n++ == 0 ) + log() << "DEV WARNING appendDate() called with a tiny (but nonzero) date" << endl; + } +#endif + _b.appendNum((char) Date); + _b.appendStr(fieldName); + _b.appendNum(dt); + return *this; + } + BSONObjBuilder& append(const StringData& fieldName, Date_t dt) { + return appendDate(fieldName, dt); + } + + /** Append a regular expression value + @param regex the regular expression pattern + @param regex options such as "i" or "g" + */ + BSONObjBuilder& appendRegex(const StringData& fieldName, const StringData& regex, const StringData& options = "") { + _b.appendNum((char) RegEx); + _b.appendStr(fieldName); + _b.appendStr(regex); + _b.appendStr(options); + return *this; + } + + BSONObjBuilder& appendCode(const StringData& fieldName, const StringData& code) { + _b.appendNum((char) Code); + _b.appendStr(fieldName); + _b.appendNum((int) code.size()+1); + _b.appendStr(code); + return *this; + } + + /** Append a string element. + @param sz size includes terminating null character */ + BSONObjBuilder& append(const StringData& fieldName, const char *str, int sz) { + _b.appendNum((char) String); + _b.appendStr(fieldName); + _b.appendNum((int)sz); + _b.appendBuf(str, sz); + return *this; + } + /** Append a string element */ + BSONObjBuilder& append(const StringData& fieldName, const char *str) { + return append(fieldName, str, (int) strlen(str)+1); + } + /** Append a string element */ + BSONObjBuilder& append(const StringData& fieldName, const string& str) { + return append(fieldName, str.c_str(), (int) str.size()+1); + } + + BSONObjBuilder& appendSymbol(const StringData& fieldName, const StringData& symbol) { + _b.appendNum((char) Symbol); + _b.appendStr(fieldName); + _b.appendNum((int) symbol.size()+1); + _b.appendStr(symbol); + return *this; + } + + /** Append a Null element to the object */ + BSONObjBuilder& appendNull( const StringData& fieldName ) { + _b.appendNum( (char) jstNULL ); + _b.appendStr( fieldName ); + return *this; + } + + // Append an element that is less than all other keys. + BSONObjBuilder& appendMinKey( const StringData& fieldName ) { + _b.appendNum( (char) MinKey ); + _b.appendStr( fieldName ); + return *this; + } + // Append an element that is greater than all other keys. + BSONObjBuilder& appendMaxKey( const StringData& fieldName ) { + _b.appendNum( (char) MaxKey ); + _b.appendStr( fieldName ); + return *this; + } + + // Append a Timestamp field -- will be updated to next OpTime on db insert. + BSONObjBuilder& appendTimestamp( const StringData& fieldName ) { + _b.appendNum( (char) Timestamp ); + _b.appendStr( fieldName ); + _b.appendNum( (unsigned long long) 0 ); + return *this; + } + + BSONObjBuilder& appendTimestamp( const StringData& fieldName , unsigned long long val ) { + _b.appendNum( (char) Timestamp ); + _b.appendStr( fieldName ); + _b.appendNum( val ); + return *this; + } + + /** + Timestamps are a special BSON datatype that is used internally for replication. + Append a timestamp element to the object being ebuilt. + @param time - in millis (but stored in seconds) + */ + BSONObjBuilder& appendTimestamp( const StringData& fieldName , unsigned long long time , unsigned int inc ); + + /* + Append an element of the deprecated DBRef type. + @deprecated + */ + BSONObjBuilder& appendDBRef( const StringData& fieldName, const StringData& ns, const OID &oid ) { + _b.appendNum( (char) DBRef ); + _b.appendStr( fieldName ); + _b.appendNum( (int) ns.size() + 1 ); + _b.appendStr( ns ); + _b.appendBuf( (void *) &oid, 12 ); + return *this; + } + + /** Append a binary data element + @param fieldName name of the field + @param len length of the binary data in bytes + @param subtype subtype information for the data. @see enum BinDataType in bsontypes.h. + Use BinDataGeneral if you don't care about the type. + @param data the byte array + */ + BSONObjBuilder& appendBinData( const StringData& fieldName, int len, BinDataType type, const void *data ) { + _b.appendNum( (char) BinData ); + _b.appendStr( fieldName ); + _b.appendNum( len ); + _b.appendNum( (char) type ); + _b.appendBuf( data, len ); + return *this; + } + + /** + Subtype 2 is deprecated. + Append a BSON bindata bytearray element. + @param data a byte array + @param len the length of data + */ + BSONObjBuilder& appendBinDataArrayDeprecated( const char * fieldName , const void * data , int len ) { + _b.appendNum( (char) BinData ); + _b.appendStr( fieldName ); + _b.appendNum( len + 4 ); + _b.appendNum( (char)0x2 ); + _b.appendNum( len ); + _b.appendBuf( data, len ); + return *this; + } + + /** Append to the BSON object a field of type CodeWScope. This is a javascript code + fragment accompanied by some scope that goes with it. + */ + BSONObjBuilder& appendCodeWScope( const StringData& fieldName, const StringData& code, const BSONObj &scope ) { + _b.appendNum( (char) CodeWScope ); + _b.appendStr( fieldName ); + _b.appendNum( ( int )( 4 + 4 + code.size() + 1 + scope.objsize() ) ); + _b.appendNum( ( int ) code.size() + 1 ); + _b.appendStr( code ); + _b.appendBuf( ( void * )scope.objdata(), scope.objsize() ); + return *this; + } + + void appendUndefined( const StringData& fieldName ) { + _b.appendNum( (char) Undefined ); + _b.appendStr( fieldName ); + } + + /* helper function -- see Query::where() for primary way to do this. */ + void appendWhere( const StringData& code, const BSONObj &scope ) { + appendCodeWScope( "$where" , code , scope ); + } + + /** + these are the min/max when comparing, not strict min/max elements for a given type + */ + void appendMinForType( const StringData& fieldName , int type ); + void appendMaxForType( const StringData& fieldName , int type ); + + /** Append an array of values. */ + template < class T > + BSONObjBuilder& append( const StringData& fieldName, const vector< T >& vals ); + + template < class T > + BSONObjBuilder& append( const StringData& fieldName, const list< T >& vals ); + + /** Append a set of values. */ + template < class T > + BSONObjBuilder& append( const StringData& fieldName, const set< T >& vals ); + + /** + * destructive + * The returned BSONObj will free the buffer when it is finished. + * @return owned BSONObj + */ + BSONObj obj() { + bool own = owned(); + massert( 10335 , "builder does not own memory", own ); + doneFast(); + BSONObj::Holder* h = (BSONObj::Holder*)_b.buf(); + decouple(); // sets _b.buf() to NULL + return BSONObj(h); + } + + /** Fetch the object we have built. + BSONObjBuilder still frees the object when the builder goes out of + scope -- very important to keep in mind. Use obj() if you + would like the BSONObj to last longer than the builder. + */ + BSONObj done() { + return BSONObj(_done()); + } + + // Like 'done' above, but does not construct a BSONObj to return to the caller. + void doneFast() { + (void)_done(); + } + + /** Peek at what is in the builder, but leave the builder ready for more appends. + The returned object is only valid until the next modification or destruction of the builder. + Intended use case: append a field if not already there. + */ + BSONObj asTempObj() { + BSONObj temp(_done()); + _b.setlen(_b.len()-1); //next append should overwrite the EOO + _doneCalled = false; + return temp; + } + + /* assume ownership of the buffer - you must then free it (with free()) */ + char* decouple(int& l) { + char *x = _done(); + assert( x ); + l = _b.len(); + _b.decouple(); + return x; + } + void decouple() { + _b.decouple(); // post done() call version. be sure jsobj frees... + } + + void appendKeys( const BSONObj& keyPattern , const BSONObj& values ); + + static string numStr( int i ) { + if (i>=0 && i<100 && numStrsReady) + return numStrs[i]; + StringBuilder o; + o << i; + return o.str(); + } + + /** Stream oriented way to add field names and values. */ + BSONObjBuilderValueStream &operator<<(const char * name ) { + _s.endField( name ); + return _s; + } + + /** Stream oriented way to add field names and values. */ + BSONObjBuilder& operator<<( GENOIDLabeler ) { return genOID(); } + + // prevent implicit string conversions which would allow bad things like BSON( BSON( "foo" << 1 ) << 2 ) + struct ForceExplicitString { + ForceExplicitString( const string &str ) : str_( str ) {} + string str_; + }; + + /** Stream oriented way to add field names and values. */ + BSONObjBuilderValueStream &operator<<( const ForceExplicitString& name ) { + return operator<<( name.str_.c_str() ); + } + + Labeler operator<<( const Labeler::Label &l ) { + massert( 10336 , "No subobject started", _s.subobjStarted() ); + return _s << l; + } + + template<typename T> + BSONObjBuilderValueStream& operator<<( const BSONField<T>& f ) { + _s.endField( f.name().c_str() ); + return _s; + } + + template<typename T> + BSONObjBuilder& operator<<( const BSONFieldValue<T>& v ) { + append( v.name().c_str() , v.value() ); + return *this; + } + + BSONObjBuilder& operator<<( const BSONElement& e ){ + append( e ); + return *this; + } + + /** @return true if we are using our own bufbuilder, and not an alternate that was given to us in our constructor */ + bool owned() const { return &_b == &_buf; } + + BSONObjIterator iterator() const ; + + bool hasField( const StringData& name ) const ; + + int len() const { return _b.len(); } + + BufBuilder& bb() { return _b; } + + private: + char* _done() { + if ( _doneCalled ) + return _b.buf() + _offset; + + _doneCalled = true; + _s.endField(); + _b.appendNum((char) EOO); + char *data = _b.buf() + _offset; + int size = _b.len() - _offset; + *((int*)data) = size; + if ( _tracker ) + _tracker->got( size ); + return data; + } + + BufBuilder &_b; + BufBuilder _buf; + int _offset; + BSONObjBuilderValueStream _s; + BSONSizeTracker * _tracker; + bool _doneCalled; + + static const string numStrs[100]; // cache of 0 to 99 inclusive + static bool numStrsReady; // for static init safety. see comments in db/jsobj.cpp + }; + + class BSONArrayBuilder : boost::noncopyable { + public: + BSONArrayBuilder() : _i(0), _b() {} + BSONArrayBuilder( BufBuilder &_b ) : _i(0), _b(_b) {} + BSONArrayBuilder( int initialSize ) : _i(0), _b(initialSize) {} + + template <typename T> + BSONArrayBuilder& append(const T& x) { + _b.append(num(), x); + return *this; + } + + BSONArrayBuilder& append(const BSONElement& e) { + _b.appendAs(e, num()); + return *this; + } + + template <typename T> + BSONArrayBuilder& operator<<(const T& x) { + return append(x); + } + + void appendNull() { + _b.appendNull(num()); + } + + /** + * destructive - ownership moves to returned BSONArray + * @return owned BSONArray + */ + BSONArray arr() { return BSONArray(_b.obj()); } + + BSONObj done() { return _b.done(); } + + void doneFast() { _b.doneFast(); } + + template <typename T> + BSONArrayBuilder& append(const StringData& name, const T& x) { + fill( name ); + append( x ); + return *this; + } + + // These two just use next position + BufBuilder &subobjStart() { return _b.subobjStart( num() ); } + BufBuilder &subarrayStart() { return _b.subarrayStart( num() ); } + + // These fill missing entries up to pos. if pos is < next pos is ignored + BufBuilder &subobjStart(int pos) { + fill(pos); + return _b.subobjStart( num() ); + } + BufBuilder &subarrayStart(int pos) { + fill(pos); + return _b.subarrayStart( num() ); + } + + // These should only be used where you really need interface compatability with BSONObjBuilder + // Currently they are only used by update.cpp and it should probably stay that way + BufBuilder &subobjStart( const StringData& name ) { + fill( name ); + return _b.subobjStart( num() ); + } + + BufBuilder &subarrayStart( const char *name ) { + fill( name ); + return _b.subarrayStart( num() ); + } + + void appendArray( const StringData& name, BSONObj subObj ) { + fill( name ); + _b.appendArray( num(), subObj ); + } + + void appendAs( const BSONElement &e, const char *name) { + fill( name ); + append( e ); + } + + int len() const { return _b.len(); } + int arrSize() const { return _i; } + + private: + // These two are undefined privates to prevent their accidental + // use as we don't support unsigned ints in BSON + BSONObjBuilder& append(const StringData& fieldName, unsigned int val); + BSONObjBuilder& append(const StringData& fieldName, unsigned long long val); + + void fill( const StringData& name ) { + char *r; + long int n = strtol( name.data(), &r, 10 ); + if ( *r ) + uasserted( 13048, (string)"can't append to array using string field name [" + name.data() + "]" ); + fill(n); + } + + void fill (int upTo){ + // if this is changed make sure to update error message and jstests/set7.js + const int maxElems = 1500000; + BOOST_STATIC_ASSERT(maxElems < (BSONObjMaxUserSize/10)); + uassert(15891, "can't backfill array to larger than 1,500,000 elements", upTo <= maxElems); + + while( _i < upTo ) + append( nullElt() ); + } + + static BSONElement nullElt() { + static BSONObj n = nullObj(); + return n.firstElement(); + } + + static BSONObj nullObj() { + BSONObjBuilder _b; + _b.appendNull( "" ); + return _b.obj(); + } + + string num() { return _b.numStr(_i++); } + int _i; + BSONObjBuilder _b; + }; + + template < class T > + inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldName, const vector< T >& vals ) { + BSONObjBuilder arrBuilder; + for ( unsigned int i = 0; i < vals.size(); ++i ) + arrBuilder.append( numStr( i ), vals[ i ] ); + appendArray( fieldName, arrBuilder.done() ); + return *this; + } + + template < class L > + inline BSONObjBuilder& _appendIt( BSONObjBuilder& _this, const StringData& fieldName, const L& vals ) { + BSONObjBuilder arrBuilder; + int n = 0; + for( typename L::const_iterator i = vals.begin(); i != vals.end(); i++ ) + arrBuilder.append( BSONObjBuilder::numStr(n++), *i ); + _this.appendArray( fieldName, arrBuilder.done() ); + return _this; + } + + template < class T > + inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldName, const list< T >& vals ) { + return _appendIt< list< T > >( *this, fieldName, vals ); + } + + template < class T > + inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldName, const set< T >& vals ) { + return _appendIt< set< T > >( *this, fieldName, vals ); + } + + + // $or helper: OR(BSON("x" << GT << 7), BSON("y" << LT 6)); + inline BSONObj OR(const BSONObj& a, const BSONObj& b) + { return BSON( "$or" << BSON_ARRAY(a << b) ); } + inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c) + { return BSON( "$or" << BSON_ARRAY(a << b << c) ); } + inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d) + { return BSON( "$or" << BSON_ARRAY(a << b << c << d) ); } + inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e) + { return BSON( "$or" << BSON_ARRAY(a << b << c << d << e) ); } + inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e, const BSONObj& f) + { return BSON( "$or" << BSON_ARRAY(a << b << c << d << e << f) ); } + +} |