mirror of
https://github.com/mongodb/mongo.git
synced 2024-12-01 01:21:03 +01:00
582 lines
20 KiB
C++
582 lines
20 KiB
C++
// BSONElement
|
|
|
|
/* 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 <vector>
|
|
#include <string.h>
|
|
#include "util/builder.h"
|
|
#include "bsontypes.h"
|
|
|
|
namespace mongo {
|
|
class OpTime;
|
|
class BSONObj;
|
|
class BSONElement;
|
|
class BSONObjBuilder;
|
|
}
|
|
|
|
namespace bson {
|
|
typedef mongo::BSONElement be;
|
|
typedef mongo::BSONObj bo;
|
|
typedef mongo::BSONObjBuilder bob;
|
|
}
|
|
|
|
namespace mongo {
|
|
|
|
/* l and r MUST have same type when called: check that first. */
|
|
int compareElementValues(const BSONElement& l, const BSONElement& r);
|
|
|
|
|
|
/** 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.
|
|
|
|
internals:
|
|
<type><fieldName ><value>
|
|
-------- size() ------------
|
|
-fieldNameSize-
|
|
value()
|
|
type()
|
|
*/
|
|
class BSONElement {
|
|
public:
|
|
/** These functions, which start with a capital letter, throw a UserException if the
|
|
element is not of the required type. Example:
|
|
|
|
string foo = obj["foo"].String(); // exception if not a string type or DNE
|
|
*/
|
|
string String() const { return chk(mongo::String).valuestr(); }
|
|
Date_t Date() const { return chk(mongo::Date).date(); }
|
|
double Number() const { return chk(isNumber()).number(); }
|
|
double Double() const { return chk(NumberDouble)._numberDouble(); }
|
|
long long Long() const { return chk(NumberLong)._numberLong(); }
|
|
int Int() const { return chk(NumberInt)._numberInt(); }
|
|
bool Bool() const { return chk(mongo::Bool).boolean(); }
|
|
vector<BSONElement> Array() const; // see implementation for detailed comments
|
|
mongo::OID OID() const { return chk(jstOID).__oid(); }
|
|
void Null() const { chk(isNull()); } // throw UserException if not null
|
|
void OK() const { chk(ok()); } // throw UserException if element DNE
|
|
|
|
/** @return the embedded object associated with this field.
|
|
Note the returned object is a reference to within the parent bson object. If that
|
|
object is out of scope, this pointer will no longer be valid. Call getOwned() on the
|
|
returned BSONObj if you need your own copy.
|
|
throws UserException if the element is not of type object.
|
|
*/
|
|
BSONObj Obj() const;
|
|
|
|
/** populate v with the value of the element. If type does not match, throw exception.
|
|
useful in templates -- see also BSONObj::Vals().
|
|
*/
|
|
void Val(Date_t& v) const { v = Date(); }
|
|
void Val(long long& v) const { v = Long(); }
|
|
void Val(bool& v) const { v = Bool(); }
|
|
void Val(BSONObj& v) const;
|
|
void Val(mongo::OID& v) const { v = OID(); }
|
|
void Val(int& v) const { v = Int(); }
|
|
void Val(double& v) const { v = Double(); }
|
|
void Val(string& v) const { v = String(); }
|
|
|
|
/** Use ok() to check if a value is assigned:
|
|
if( myObj["foo"].ok() ) ...
|
|
*/
|
|
bool ok() const { return !eoo(); }
|
|
|
|
string toString( bool includeFieldName = true, bool full=false) const;
|
|
void toString(StringBuilder& s, bool includeFieldName = true, bool full=false) const;
|
|
string jsonString( JsonStringFormat format, bool includeFieldNames = true, int pretty = 0 ) const;
|
|
operator string() const { return toString(); }
|
|
|
|
/** Returns the type of the element */
|
|
BSONType type() const { return (BSONType) *data; }
|
|
|
|
/** retrieve a field within this element
|
|
throws exception if *this is not an embedded object
|
|
*/
|
|
BSONElement operator[] (const string& field) const;
|
|
|
|
/** 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;
|
|
|
|
/** 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 ) const;
|
|
int size() 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() == mongo::Bool; }
|
|
|
|
/** @return value of a boolean element.
|
|
You must assure element is a boolean before
|
|
calling. */
|
|
bool boolean() const {
|
|
return *value() ? true : false;
|
|
}
|
|
|
|
bool booleanSafe() const { return isBoolean() && boolean(); }
|
|
|
|
/** Retrieve a java style date value from the element.
|
|
Ensure element is of type Date before calling.
|
|
@see Bool(), trueValue()
|
|
*/
|
|
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., treats zero and null and eoo as false).
|
|
*/
|
|
bool trueValue() const;
|
|
|
|
/** True if number, string, bool, date, OID */
|
|
bool isSimpleType() const;
|
|
|
|
/** True if element is of a numeric type. */
|
|
bool isNumber() const;
|
|
|
|
/** 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;
|
|
/** Retrieve long value for the element safely. Zero returned if not a number. */
|
|
long long numberLong() const;
|
|
/** 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;
|
|
/** 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 mongo::OID &__oid() const { return *reinterpret_cast< const mongo::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.
|
|
@return string size including terminating null
|
|
*/
|
|
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() == mongo::String ? valuestr() : "";
|
|
}
|
|
/** Get the string value of the element. If not a string returns "". */
|
|
string str() const {
|
|
return type() == mongo::String ? string(valuestr(), valuestrsize()-1) : string();
|
|
}
|
|
|
|
/** 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;
|
|
|
|
/** Get raw binary data. Element must be of type BinData. Doesn't handle type 2 specially */
|
|
const char *binData(int& len) const {
|
|
// BinData: <int len> <byte subtype> <byte[len] data>
|
|
assert( type() == BinData );
|
|
len = valuestrsize();
|
|
return value() + 5;
|
|
}
|
|
/** Get binary data. Element must be of type BinData. Handles type 2 */
|
|
const char *binDataClean(int& len) const {
|
|
// BinData: <int len> <byte subtype> <byte[len] data>
|
|
if (binDataType() != ByteArrayDeprecated) {
|
|
return binData(len);
|
|
}
|
|
else {
|
|
// Skip extra size
|
|
len = valuestrsize() - 4;
|
|
return value() + 5 + 4;
|
|
}
|
|
}
|
|
|
|
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 {
|
|
return woCompare( r , false ) == 0;
|
|
}
|
|
|
|
/** Returns true if elements are equal. */
|
|
bool operator==(const BSONElement& r) const {
|
|
return woCompare( r , true ) == 0;
|
|
}
|
|
|
|
/** 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 mongo::Array:
|
|
case CodeWScope:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/** True if this element can be a BSONObj */
|
|
bool isABSONObj() const {
|
|
switch( type() ) {
|
|
case Object:
|
|
case mongo::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 mongo::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 mongo::OID* >( start );
|
|
}
|
|
|
|
/** this does not use fieldName in the comparison, just the value */
|
|
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;
|
|
}
|
|
|
|
// @param maxLen don't scan more than maxLen bytes
|
|
explicit BSONElement(const char *d, int maxLen) : data(d) {
|
|
if ( eoo() ) {
|
|
totalSize = 1;
|
|
fieldNameSize_ = 0;
|
|
}
|
|
else {
|
|
totalSize = -1;
|
|
fieldNameSize_ = -1;
|
|
if ( maxLen != -1 ) {
|
|
int size = (int) strnlen( fieldName(), maxLen - 1 );
|
|
massert( 10333 , "Invalid field name", size != -1 );
|
|
fieldNameSize_ = size + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
explicit BSONElement(const char *d) : data(d) {
|
|
fieldNameSize_ = -1;
|
|
totalSize = -1;
|
|
if ( eoo() ) {
|
|
fieldNameSize_ = 0;
|
|
totalSize = 1;
|
|
}
|
|
}
|
|
|
|
string _asCode() const;
|
|
OpTime _opTime() const;
|
|
|
|
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 */
|
|
|
|
friend class BSONObjIterator;
|
|
friend class BSONObj;
|
|
const BSONElement& chk(int t) const {
|
|
if ( t != type() ) {
|
|
StringBuilder ss;
|
|
if( eoo() )
|
|
ss << "field not found, expected type " << t;
|
|
else
|
|
ss << "wrong type for field (" << fieldName() << ") " << type() << " != " << t;
|
|
uasserted(13111, ss.str() );
|
|
}
|
|
return *this;
|
|
}
|
|
const BSONElement& chk(bool expr) const {
|
|
uassert(13118, "unexpected or missing type value in BSON object", expr);
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
|
|
inline int BSONElement::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 mongo::String:
|
|
case Symbol:
|
|
return 15;
|
|
case Object:
|
|
return 20;
|
|
case mongo::Array:
|
|
return 25;
|
|
case BinData:
|
|
return 30;
|
|
case jstOID:
|
|
return 35;
|
|
case mongo::Bool:
|
|
return 40;
|
|
case mongo::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;
|
|
}
|
|
}
|
|
|
|
inline bool BSONElement::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 mongo::Bool:
|
|
return boolean();
|
|
case EOO:
|
|
case jstNULL:
|
|
case Undefined:
|
|
return false;
|
|
|
|
default:
|
|
;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** @return true if element is of a numeric type. */
|
|
inline bool BSONElement::isNumber() const {
|
|
switch( type() ) {
|
|
case NumberLong:
|
|
case NumberDouble:
|
|
case NumberInt:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inline bool BSONElement::isSimpleType() const {
|
|
switch( type() ) {
|
|
case NumberLong:
|
|
case NumberDouble:
|
|
case NumberInt:
|
|
case mongo::String:
|
|
case mongo::Bool:
|
|
case mongo::Date:
|
|
case jstOID:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inline double BSONElement::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 int value for the element safely. Zero returned if not a number. Converted to int if another numeric type. */
|
|
inline int BSONElement::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. */
|
|
inline long long BSONElement::numberLong() const {
|
|
switch( type() ) {
|
|
case NumberDouble:
|
|
return (long long) _numberDouble();
|
|
case NumberInt:
|
|
return _numberInt();
|
|
case NumberLong:
|
|
return _numberLong();
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
inline BSONElement::BSONElement() {
|
|
static char z = 0;
|
|
data = &z;
|
|
fieldNameSize_ = 0;
|
|
totalSize = 1;
|
|
}
|
|
|
|
}
|