0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-30 00:56:44 +01:00
mongodb/db/jsobj.h

2046 lines
66 KiB
C++

/** @file jsobj.h
BSON classes
*/
/* 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.
*/
/**
BSONObj and its helpers
"BSON" stands for "binary JSON" -- ie a binary way to represent objects that would be
represented in JSON (plus a few extensions useful for databases & other languages).
http://www.mongodb.org/display/DOCS/BSON
*/
#pragma once
#include "../stdafx.h"
#include "../util/builder.h"
#include "../util/optime.h"
#include "boost/utility.hpp"
#include <set>
namespace mongo {
class BSONObj;
struct BSONArray; // empty subclass of BSONObj useful for overloading
class BSONElement;
class Record;
class BSONObjBuilder;
class BSONArrayBuilder;
class BSONObjBuilderValueStream;
#pragma pack(1)
/**
the complete list of valid BSON types
*/
enum BSONType {
/** smaller than all other types */
MinKey=-1,
/** end of object */
EOO=0,
/** double precision floating point value */
NumberDouble=1,
/** character string, stored in utf8 */
String=2,
/** an embedded object */
Object=3,
/** an embedded array */
Array=4,
/** binary data */
BinData=5,
/** Undefined type */
Undefined=6,
/** ObjectId */
jstOID=7,
/** boolean type */
Bool=8,
/** date type */
Date=9,
/** null type */
jstNULL=10,
/** regular expression, a pattern with options */
RegEx=11,
/** deprecated / will be redesigned */
DBRef=12,
/** deprecated / use CodeWScope */
Code=13,
/** a programming language (e.g., Python) symbol */
Symbol=14,
/** javascript code that can execute on the database server, with SavedContext */
CodeWScope=15,
/** 32 bit signed integer */
NumberInt = 16,
/** Updated to a Date with value next OpTime on insert */
Timestamp = 17,
/** 64 bit integer */
NumberLong = 18,
/** max type that is not MaxKey */
JSTypeMax=18,
/** larger than all other types */
MaxKey=127
};
/* subtypes of BinData.
bdtCustom and above are ones that the JS compiler understands, but are
opaque to the database.
*/
enum BinDataType { Function=1, ByteArray=2, bdtUUID = 3, MD5Type=5, bdtCustom=128 };
/** Object ID type.
BSON objects typically have an _id field for the object id. This field should be the first
member of the object when present. class OID is a special type that is a 12 byte id which
is likely to be unique to the system. You may also use other types for _id's.
When _id field is missing from a BSON object, on an insert the database may insert one
automatically in certain circumstances.
Warning: You must call OID::newState() after a fork().
*/
class OID {
union {
struct{
long long a;
unsigned b;
};
unsigned char data[12];
};
static unsigned _machine;
public:
/** call this after a fork */
static void newState();
/** initialize to 'null' */
void clear() { a = 0; b = 0; }
const unsigned char *getData() const { return data; }
bool operator==(const OID& r) {
return a==r.a&&b==r.b;
}
bool operator!=(const OID& r) {
return a!=r.a||b!=r.b;
}
/** The object ID output as 24 hex digits. */
string str() const {
stringstream s;
s << hex;
// s.fill( '0' );
// s.width( 2 );
// fill wasn't working so doing manually...
for( int i = 0; i < 8; i++ ) {
unsigned u = data[i];
if( u < 16 ) s << '0';
s << u;
}
const unsigned char * raw = (const unsigned char*)&b;
for( int i = 0; i < 4; i++ ) {
unsigned u = raw[i];
if( u < 16 ) s << '0';
s << u;
}
/*
s.width( 16 );
s << a;
s.width( 8 );
s << b;
s << dec;
*/
return s.str();
}
/**
sets the contents to a new oid / randomized value
*/
void init();
/** Set to the hex string value specified. */
void init( string s );
};
ostream& operator<<( ostream &s, const OID &o );
/** Formatting mode for generating JSON from BSON.
See <http://mongodb.onconfluence.com/display/DOCS/Mongo+Extended+JSON>
for details.
*/
enum JsonStringFormat {
/** strict RFC format */
Strict,
/** 10gen format, which is close to JS format. This form is understandable by
javascript running inside the Mongo server via eval() */
TenGen,
/** Javascript JSON compatible */
JS
};
/* l and r MUST have same type when called: check that first. */
int compareElementValues(const BSONElement& l, const BSONElement& r);
#pragma pack()
/* internals
<type><fieldName ><value>
-------- size() ------------
-fieldNameSize-
value()
type()
*/
/** BSONElement represents an "element" in a BSONObj. So for the object { a : 3, b : "abc" },
'a : 3' is the first element (key+value).
The BSONElement object points into the BSONObj's data. Thus the BSONObj must stay in scope
for the life of the BSONElement.
*/
class BSONElement {
friend class BSONObjIterator;
friend class BSONObj;
public:
string toString( bool includeFieldName = true ) const;
operator string() const { return toString(); }
string jsonString( JsonStringFormat format, bool includeFieldNames = true ) const;
/** Returns the type of the element */
BSONType type() const {
return (BSONType) *data;
}
/** returns the tyoe of the element fixed for the main type
the main purpose is numbers. any numeric type will return NumberDouble
Note: if the order changes, indexes have to be re-built or than can be corruption
*/
int canonicalType() const {
BSONType t = type();
switch ( t ){
case MinKey:
case MaxKey:
return t;
case EOO:
case Undefined:
return 0;
case jstNULL:
return 5;
case NumberDouble:
case NumberInt:
case NumberLong:
return 10;
case String:
case Symbol:
return 15;
case Object:
return 20;
case Array:
return 25;
case BinData:
return 30;
case jstOID:
return 35;
case Bool:
return 40;
case Date:
case Timestamp:
return 45;
case RegEx:
return 50;
case DBRef:
return 55;
case Code:
return 60;
case CodeWScope:
return 65;
default:
assert(0);
return -1;
}
}
/** Indicates if it is the end-of-object element, which is present at the end of
every BSON object.
*/
bool eoo() const {
return type() == EOO;
}
/** Size of the element.
@param maxLen If maxLen is specified, don't scan more than maxLen bytes to calculate size.
*/
int size( int maxLen = -1 ) const;
/** Wrap this element up as a singleton object. */
BSONObj wrap() const;
/** Wrap this element up as a singleton object with a new name. */
BSONObj wrap( const char* newName) const;
/** field name of the element. e.g., for
name : "Joe"
"name" is the fieldname
*/
const char * fieldName() const {
if ( eoo() ) return ""; // no fieldname for it.
return data + 1;
}
/** raw data of the element's value (so be careful). */
const char * value() const {
return (data + fieldNameSize() + 1);
}
/** size in bytes of the element's value (when applicable). */
int valuesize() const {
return size() - fieldNameSize() - 1;
}
bool isBoolean() const {
return type() == Bool;
}
/** @return value of a boolean element.
You must assure element is a boolean before
calling. */
bool boolean() const {
return *value() ? true : false;
}
/** Retrieve a java style date value from the element.
Ensure element is of type Date before calling.
*/
Date_t date() const {
return *reinterpret_cast< const Date_t* >( value() );
}
/** Convert the value to boolean, regardless of its type, in a javascript-like fashion
(i.e., treat zero and null as false).
*/
bool trueValue() const {
switch( type() ) {
case NumberLong:
return *reinterpret_cast< const long long* >( value() ) != 0;
case NumberDouble:
return *reinterpret_cast< const double* >( value() ) != 0;
case NumberInt:
return *reinterpret_cast< const int* >( value() ) != 0;
case Bool:
return boolean();
case EOO:
case jstNULL:
case Undefined:
return false;
default:
;
}
return true;
}
/** True if element is of a numeric type. */
bool isNumber() const {
switch( type() ) {
case NumberLong:
case NumberDouble:
case NumberInt:
return true;
default:
return false;
}
}
bool isSimpleType() const {
switch( type() ){
case NumberLong:
case NumberDouble:
case NumberInt:
case String:
case Bool:
case Date:
case jstOID:
return true;
default:
return false;
}
}
/** Return double value for this field. MUST be NumberDouble type. */
double _numberDouble() const {return *reinterpret_cast< const double* >( value() ); }
/** Return double value for this field. MUST be NumberInt type. */
int _numberInt() const {return *reinterpret_cast< const int* >( value() ); }
/** Return double value for this field. MUST be NumberLong type. */
long long _numberLong() const {return *reinterpret_cast< const long long* >( value() ); }
/** Retrieve int value for the element safely. Zero returned if not a number. */
int numberInt() const {
switch( type() ) {
case NumberDouble:
return (int) _numberDouble();
case NumberInt:
return _numberInt();
case NumberLong:
return (int) _numberLong();
default:
return 0;
}
}
/** Retrieve long value for the element safely. Zero returned if not a number. */
long long numberLong() const {
switch( type() ) {
case NumberDouble:
return (long long) _numberDouble();
case NumberInt:
return _numberInt();
case NumberLong:
return _numberLong();
default:
return 0;
}
}
/** Retrieve the numeric value of the element. If not of a numeric type, returns 0.
NOTE: casts to double, data loss may occur with large (>52 bit) NumberLong values.
*/
double numberDouble() const {
switch( type() ) {
case NumberDouble:
return _numberDouble();
case NumberInt:
return *reinterpret_cast< const int* >( value() );
case NumberLong:
return (double) *reinterpret_cast< const long long* >( value() );
default:
return 0;
}
}
/** Retrieve the numeric value of the element. If not of a numeric type, returns 0.
NOTE: casts to double, data loss may occur with large (>52 bit) NumberLong values.
*/
double number() const { return numberDouble(); }
/** Retrieve the object ID stored in the object.
You must ensure the element is of type jstOID first. */
const OID &__oid() const {
return *reinterpret_cast< const OID* >( value() );
}
/** True if element is null. */
bool isNull() const {
return type() == jstNULL;
}
/** Size (length) of a string element.
You must assure of type String first. */
int valuestrsize() const {
return *reinterpret_cast< const int* >( value() );
}
// for objects the size *includes* the size of the size field
int objsize() const {
return *reinterpret_cast< const int* >( value() );
}
/** Get a string's value. Also gives you start of the real data for an embedded object.
You must assure data is of an appropriate type first -- see also valuestrsafe().
*/
const char * valuestr() const {
return value() + 4;
}
/** Get the string value of the element. If not a string returns "". */
const char *valuestrsafe() const {
return type() == String ? valuestr() : "";
}
/** Get the string value of the element. If not a string returns "". */
string str() const { return valuestrsafe(); }
/** Get javascript code of a CodeWScope data element. */
const char * codeWScopeCode() const {
return value() + 8;
}
/** Get the scope SavedContext of a CodeWScope data element. */
const char * codeWScopeScopeData() const {
// TODO fix
return codeWScopeCode() + strlen( codeWScopeCode() ) + 1;
}
/** Get the embedded object this element holds. */
BSONObj embeddedObject() const;
/* uasserts if not an object */
BSONObj embeddedObjectUserCheck() const;
BSONObj codeWScopeObject() const;
string ascode() const {
switch( type() ){
case String:
case Code:
return valuestr();
case CodeWScope:
return codeWScopeCode();
default:
log() << "can't convert type: " << (int)(type()) << " to code" << endl;
}
uassert( 10062 , "not code" , 0 );
return "";
}
/** Get binary data. Element must be of type BinData */
const char *binData(int& len) const {
// BinData: <int len> <byte subtype> <byte[len] data>
assert( type() == BinData );
len = valuestrsize();
return value() + 5;
}
BinDataType binDataType() const {
// BinData: <int len> <byte subtype> <byte[len] data>
assert( type() == BinData );
unsigned char c = (value() + 4)[0];
return (BinDataType)c;
}
/** Retrieve the regex string for a Regex element */
const char *regex() const {
assert(type() == RegEx);
return value();
}
/** Retrieve the regex flags (options) for a Regex element */
const char *regexFlags() const {
const char *p = regex();
return p + strlen(p) + 1;
}
/** like operator== but doesn't check the fieldname,
just the value.
*/
bool valuesEqual(const BSONElement& r) const {
switch( type() ) {
case NumberLong:
return _numberLong() == r.numberLong() && r.isNumber();
case NumberDouble:
return _numberDouble() == r.number() && r.isNumber();
case NumberInt:
return _numberInt() == r.numberInt() && r.isNumber();
default:
;
}
bool match= valuesize() == r.valuesize() &&
memcmp(value(),r.value(),valuesize()) == 0;
return match && canonicalType() == r.canonicalType();
}
/** Returns true if elements are equal. */
bool operator==(const BSONElement& r) const {
if ( strcmp(fieldName(), r.fieldName()) != 0 )
return false;
return valuesEqual(r);
}
/** Well ordered comparison.
@return <0: l<r. 0:l==r. >0:l>r
order by type, field name, and field value.
If considerFieldName is true, pay attention to the field name.
*/
int woCompare( const BSONElement &e, bool considerFieldName = true ) const;
const char * rawdata() const {
return data;
}
/** 0 == Equality, just not defined yet */
int getGtLtOp( int def = 0 ) const;
/** Constructs an empty element */
BSONElement();
/** Check that data is internally consistent. */
void validate() const;
/** True if this element may contain subobjects. */
bool mayEncapsulate() const {
switch ( type() ){
case Object:
case Array:
case CodeWScope:
return true;
default:
return false;
}
}
/** True if this element can be a BSONObj */
bool isABSONObj() const {
switch( type() ){
case Object:
case Array:
return true;
default:
return false;
}
}
Date_t timestampTime() const{
unsigned long long t = ((unsigned int*)(value() + 4 ))[0];
return t * 1000;
}
unsigned int timestampInc() const{
return ((unsigned int*)(value() ))[0];
}
const char * dbrefNS() const {
uassert( 10063 , "not a dbref" , type() == DBRef );
return value() + 4;
}
const OID& dbrefOID() const {
uassert( 10064 , "not a dbref" , type() == DBRef );
const char * start = value();
start += 4 + *reinterpret_cast< const int* >( start );
return *reinterpret_cast< const OID* >( start );
}
bool operator<( const BSONElement& other ) const {
int x = (int)canonicalType() - (int)other.canonicalType();
if ( x < 0 ) return true;
else if ( x > 0 ) return false;
return compareElementValues(*this,other) < 0;
}
// If maxLen is specified, don't scan more than maxLen bytes.
BSONElement(const char *d, int maxLen = -1) : data(d) {
fieldNameSize_ = -1;
if ( eoo() )
fieldNameSize_ = 0;
else {
if ( maxLen != -1 ) {
int size = strnlen( fieldName(), maxLen - 1 );
massert( 10333 , "Invalid field name", size != -1 );
fieldNameSize_ = size + 1;
}
}
totalSize = -1;
}
private:
const char *data;
mutable int fieldNameSize_; // cached value
int fieldNameSize() const {
if ( fieldNameSize_ == -1 )
fieldNameSize_ = (int)strlen( fieldName() ) + 1;
return fieldNameSize_;
}
mutable int totalSize; /* caches the computed size */
};
int getGtLtOp(const BSONElement& e);
struct BSONElementCmpWithoutField {
bool operator()( const BSONElement &l, const BSONElement &r ) const {
return l.woCompare( r, false ) < 0;
}
};
typedef set< BSONElement, BSONElementCmpWithoutField > BSONElementSet;
/**
C++ representation of a "BSON" object -- that is, an extended JSON-style
object in a binary representation.
Note that BSONObj's have a smart pointer capability built in -- so you can
pass them around by value. The reference counts used to implement this
do not use locking, so copying and destroying BSONObj's are not thread-safe
operations.
BSON object format:
\code
<unsigned totalSize> {<byte BSONType><cstring FieldName><Data>}* EOO
totalSize includes itself.
Data:
Bool: <byte>
EOO: nothing follows
Undefined: nothing follows
OID: an OID object
NumberDouble: <double>
NumberInt: <int32>
String: <unsigned32 strsizewithnull><cstring>
Date: <8bytes>
Regex: <cstring regex><cstring options>
Object: a nested object, leading with its entire size, which terminates with EOO.
Array: same as object
DBRef: <strlen> <cstring ns> <oid>
DBRef: a database reference: basically a collection name plus an Object ID
BinData: <int len> <byte subtype> <byte[len] data>
Code: a function (not a closure): same format as String.
Symbol: a language symbol (say a python symbol). same format as String.
Code With Scope: <total size><String><Object>
\endcode
*/
class BSONObj {
friend class BSONObjIterator;
class Holder {
public:
Holder( const char *objdata ) :
_objdata( objdata ) {
}
~Holder() {
free((void *)_objdata);
_objdata = 0;
}
private:
const char *_objdata;
};
const char *_objdata;
boost::shared_ptr< Holder > _holder;
void init(const char *data, bool ifree) {
if ( ifree )
_holder.reset( new Holder( data ) );
_objdata = data;
if ( ! isValid() ){
stringstream ss;
ss << "Invalid BSONObj spec size: " << objsize();
try {
BSONElement e = firstElement();
ss << " first element:" << e.toString() << " ";
}
catch ( ... ){}
string s = ss.str();
massert( 10334 , s , 0 );
}
}
#pragma pack(1)
static struct EmptyObject {
EmptyObject() {
len = 5;
jstype = EOO;
}
int len;
char jstype;
} emptyObject;
#pragma pack()
public:
/** Construct a BSONObj from data in the proper format.
@param ifree true if the BSONObj should free() the msgdata when
it destructs.
*/
explicit BSONObj(const char *msgdata, bool ifree = false) {
init(msgdata, ifree);
}
BSONObj(const Record *r);
/** Construct an empty BSONObj -- that is, {}. */
BSONObj() : _objdata( reinterpret_cast< const char * >( &emptyObject ) ) { }
// defensive
~BSONObj() { _objdata = 0; }
void appendSelfToBufBuilder(BufBuilder& b) const {
assert( objsize() );
b.append(reinterpret_cast<const void *>( objdata() ), objsize());
}
/** Readable representation of a BSON object in an extended JSON-style notation.
This is an abbreviated representation which might be used for logging.
*/
string toString() const;
operator string() const { return toString(); }
/** Properly formatted JSON string. */
string jsonString( JsonStringFormat format = Strict ) const;
/** note: addFields always adds _id even if not specified */
int addFields(BSONObj& from, set<string>& fields); /* returns n added */
/** returns # of top level fields in the object
note: iterates to count the fields
*/
int nFields() const;
/** adds the field names to the fields set. does NOT clear it (appends). */
int getFieldNames(set<string>& fields) const;
/** return has eoo() true if no match
supports "." notation to reach into embedded objects
*/
BSONElement getFieldDotted(const char *name) const;
/** Like getFieldDotted(), but expands multikey arrays and returns all matching objects
*/
void getFieldsDotted(const char *name, BSONElementSet &ret, bool *deep = 0) const;
/** Like getFieldDotted(), but returns first array encountered while traversing the
dotted fields of name. The name variable is updated to represent field
names with respect to the returned element. */
BSONElement getFieldDottedOrArray(const char *&name) const;
/** Get the field of the specified name. eoo() is true on the returned
element if not found.
*/
BSONElement getField(const char *name) const;
/** Get the field of the specified name. eoo() is true on the returned
element if not found.
*/
BSONElement getField(const string name) const {
return getField( name.c_str() );
};
/** Get the field of the specified name. eoo() is true on the returned
element if not found.
*/
BSONElement operator[] (const char *field) const {
return getField(field);
}
BSONElement operator[] (const string& field) const {
return getField(field);
}
BSONElement operator[] (int field) const {
stringstream ss;
ss << field;
string s = ss.str();
return getField(s.c_str());
}
/** @return true if field exists */
bool hasField( const char * name )const {
return ! getField( name ).eoo();
}
/** @return "" if DNE or wrong type */
const char * getStringField(const char *name) const;
/** @return subobject of the given name */
BSONObj getObjectField(const char *name) const;
/** @return INT_MIN if not present - does some type conversions */
int getIntField(const char *name) const;
/** @return false if not present */
bool getBoolField(const char *name) const;
/** makes a new BSONObj with the fields specified in pattern.
fields returned in the order they appear in pattern.
if any field is missing or undefined in the object, that field in the
output will be null.
sets output field names to match pattern field names.
If an array is encountered while scanning the dotted names in pattern,
that field is treated as missing.
*/
BSONObj extractFieldsDotted(BSONObj pattern) const;
/**
sets element field names to empty string
If a field in pattern is missing, it is omitted from the returned
object.
*/
BSONObj extractFieldsUnDotted(BSONObj pattern) const;
/** extract items from object which match a pattern object.
e.g., if pattern is { x : 1, y : 1 }, builds an object with
x and y elements of this object, if they are present.
returns elements with original field names
*/
BSONObj extractFields(const BSONObj &pattern , bool fillWithNull=false) const;
BSONObj filterFieldsUndotted(const BSONObj &filter, bool inFilter) const;
BSONElement getFieldUsingIndexNames(const char *fieldName, const BSONObj &indexKey) const;
/** @return the raw data of the object */
const char *objdata() const {
return _objdata;
}
/** @return total size of the BSON object in bytes */
int objsize() const {
return *(reinterpret_cast<const int*>(objdata()));
}
bool isValid();
/** @return if the user is a valid user doc
criter: isValid() no . or $ field names
*/
bool okForStorage() const;
/** @return true if object is empty -- i.e., {} */
bool isEmpty() const {
return objsize() <= 5;
}
void dump() const {
out() << hex;
const char *p = objdata();
for ( int i = 0; i < objsize(); i++ ) {
out() << i << '\t' << ( 0xff & ( (unsigned) *p ) );
if ( *p >= 'A' && *p <= 'z' )
out() << '\t' << *p;
out() << endl;
p++;
}
}
// Alternative output format
string hexDump() const;
/**wo='well ordered'. fields must be in same order in each object.
Ordering is with respect to the signs of the elements in idxKey.
@return <0 if l<r. 0 if l==r. >0 if l>r
*/
int woCompare(const BSONObj& r, const BSONObj &idxKey = BSONObj(),
bool considerFieldName=true) const;
int woSortOrder( const BSONObj& r , const BSONObj& sortKey ) const;
/** This is "shallow equality" -- ints and doubles won't match. for a
deep equality test use woCompare (which is slower).
*/
bool woEqual(const BSONObj& r) const {
int os = objsize();
if ( os == r.objsize() ) {
return (os == 0 || memcmp(objdata(),r.objdata(),os)==0);
}
return false;
}
/** @return first field of the object */
BSONElement firstElement() const {
return BSONElement(objdata() + 4);
}
/** use getField() instead. */
//BSONElement getField(const char *name) const;
//BSONElement getField(string name) const {
/** @return true if field exists in the object */
bool hasElement(const char *name) const;
/** Get the _id field from the object. For good performance drivers should
assure that _id is the first element of the object; however, correct operation
is assured regardless.
@return true if found
*/
bool getObjectID(BSONElement& e) const;
/** makes a copy of the object.
*/
BSONObj copy() const;
/* make sure the data buffer is under the control of BSONObj's and not a remote buffer */
BSONObj getOwned() const{
if ( !isOwned() )
return copy();
return *this;
}
bool isOwned() const { return _holder.get() != 0; }
/** @return A hash code for the object */
int hash() const {
unsigned x = 0;
const char *p = objdata();
for ( int i = 0; i < objsize(); i++ )
x = x * 131 + p[i];
return (x & 0x7fffffff) | 0x8000000; // must be > 0
}
// Return a version of this object where top level elements of types
// that are not part of the bson wire protocol are replaced with
// string identifier equivalents.
// TODO Support conversion of element types other than min and max.
BSONObj clientReadable() const;
/** Return new object with the field names replaced by those in the
passed object. */
BSONObj replaceFieldNames( const BSONObj &obj ) const;
/** true unless corrupt */
bool valid() const;
string md5() const;
bool operator==( const BSONObj& other ){
return woCompare( other ) == 0;
}
enum MatchType {
Equality = 0,
LT = 0x1,
LTE = 0x3,
GTE = 0x6,
GT = 0x4,
opIN = 0x8, // { x : { $in : [1,2,3] } }
NE = 0x9,
opSIZE = 0x0A,
opALL = 0x0B,
NIN = 0x0C,
opEXISTS = 0x0D,
opMOD = 0x0E,
opTYPE = 0x0F,
opREGEX = 0x10,
opOPTIONS = 0x11,
opELEM_MATCH = 0x12,
opNEAR = 0x13
};
};
ostream& operator<<( ostream &s, const BSONObj &o );
ostream& operator<<( ostream &s, const BSONElement &e );
struct BSONArray: BSONObj {
// Don't add anything other than forwarding constructors!!!
BSONArray(): BSONObj() {}
explicit BSONArray(const BSONObj& obj): BSONObj(obj) {}
};
class BSONObjCmp {
public:
BSONObjCmp( const BSONObj &_order = BSONObj() ) : order( _order ) {}
bool operator()( const BSONObj &l, const BSONObj &r ) const {
return l.woCompare( r, order ) < 0;
}
private:
BSONObj order;
};
class BSONObjCmpDefaultOrder : public BSONObjCmp {
public:
BSONObjCmpDefaultOrder() : BSONObjCmp( BSONObj() ) {}
};
typedef set< BSONObj, BSONObjCmpDefaultOrder > BSONObjSetDefaultOrder;
enum FieldCompareResult {
LEFT_SUBFIELD = -2,
LEFT_BEFORE = -1,
SAME = 0,
RIGHT_BEFORE = 1 ,
RIGHT_SUBFIELD = 2
};
FieldCompareResult compareDottedFieldNames( const string& l , const string& r );
/** Use BSON macro to build a BSONObj from a stream
e.g.,
BSON( "name" << "joe" << "age" << 33 )
with auto-generated object id:
BSON( GENOID << "name" << "joe" << "age" << 33 )
The labels GT, GTE, LT, LTE, NE can be helpful for stream-oriented construction
of a BSONObj, particularly when assembling a Query. For example,
BSON( "a" << GT << 23.4 << NE << 30 << "b" << 2 ) produces the object
{ a: { \$gt: 23.4, \$ne: 30 }, b: 2 }.
*/
#define BSON(x) (( mongo::BSONObjBuilder(64) << x ).obj())
/** Use BSON_ARRAY macro like BSON macro, but without keys
BSONArray arr = BSON_ARRAY( "hello" << 1 << BSON( "foo" << BSON_ARRAY( "bar" << "baz" << "qux" ) ) );
*/
#define BSON_ARRAY(x) (( mongo::BSONArrayBuilder() << x ).arr())
/* Utility class to auto assign object IDs.
Example:
cout << BSON( GENOID << "z" << 3 ); // { _id : ..., z : 3 }
*/
extern struct IDLabeler { } GENOID;
/* Utility class to add a Date element with the current time
Example:
cout << BSON( "created" << DATENOW ); // { created : "2009-10-09 11:41:42" }
*/
extern struct DateNowLabeler { } DATENOW;
// Utility class to implement GT, GTE, etc as described above.
class Labeler {
public:
struct Label {
Label( const char *l ) : l_( l ) {}
const char *l_;
};
Labeler( const Label &l, BSONObjBuilderValueStream *s ) : l_( l ), s_( s ) {}
template<class T>
BSONObjBuilder& operator<<( T value );
/* the value of the element e is appended i.e. for
"age" << GT << someElement
one gets
{ age : { $gt : someElement's value } }
*/
BSONObjBuilder& operator<<( const BSONElement& e );
private:
const Label &l_;
BSONObjBuilderValueStream *s_;
};
extern Labeler::Label GT;
extern Labeler::Label GTE;
extern Labeler::Label LT;
extern Labeler::Label LTE;
extern Labeler::Label NE;
extern Labeler::Label SIZE;
// Utility class to implement BSON( key << val ) as described above.
class BSONObjBuilderValueStream : public boost::noncopyable {
public:
friend class Labeler;
BSONObjBuilderValueStream( BSONObjBuilder * builder );
BSONObjBuilder& operator<<( const BSONElement& e );
template<class T>
BSONObjBuilder& operator<<( T value );
BSONObjBuilder& operator<<(DateNowLabeler& id);
Labeler operator<<( const Labeler::Label &l );
void endField( const char *nextFieldName = 0 );
bool subobjStarted() const { return _fieldName != 0; }
private:
const char * _fieldName;
BSONObjBuilder * _builder;
bool haveSubobj() const { return _subobj.get() != 0; }
BSONObjBuilder *subobj();
auto_ptr< BSONObjBuilder > _subobj;
};
/**
used in conjuction with BSONObjBuilder, allows for proper buffer size to prevent crazy memory usage
*/
class BSONSizeTracker {
public:
#define BSONSizeTrackerSize 10
BSONSizeTracker(){
_pos = 0;
for ( int i=0; i<BSONSizeTrackerSize; i++ )
_sizes[i] = 512; // this is the default, so just be consistent
}
~BSONSizeTracker(){
}
void got( int size ){
_sizes[_pos++] = size;
if ( _pos >= BSONSizeTrackerSize )
_pos = 0;
}
/**
* right now choosing largest size
*/
int getSize() const {
int x = 16; // sane min
for ( int i=0; i<BSONSizeTrackerSize; i++ ){
if ( _sizes[i] > x )
x = _sizes[i];
}
return x;
}
private:
int _pos;
int _sizes[BSONSizeTrackerSize];
};
/**
utility for creating a BSONObj
*/
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), offset_( 0 ), s_( this ) , _tracker(0) {
b.skip(4); /*leave room for size field*/
}
/** @param baseBuilder construct a BSONObjBuilder using an existing BufBuilder */
BSONObjBuilder( BufBuilder &baseBuilder ) : b( baseBuilder ), buf_( 0 ), offset_( baseBuilder.len() ), s_( this ) , _tracker(0) {
b.skip( 4 );
}
BSONObjBuilder( const BSONSizeTracker & tracker ) : b(buf_) , buf_(tracker.getSize() ), offset_(0), s_( this ) , _tracker( (BSONSizeTracker*)(&tracker) ){
b.skip( 4 );
}
/** add all the fields from the object specified to this object */
BSONObjBuilder& appendElements(BSONObj x);
/** append element to the object we are building */
void append( const BSONElement& e) {
assert( !e.eoo() ); // do not append eoo, that would corrupt us. the builder auto appends when done() is called.
b.append((void*) e.rawdata(), e.size());
}
/** append an element but with a new name */
void appendAs(const BSONElement& e, const char *as) {
assert( !e.eoo() ); // do not append eoo, that would corrupt us. the builder auto appends when done() is called.
b.append((char) e.type());
b.append(as);
b.append((void *) e.value(), e.valuesize());
}
void appendAs(const BSONElement& e, const string& as) {
appendAs( e , as.c_str() );
}
/** add a subobject as a member */
void append(const char *fieldName, BSONObj subObj) {
b.append((char) Object);
b.append(fieldName);
b.append((void *) subObj.objdata(), subObj.objsize());
}
void append(const string& fieldName , BSONObj subObj) {
append( fieldName.c_str() , subObj );
}
/** add header for a new subobject and return bufbuilder for writing to
the subobject's body */
BufBuilder &subobjStart(const char *fieldName) {
b.append((char) Object);
b.append(fieldName);
return b;
}
/** add a subobject as a member with type Array. Thus arr object should have "0", "1", ...
style fields in it.
*/
void appendArray(const char *fieldName, BSONObj subObj) {
b.append((char) Array);
b.append(fieldName);
b.append((void *) subObj.objdata(), subObj.objsize());
}
void append(const char *fieldName, BSONArray arr) { appendArray(fieldName, arr); }
/** add header for a new subarray and return bufbuilder for writing to
the subarray's body */
BufBuilder &subarrayStart(const char *fieldName) {
b.append((char) Array);
b.append(fieldName);
return b;
}
/** Append a boolean element */
void appendBool(const char *fieldName, int val) {
b.append((char) Bool);
b.append(fieldName);
b.append((char) (val?1:0));
}
/** Append a boolean element */
void append(const char *fieldName, bool val) {
b.append((char) Bool);
b.append(fieldName);
b.append((char) (val?1:0));
}
/** Append a 32 bit integer element */
void append(const char *fieldName, int n) {
b.append((char) NumberInt);
b.append(fieldName);
b.append(n);
}
/** Append a 32 bit integer element */
void append(const string &fieldName, int n) {
append( fieldName.c_str(), n );
}
/** Append a 32 bit unsigned element - cast to a signed int. */
void append(const char *fieldName, unsigned n) { append(fieldName, (int) n); }
/** Append a NumberLong */
void append(const char *fieldName, long long n) {
b.append((char) NumberLong);
b.append(fieldName);
b.append(n);
}
/** Append a NumberLong */
void append(const string& fieldName, long long n) {
append( fieldName.c_str() , n );
}
/** appends a number. if n < max(int)/2 then uses int, otherwise long long */
void appendIntOrLL( const string& fieldName , long long n ){
long long x = n;
if ( x < 0 )
x = x * -1;
if ( x < ( numeric_limits<int>::max() / 2 ) )
append( fieldName.c_str() , (int)n );
else
append( fieldName.c_str() , n );
}
/**
* appendNumber is a series of method for appending the smallest sensible type
* mostly for JS
*/
void appendNumber( const string& fieldName , int n ){
append( fieldName.c_str() , n );
}
void appendNumber( const string& fieldName , double d ){
append( fieldName.c_str() , d );
}
void appendNumber( const string& 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 );
if ( l < maxInt )
append( fieldName.c_str() , (int)l );
else if ( l < maxDouble )
append( fieldName.c_str() , (double)l );
else
append( fieldName.c_str() , l );
}
/** Append a double element */
BSONObjBuilder& append(const char *fieldName, double n) {
b.append((char) NumberDouble);
b.append(fieldName);
b.append(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 string& fieldName , const string& data );
/** Append a BSON Object ID (OID type). */
void appendOID(const char *fieldName, OID *oid = 0 , bool generateIfBlank = false ) {
b.append((char) jstOID);
b.append(fieldName);
if ( oid )
b.append( (void *) oid, 12 );
else {
OID tmp;
if ( generateIfBlank )
tmp.init();
else
tmp.clear();
b.append( (void *) &tmp, 12 );
}
}
void append( const char *fieldName, OID oid ) {
appendOID( fieldName, &oid );
}
/** 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
*/
void appendTimeT(const char *fieldName, time_t dt) {
b.append((char) Date);
b.append(fieldName);
b.append(static_cast<unsigned long long>(dt) * 1000);
}
/** 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
*/
void appendDate(const char *fieldName, Date_t dt) {
b.append((char) Date);
b.append(fieldName);
b.append(dt);
}
void append(const char *fieldName, Date_t dt) {
appendDate(fieldName, dt);
}
/** Append a regular expression value
@param regex the regular expression pattern
@param regex options such as "i" or "g"
*/
void appendRegex(const char *fieldName, const char *regex, const char *options = "") {
b.append((char) RegEx);
b.append(fieldName);
b.append(regex);
b.append(options);
}
/** Append a regular expression value
@param regex the regular expression pattern
@param regex options such as "i" or "g"
*/
void appendRegex(string fieldName, string regex, string options = "") {
appendRegex(fieldName.c_str(), regex.c_str(), options.c_str());
}
void appendCode(const char *fieldName, const char *code) {
b.append((char) Code);
b.append(fieldName);
b.append((int) strlen(code)+1);
b.append(code);
}
/** Append a string element */
BSONObjBuilder& append(const char *fieldName, const char *str) {
b.append((char) String);
b.append(fieldName);
b.append((int) strlen(str)+1);
b.append(str);
return *this;
}
/** Append a string element */
void append(const char *fieldName, string str) {
append(fieldName, str.c_str());
}
void appendSymbol(const char *fieldName, const char *symbol) {
b.append((char) Symbol);
b.append(fieldName);
b.append((int) strlen(symbol)+1);
b.append(symbol);
}
/** Append a Null element to the object */
void appendNull( const char *fieldName ) {
b.append( (char) jstNULL );
b.append( fieldName );
}
// Append an element that is less than all other keys.
void appendMinKey( const char *fieldName ) {
b.append( (char) MinKey );
b.append( fieldName );
}
// Append an element that is greater than all other keys.
void appendMaxKey( const char *fieldName ) {
b.append( (char) MaxKey );
b.append( fieldName );
}
// Append a Timestamp field -- will be updated to next OpTime on db insert.
void appendTimestamp( const char *fieldName ) {
b.append( (char) Timestamp );
b.append( fieldName );
b.append( (unsigned long long) 0 );
}
void appendTimestamp( const char *fieldName , unsigned long long val ) {
b.append( (char) Timestamp );
b.append( fieldName );
b.append( val );
}
/**
* @param time - in millis (but stored in seconds)
*/
void appendTimestamp( const char *fieldName , unsigned long long time , unsigned int inc ){
OpTime t( (unsigned) (time / 1000) , inc );
appendTimestamp( fieldName , t.asDate() );
}
/* Deprecated (but supported) */
void appendDBRef( const char *fieldName, const char *ns, const OID &oid ) {
b.append( (char) DBRef );
b.append( fieldName );
b.append( (int) strlen( ns ) + 1 );
b.append( ns );
b.append( (void *) &oid, 12 );
}
/** Append a binary data element
@param fieldName name of the field
@param len length of the binary data in bytes
@param type type information for the data. @see BinDataType. Use ByteArray if you
don't care about the type.
@param data the byte array
*/
void appendBinData( const char *fieldName, int len, BinDataType type, const char *data ) {
b.append( (char) BinData );
b.append( fieldName );
b.append( len );
b.append( (char) type );
b.append( (void *) data, len );
}
void appendBinData( const char *fieldName, int len, BinDataType type, const unsigned char *data ) {
appendBinData(fieldName, len, type, (const char *) data);
}
/**
@param len the length of data
*/
void appendBinDataArray( const char * fieldName , const char * data , int len ){
b.append( (char) BinData );
b.append( fieldName );
b.append( len + 4 );
b.append( (char)0x2 );
b.append( len );
b.append( (void *) data, len );
}
/** Append to the BSON object a field of type CodeWScope. This is a javascript code
fragment accompanied by some scope that goes with it.
*/
void appendCodeWScope( const char *fieldName, const char *code, const BSONObj &scope ) {
b.append( (char) CodeWScope );
b.append( fieldName );
b.append( ( int )( 4 + 4 + strlen( code ) + 1 + scope.objsize() ) );
b.append( ( int ) strlen( code ) + 1 );
b.append( code );
b.append( ( void * )scope.objdata(), scope.objsize() );
}
void appendUndefined( const char *fieldName ) {
b.append( (char) Undefined );
b.append( fieldName );
}
/* helper function -- see Query::where() for primary way to do this. */
void appendWhere( const char *code, const BSONObj &scope ){
appendCodeWScope( "$where" , code , scope );
}
void appendWhere( const string &code, const BSONObj &scope ){
appendWhere( code.c_str(), scope );
}
/**
these are the min/max when comparing, not strict min/max elements for a given type
*/
void appendMinForType( const string& field , int type );
void appendMaxForType( const string& field , int type );
/** Append an array of values. */
template < class T >
void append( const char *fieldName, const vector< T >& vals ) {
BSONObjBuilder arrBuilder;
for ( unsigned int i = 0; i < vals.size(); ++i )
arrBuilder.append( numStr( i ).c_str(), vals[ i ] );
marshalArray( fieldName, arrBuilder.done() );
}
/* Append an array of ints
void appendArray( const char *fieldName, const vector< int >& vals ) {
BSONObjBuilder arrBuilder;
for ( unsigned i = 0; i < vals.size(); ++i )
arrBuilder.append( numStr( i ).c_str(), vals[ i ] );
marshalArray( fieldName, arrBuilder.done() );
}*/
/** The returned BSONObj will free the buffer when it is finished. */
BSONObj obj() {
massert( 10335 , "builder does not own memory", owned() );
int l;
return BSONObj(decouple(l), true);
}
/** 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());
}
/** 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
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 );
private:
static const string numStrs[100]; // cache of 0 to 99 inclusive
public:
static string numStr( int i ) {
if (i>=0 && i<100)
return numStrs[i];
stringstream 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<<( IDLabeler ) {
OID oid;
oid.init();
appendOID("_id", &oid);
return *this;
}
// 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;
}
bool owned() const {
return &b == &buf_;
}
private:
// Append the provided arr object as an array.
void marshalArray( const char *fieldName, const BSONObj &arr ) {
b.append( (char) Array );
b.append( fieldName );
b.append( (void *) arr.objdata(), arr.objsize() );
}
char* _done() {
s_.endField();
b.append((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;
};
class BSONArrayBuilder : boost::noncopyable{
public:
BSONArrayBuilder() : _i(0), _b() {}
BSONArrayBuilder( BufBuilder &b ) : _i(0), _b(b) {}
template <typename T>
BSONArrayBuilder& append(const T& x){
_b.append(num().c_str(), x);
return *this;
}
BSONArrayBuilder& append(const BSONElement& e){
_b.appendAs(e, num().c_str());
return *this;
}
template <typename T>
BSONArrayBuilder& operator<<(const T& x){
return append(x);
}
void appendNull() {
_b.appendNull(num().c_str());
}
BSONArray arr(){ return BSONArray(_b.obj()); }
BSONObj done() { return _b.done(); }
template <typename T>
BSONArrayBuilder& append(const char *name, const T& x){
fill( name );
append( x );
return *this;
}
BufBuilder &subobjStart( const char *name ) {
fill( name );
return _b.subobjStart( num().c_str() );
}
BufBuilder &subarrayStart( const char *name ) {
fill( name );
return _b.subarrayStart( num().c_str() );
}
void appendArray( const char *name, BSONObj subObj ) {
fill( name );
_b.appendArray( num().c_str(), subObj );
}
void appendAs( const BSONElement &e, const char *name ) {
fill( name );
append( e );
}
private:
void fill( const char *name ) {
char *r;
int n = strtol( name, &r, 10 );
uassert( 13048, "can't append to array using string field name", !*r );
while( _i < n )
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;
};
/** iterator for a BSONObj
Note each BSONObj ends with an EOO element: so you will get more() on an empty
object, although next().eoo() will be true.
todo: we may want to make a more stl-like iterator interface for this
with things like begin() and end()
*/
class BSONObjIterator {
public:
/** Create an iterator for a BSON object.
*/
BSONObjIterator(const BSONObj& jso) {
int sz = jso.objsize();
if ( sz == 0 ) {
pos = theend = 0;
return;
}
pos = jso.objdata() + 4;
theend = jso.objdata() + sz;
}
/** @return true if more elements exist to be enumerated. */
bool moreWithEOO() {
return pos < theend;
}
bool more(){
return pos < theend && pos[0];
}
/** @return the next element in the object. For the final element, element.eoo() will be true. */
BSONElement next( bool checkEnd = false ) {
assert( pos < theend );
BSONElement e( pos, checkEnd ? (int)(theend - pos) : -1 );
pos += e.size( checkEnd ? (int)(theend - pos) : -1 );
return e;
}
private:
const char *pos;
const char *theend;
};
/* iterator a BSONObj which is an array, in array order.
class JSArrayIter {
public:
BSONObjIterator(const BSONObj& jso) {
...
}
bool more() { return ... }
BSONElement next() {
...
}
};
*/
extern BSONObj maxKey;
extern BSONObj minKey;
// a BoundList contains intervals specified by inclusive start
// and end bounds. The intervals should be nonoverlapping and occur in
// the specified direction of traversal. For example, given a simple index {i:1}
// and direction +1, one valid BoundList is: (1, 2); (4, 6). The same BoundList
// would be valid for index {i:-1} with direction -1.
typedef vector< pair< BSONObj, BSONObj > > BoundList;
/*- just for testing -- */
#pragma pack(1)
struct JSObj1 {
JSObj1() {
totsize=sizeof(JSObj1);
n = NumberDouble;
strcpy_s(nname, 5, "abcd");
N = 3.1;
s = String;
strcpy_s(sname, 7, "abcdef");
slen = 10;
strcpy_s(sval, 10, "123456789");
eoo = EOO;
}
unsigned totsize;
char n;
char nname[5];
double N;
char s;
char sname[7];
unsigned slen;
char sval[10];
char eoo;
};
#pragma pack()
extern JSObj1 js1;
#ifdef _DEBUG
#define CHECK_OBJECT( o , msg ) massert( 10337 , (string)"object not valid" + (msg) , (o).isValid() )
#else
#define CHECK_OBJECT( o , msg )
#endif
inline BSONObj BSONElement::embeddedObjectUserCheck() const {
uassert( 10065 , "invalid parameter: expected an object", isABSONObj() );
return BSONObj(value());
}
inline BSONObj BSONElement::embeddedObject() const {
assert( isABSONObj() );
return BSONObj(value());
}
inline BSONObj BSONElement::codeWScopeObject() const {
assert( type() == CodeWScope );
int strSizeWNull = *(int *)( value() + 4 );
return BSONObj( value() + 4 + 4 + strSizeWNull );
}
inline BSONObj BSONObj::copy() const {
char *p = (char*) malloc(objsize());
memcpy(p, objdata(), objsize());
return BSONObj(p, true);
}
// wrap this element up as a singleton object.
inline BSONObj BSONElement::wrap() const {
BSONObjBuilder b(size()+6);
b.append(*this);
return b.obj();
}
inline BSONObj BSONElement::wrap( const char * newName ) const {
BSONObjBuilder b(size()+6+strlen(newName));
b.appendAs(*this,newName);
return b.obj();
}
inline bool BSONObj::hasElement(const char *name) const {
if ( !isEmpty() ) {
BSONObjIterator it(*this);
while ( it.moreWithEOO() ) {
BSONElement e = it.next();
if ( strcmp(name, e.fieldName()) == 0 )
return true;
}
}
return false;
}
inline BSONElement BSONObj::getField(const char *name) const {
BSONObjIterator i(*this);
while ( i.more() ) {
BSONElement e = i.next();
if ( strcmp(e.fieldName(), name) == 0 )
return e;
}
return BSONElement();
}
/* add all the fields from the object specified to this object */
inline BSONObjBuilder& BSONObjBuilder::appendElements(BSONObj x) {
BSONObjIterator it(x);
while ( it.moreWithEOO() ) {
BSONElement e = it.next();
if ( e.eoo() ) break;
append(e);
}
return *this;
}
inline bool BSONObj::isValid(){
return objsize() > 0 && objsize() <= 1024 * 1024 * 8;
}
inline bool BSONObj::getObjectID(BSONElement& e) const {
BSONElement f = getField("_id");
if( !f.eoo() ) {
e = f;
return true;
}
return false;
}
inline BSONObjBuilderValueStream::BSONObjBuilderValueStream( BSONObjBuilder * builder ) {
_fieldName = 0;
_builder = builder;
}
template<class T>
inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<( T value ) {
_builder->append(_fieldName, value);
_fieldName = 0;
return *_builder;
}
inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const BSONElement& e ) {
_builder->appendAs( e , _fieldName );
_fieldName = 0;
return *_builder;
}
inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(DateNowLabeler& id){
_builder->appendDate(_fieldName, jsTime());
_fieldName = 0;
return *_builder;
}
inline Labeler BSONObjBuilderValueStream::operator<<( const Labeler::Label &l ) {
return Labeler( l, this );
}
inline void BSONObjBuilderValueStream::endField( const char *nextFieldName ) {
if ( _fieldName && haveSubobj() ) {
_builder->append( _fieldName, subobj()->done() );
}
_subobj.reset();
_fieldName = nextFieldName;
}
inline BSONObjBuilder *BSONObjBuilderValueStream::subobj() {
if ( !haveSubobj() )
_subobj.reset( new BSONObjBuilder() );
return _subobj.get();
}
template<class T> inline
BSONObjBuilder& Labeler::operator<<( T value ) {
s_->subobj()->append( l_.l_, value );
return *s_->_builder;
}
inline
BSONObjBuilder& Labeler::operator<<( const BSONElement& e ) {
s_->subobj()->appendAs( e, l_.l_ );
return *s_->_builder;
}
// {a: {b:1}} -> {a.b:1}
void nested2dotted(BSONObjBuilder& b, const BSONObj& obj, const string& base="");
inline BSONObj nested2dotted(const BSONObj& obj){
BSONObjBuilder b;
nested2dotted(b, obj);
return b.obj();
}
// {a.b:1} -> {a: {b:1}}
void dotted2nested(BSONObjBuilder& b, const BSONObj& obj);
inline BSONObj dotted2nested(const BSONObj& obj){
BSONObjBuilder b;
dotted2nested(b, obj);
return b.obj();
}
/* WARNING: nested/dotted conversions are not 100% reversible
* nested2dotted(dotted2nested({a.b: {c:1}})) -> {a.b.c: 1}
* also, dotted2nested ignores order
*/
typedef map<string, BSONElement> BSONMap;
inline BSONMap bson2map(const BSONObj& obj){
BSONMap m;
BSONObjIterator it(obj);
while (it.more()){
BSONElement e = it.next();
m[e.fieldName()] = e;
}
return m;
}
struct BSONElementFieldNameCmp {
bool operator()( const BSONElement &l, const BSONElement &r ) const {
return strcmp( l.fieldName() , r.fieldName() ) <= 0;
}
};
typedef set<BSONElement, BSONElementFieldNameCmp> BSONSortedElements;
inline BSONSortedElements bson2set( const BSONObj& obj ){
BSONSortedElements s;
BSONObjIterator it(obj);
while ( it.more() )
s.insert( it.next() );
return s;
}
class BSONObjIteratorSorted {
public:
BSONObjIteratorSorted( const BSONObj& o );
~BSONObjIteratorSorted(){
assert( _fields );
delete _fields;
_fields = 0;
}
bool more(){
return _cur < _nfields;
}
BSONElement next(){
assert( _fields );
if ( _cur < _nfields )
return BSONElement( _fields[_cur++] );
return BSONElement();
}
private:
const char ** _fields;
int _nfields;
int _cur;
};
} // namespace mongo