mirror of
https://github.com/mongodb/mongo.git
synced 2024-12-01 01:21:03 +01:00
1491 lines
47 KiB
C++
1491 lines
47 KiB
C++
/** @file jsobj.cpp - BSON implementation
|
|
http://www.mongodb.org/display/DOCS/BSON
|
|
*/
|
|
|
|
/* 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.
|
|
*/
|
|
|
|
#include "pch.h"
|
|
#include "jsobj.h"
|
|
#include "nonce.h"
|
|
#include "../bson/util/atomic_int.h"
|
|
#include "../util/base64.h"
|
|
#include "../util/md5.hpp"
|
|
#include <limits>
|
|
#include "../util/unittest.h"
|
|
#include "../util/embedded_builder.h"
|
|
#include "json.h"
|
|
#include "jsobjmanipulator.h"
|
|
#include "../util/optime.h"
|
|
#include <boost/static_assert.hpp>
|
|
#include <boost/any.hpp>
|
|
#undef assert
|
|
#define assert MONGO_assert
|
|
|
|
// make sure our assumptions are valid
|
|
BOOST_STATIC_ASSERT( sizeof(int) == 4 );
|
|
BOOST_STATIC_ASSERT( sizeof(long long) == 8 );
|
|
BOOST_STATIC_ASSERT( sizeof(double) == 8 );
|
|
BOOST_STATIC_ASSERT( sizeof(mongo::Date_t) == 8 );
|
|
BOOST_STATIC_ASSERT( sizeof(mongo::OID) == 12 );
|
|
|
|
namespace mongo {
|
|
|
|
BSONElement nullElement;
|
|
|
|
GENOIDLabeler GENOID;
|
|
|
|
DateNowLabeler DATENOW;
|
|
|
|
string escape( string s , bool escape_slash=false) {
|
|
stringstream ret;
|
|
for ( string::iterator i = s.begin(); i != s.end(); ++i ) {
|
|
switch ( *i ) {
|
|
case '"':
|
|
ret << "\\\"";
|
|
break;
|
|
case '\\':
|
|
ret << "\\\\";
|
|
break;
|
|
case '/':
|
|
ret << (escape_slash ? "\\/" : "/");
|
|
break;
|
|
case '\b':
|
|
ret << "\\b";
|
|
break;
|
|
case '\f':
|
|
ret << "\\f";
|
|
break;
|
|
case '\n':
|
|
ret << "\\n";
|
|
break;
|
|
case '\r':
|
|
ret << "\\r";
|
|
break;
|
|
case '\t':
|
|
ret << "\\t";
|
|
break;
|
|
default:
|
|
if ( *i >= 0 && *i <= 0x1f ) {
|
|
ret << "\\u";
|
|
ret << hex;
|
|
ret.width( 4 );
|
|
ret.fill( '0' );
|
|
ret << int( *i );
|
|
} else {
|
|
ret << *i;
|
|
}
|
|
}
|
|
}
|
|
return ret.str();
|
|
}
|
|
|
|
string BSONElement::jsonString( JsonStringFormat format, bool includeFieldNames, int pretty ) const {
|
|
BSONType t = type();
|
|
if ( t == Undefined )
|
|
return "";
|
|
|
|
stringstream s;
|
|
if ( includeFieldNames )
|
|
s << '"' << escape( fieldName() ) << "\" : ";
|
|
switch ( type() ) {
|
|
case mongo::String:
|
|
case Symbol:
|
|
s << '"' << escape( valuestr() ) << '"';
|
|
break;
|
|
case NumberLong:
|
|
s << _numberLong();
|
|
break;
|
|
case NumberInt:
|
|
case NumberDouble:
|
|
if ( number() >= -numeric_limits< double >::max() &&
|
|
number() <= numeric_limits< double >::max() ) {
|
|
s.precision( 16 );
|
|
s << number();
|
|
} else {
|
|
stringstream ss;
|
|
ss << "Number " << number() << " cannot be represented in JSON";
|
|
string message = ss.str();
|
|
massert( 10311 , message.c_str(), false );
|
|
}
|
|
break;
|
|
case mongo::Bool:
|
|
s << ( boolean() ? "true" : "false" );
|
|
break;
|
|
case jstNULL:
|
|
s << "null";
|
|
break;
|
|
case Object:
|
|
s << embeddedObject().jsonString( format, pretty );
|
|
break;
|
|
case mongo::Array: {
|
|
if ( embeddedObject().isEmpty() ) {
|
|
s << "[]";
|
|
break;
|
|
}
|
|
s << "[ ";
|
|
BSONObjIterator i( embeddedObject() );
|
|
BSONElement e = i.next();
|
|
if ( !e.eoo() )
|
|
while ( 1 ) {
|
|
if( pretty ) {
|
|
s << '\n';
|
|
for( int x = 0; x < pretty; x++ )
|
|
s << " ";
|
|
}
|
|
s << e.jsonString( format, false, pretty?pretty+1:0 );
|
|
e = i.next();
|
|
if ( e.eoo() )
|
|
break;
|
|
s << ", ";
|
|
}
|
|
s << " ]";
|
|
break;
|
|
}
|
|
case DBRef: {
|
|
mongo::OID *x = (mongo::OID *) (valuestr() + valuestrsize());
|
|
if ( format == TenGen )
|
|
s << "Dbref( ";
|
|
else
|
|
s << "{ \"$ref\" : ";
|
|
s << '"' << valuestr() << "\", ";
|
|
if ( format != TenGen )
|
|
s << "\"$id\" : ";
|
|
s << '"' << *x << "\" ";
|
|
if ( format == TenGen )
|
|
s << ')';
|
|
else
|
|
s << '}';
|
|
break;
|
|
}
|
|
case jstOID:
|
|
if ( format == TenGen ) {
|
|
s << "ObjectId( ";
|
|
} else {
|
|
s << "{ \"$oid\" : ";
|
|
}
|
|
s << '"' << __oid() << '"';
|
|
if ( format == TenGen ) {
|
|
s << " )";
|
|
} else {
|
|
s << " }";
|
|
}
|
|
break;
|
|
case BinData: {
|
|
int len = *(int *)( value() );
|
|
BinDataType type = BinDataType( *(char *)( (int *)( value() ) + 1 ) );
|
|
s << "{ \"$binary\" : \"";
|
|
char *start = ( char * )( value() ) + sizeof( int ) + 1;
|
|
base64::encode( s , start , len );
|
|
s << "\", \"$type\" : \"" << hex;
|
|
s.width( 2 );
|
|
s.fill( '0' );
|
|
s << type << dec;
|
|
s << "\" }";
|
|
break;
|
|
}
|
|
case mongo::Date:
|
|
if ( format == Strict )
|
|
s << "{ \"$date\" : ";
|
|
else
|
|
s << "Date( ";
|
|
if( pretty ) {
|
|
Date_t d = date();
|
|
if( d == 0 ) s << '0';
|
|
else
|
|
s << '"' << date().toString() << '"';
|
|
} else
|
|
s << date();
|
|
if ( format == Strict )
|
|
s << " }";
|
|
else
|
|
s << " )";
|
|
break;
|
|
case RegEx:
|
|
if ( format == Strict ){
|
|
s << "{ \"$regex\" : \"" << escape( regex() );
|
|
s << "\", \"$options\" : \"" << regexFlags() << "\" }";
|
|
} else {
|
|
s << "/" << escape( regex() , true ) << "/";
|
|
// FIXME Worry about alpha order?
|
|
for ( const char *f = regexFlags(); *f; ++f ){
|
|
switch ( *f ) {
|
|
case 'g':
|
|
case 'i':
|
|
case 'm':
|
|
s << *f;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Code:
|
|
s << _asCode();
|
|
break;
|
|
|
|
case Timestamp:
|
|
s << "{ \"t\" : " << timestampTime() << " , \"i\" : " << timestampInc() << " }";
|
|
break;
|
|
|
|
case MinKey:
|
|
s << "{ \"$minKey\" : 1 }";
|
|
break;
|
|
|
|
case MaxKey:
|
|
s << "{ \"$maxKey\" : 1 }";
|
|
break;
|
|
|
|
default:
|
|
stringstream ss;
|
|
ss << "Cannot create a properly formatted JSON string with "
|
|
<< "element: " << toString() << " of type: " << type();
|
|
string message = ss.str();
|
|
massert( 10312 , message.c_str(), false );
|
|
}
|
|
return s.str();
|
|
}
|
|
|
|
int BSONElement::getGtLtOp( int def ) const {
|
|
const char *fn = fieldName();
|
|
if ( fn[0] == '$' && fn[1] ) {
|
|
if ( fn[2] == 't' ) {
|
|
if ( fn[1] == 'g' ) {
|
|
if ( fn[3] == 0 ) return BSONObj::GT;
|
|
else if ( fn[3] == 'e' && fn[4] == 0 ) return BSONObj::GTE;
|
|
}
|
|
else if ( fn[1] == 'l' ) {
|
|
if ( fn[3] == 0 ) return BSONObj::LT;
|
|
else if ( fn[3] == 'e' && fn[4] == 0 ) return BSONObj::LTE;
|
|
}
|
|
}
|
|
else if ( fn[1] == 'n' && fn[2] == 'e' ){
|
|
if ( fn[3] == 0 )
|
|
return BSONObj::NE;
|
|
if ( fn[3] == 'a' && fn[4] == 'r' && fn[5] == 0 )
|
|
return BSONObj::opNEAR;
|
|
}
|
|
else if ( fn[1] == 'm' ){
|
|
if ( fn[2] == 'o' && fn[3] == 'd' && fn[4] == 0 )
|
|
return BSONObj::opMOD;
|
|
if ( fn[2] == 'a' && fn[3] == 'x' && fn[4] == 'D' && fn[5] == 'i' && fn[6] == 's' && fn[7] == 't' && fn[8] == 'a' && fn[9] == 'n' && fn[10] == 'c' && fn[11] == 'e' && fn[12] == 0 )
|
|
return BSONObj::opMAX_DISTANCE;
|
|
}
|
|
else if ( fn[1] == 't' && fn[2] == 'y' && fn[3] == 'p' && fn[4] == 'e' && fn[5] == 0 )
|
|
return BSONObj::opTYPE;
|
|
else if ( fn[1] == 'i' && fn[2] == 'n' && fn[3] == 0 )
|
|
return BSONObj::opIN;
|
|
else if ( fn[1] == 'n' && fn[2] == 'i' && fn[3] == 'n' && fn[4] == 0 )
|
|
return BSONObj::NIN;
|
|
else if ( fn[1] == 'a' && fn[2] == 'l' && fn[3] == 'l' && fn[4] == 0 )
|
|
return BSONObj::opALL;
|
|
else if ( fn[1] == 's' && fn[2] == 'i' && fn[3] == 'z' && fn[4] == 'e' && fn[5] == 0 )
|
|
return BSONObj::opSIZE;
|
|
else if ( fn[1] == 'e' ){
|
|
if ( fn[2] == 'x' && fn[3] == 'i' && fn[4] == 's' && fn[5] == 't' && fn[6] == 's' && fn[7] == 0 )
|
|
return BSONObj::opEXISTS;
|
|
if ( fn[2] == 'l' && fn[3] == 'e' && fn[4] == 'm' && fn[5] == 'M' && fn[6] == 'a' && fn[7] == 't' && fn[8] == 'c' && fn[9] == 'h' && fn[10] == 0 )
|
|
return BSONObj::opELEM_MATCH;
|
|
}
|
|
else if ( fn[1] == 'r' && fn[2] == 'e' && fn[3] == 'g' && fn[4] == 'e' && fn[5] == 'x' && fn[6] == 0 )
|
|
return BSONObj::opREGEX;
|
|
else if ( fn[1] == 'o' && fn[2] == 'p' && fn[3] == 't' && fn[4] == 'i' && fn[5] == 'o' && fn[6] == 'n' && fn[7] == 's' && fn[8] == 0 )
|
|
return BSONObj::opOPTIONS;
|
|
else if ( fn[1] == 'w' && fn[2] == 'i' && fn[3] == 't' && fn[4] == 'h' && fn[5] == 'i' && fn[6] == 'n' && fn[7] == 0 )
|
|
return BSONObj::opWITHIN;
|
|
}
|
|
return def;
|
|
}
|
|
|
|
/* wo = "well ordered" */
|
|
int BSONElement::woCompare( const BSONElement &e,
|
|
bool considerFieldName ) const {
|
|
int lt = (int) canonicalType();
|
|
int rt = (int) e.canonicalType();
|
|
int x = lt - rt;
|
|
if( x != 0 && (!isNumber() || !e.isNumber()) )
|
|
return x;
|
|
if ( considerFieldName ) {
|
|
x = strcmp(fieldName(), e.fieldName());
|
|
if ( x != 0 )
|
|
return x;
|
|
}
|
|
x = compareElementValues(*this, e);
|
|
return x;
|
|
}
|
|
|
|
/* must be same type when called, unless both sides are #s
|
|
*/
|
|
int compareElementValues(const BSONElement& l, const BSONElement& r) {
|
|
int f;
|
|
double x;
|
|
|
|
switch ( l.type() ) {
|
|
case EOO:
|
|
case Undefined:
|
|
case jstNULL:
|
|
case MaxKey:
|
|
case MinKey:
|
|
f = l.canonicalType() - r.canonicalType();
|
|
if ( f<0 ) return -1;
|
|
return f==0 ? 0 : 1;
|
|
case Bool:
|
|
return *l.value() - *r.value();
|
|
case Timestamp:
|
|
case Date:
|
|
if ( l.date() < r.date() )
|
|
return -1;
|
|
return l.date() == r.date() ? 0 : 1;
|
|
case NumberLong:
|
|
if( r.type() == NumberLong ) {
|
|
long long L = l._numberLong();
|
|
long long R = r._numberLong();
|
|
if( L < R ) return -1;
|
|
if( L == R ) return 0;
|
|
return 1;
|
|
}
|
|
// else fall through
|
|
case NumberInt:
|
|
case NumberDouble: {
|
|
double left = l.number();
|
|
double right = r.number();
|
|
bool lNan = !( left <= numeric_limits< double >::max() &&
|
|
left >= -numeric_limits< double >::max() );
|
|
bool rNan = !( right <= numeric_limits< double >::max() &&
|
|
right >= -numeric_limits< double >::max() );
|
|
if ( lNan ) {
|
|
if ( rNan ) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
} else if ( rNan ) {
|
|
return 1;
|
|
}
|
|
x = left - right;
|
|
if ( x < 0 ) return -1;
|
|
return x == 0 ? 0 : 1;
|
|
}
|
|
case jstOID:
|
|
return memcmp(l.value(), r.value(), 12);
|
|
case Code:
|
|
case Symbol:
|
|
case String:
|
|
/* todo: utf version */
|
|
return strcmp(l.valuestr(), r.valuestr());
|
|
case Object:
|
|
case Array:
|
|
return l.embeddedObject().woCompare( r.embeddedObject() );
|
|
case DBRef: {
|
|
int lsz = l.valuesize();
|
|
int rsz = r.valuesize();
|
|
if ( lsz - rsz != 0 ) return lsz - rsz;
|
|
return memcmp(l.value(), r.value(), lsz);
|
|
}
|
|
case BinData: {
|
|
int lsz = l.objsize(); // our bin data size in bytes, not including the subtype byte
|
|
int rsz = r.objsize();
|
|
if ( lsz - rsz != 0 ) return lsz - rsz;
|
|
return memcmp(l.value()+4, r.value()+4, lsz+1);
|
|
}
|
|
case RegEx:
|
|
{
|
|
int c = strcmp(l.regex(), r.regex());
|
|
if ( c )
|
|
return c;
|
|
return strcmp(l.regexFlags(), r.regexFlags());
|
|
}
|
|
case CodeWScope : {
|
|
f = l.canonicalType() - r.canonicalType();
|
|
if ( f )
|
|
return f;
|
|
f = strcmp( l.codeWScopeCode() , r.codeWScopeCode() );
|
|
if ( f )
|
|
return f;
|
|
f = strcmp( l.codeWScopeScopeData() , r.codeWScopeScopeData() );
|
|
if ( f )
|
|
return f;
|
|
return 0;
|
|
}
|
|
default:
|
|
out() << "compareElementValues: bad type " << (int) l.type() << endl;
|
|
assert(false);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* Matcher --------------------------------------*/
|
|
|
|
// If the element is something like:
|
|
// a : { $gt : 3 }
|
|
// we append
|
|
// a : 3
|
|
// else we just append the element.
|
|
//
|
|
void appendElementHandlingGtLt(BSONObjBuilder& b, const BSONElement& e) {
|
|
if ( e.type() == Object ) {
|
|
BSONElement fe = e.embeddedObject().firstElement();
|
|
const char *fn = fe.fieldName();
|
|
if ( fn[0] == '$' && fn[1] && fn[2] == 't' ) {
|
|
b.appendAs(fe, e.fieldName());
|
|
return;
|
|
}
|
|
}
|
|
b.append(e);
|
|
}
|
|
|
|
int getGtLtOp(const BSONElement& e) {
|
|
if ( e.type() != Object )
|
|
return BSONObj::Equality;
|
|
|
|
BSONElement fe = e.embeddedObject().firstElement();
|
|
return fe.getGtLtOp();
|
|
}
|
|
|
|
FieldCompareResult compareDottedFieldNames( const string& l , const string& r ){
|
|
size_t lstart = 0;
|
|
size_t rstart = 0;
|
|
while ( 1 ){
|
|
if ( lstart >= l.size() ){
|
|
if ( rstart >= r.size() )
|
|
return SAME;
|
|
return RIGHT_SUBFIELD;
|
|
}
|
|
if ( rstart >= r.size() )
|
|
return LEFT_SUBFIELD;
|
|
|
|
size_t a = l.find( '.' , lstart );
|
|
size_t b = r.find( '.' , rstart );
|
|
|
|
size_t lend = a == string::npos ? l.size() : a;
|
|
size_t rend = b == string::npos ? r.size() : b;
|
|
|
|
const string& c = l.substr( lstart , lend - lstart );
|
|
const string& d = r.substr( rstart , rend - rstart );
|
|
|
|
int x = lexNumCmp( c.c_str(), d.c_str() );
|
|
|
|
if ( x < 0 )
|
|
return LEFT_BEFORE;
|
|
if ( x > 0 )
|
|
return RIGHT_BEFORE;
|
|
|
|
lstart = lend + 1;
|
|
rstart = rend + 1;
|
|
}
|
|
}
|
|
|
|
/* BSONObj ------------------------------------------------------------*/
|
|
|
|
string BSONObj::md5() const {
|
|
md5digest d;
|
|
md5_state_t st;
|
|
md5_init(&st);
|
|
md5_append( &st , (const md5_byte_t*)_objdata , objsize() );
|
|
md5_finish(&st, d);
|
|
return digestToString( d );
|
|
}
|
|
|
|
string BSONObj::jsonString( JsonStringFormat format, int pretty ) const {
|
|
|
|
if ( isEmpty() ) return "{}";
|
|
|
|
stringstream s;
|
|
s << "{ ";
|
|
BSONObjIterator i(*this);
|
|
BSONElement e = i.next();
|
|
if ( !e.eoo() )
|
|
while ( 1 ) {
|
|
s << e.jsonString( format, true, pretty?pretty+1:0 );
|
|
e = i.next();
|
|
if ( e.eoo() )
|
|
break;
|
|
s << ",";
|
|
if ( pretty ) {
|
|
s << '\n';
|
|
for( int x = 0; x < pretty; x++ )
|
|
s << " ";
|
|
}
|
|
else {
|
|
s << " ";
|
|
}
|
|
}
|
|
s << " }";
|
|
return s.str();
|
|
}
|
|
|
|
// todo: can be a little faster if we don't use toString() here.
|
|
bool BSONObj::valid() const {
|
|
try{
|
|
BSONObjIterator it(*this);
|
|
while( it.moreWithEOO() ){
|
|
// both throw exception on failure
|
|
BSONElement e = it.next(true);
|
|
e.validate();
|
|
|
|
if (e.eoo()){
|
|
if (it.moreWithEOO())
|
|
return false;
|
|
return true;
|
|
}else if (e.isABSONObj()){
|
|
if(!e.embeddedObject().valid())
|
|
return false;
|
|
}else if (e.type() == CodeWScope){
|
|
if(!e.codeWScopeObject().valid())
|
|
return false;
|
|
}
|
|
}
|
|
} catch (...) {
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int BSONObj::woCompare(const BSONObj& r, const Ordering &o, bool considerFieldName) const {
|
|
if ( isEmpty() )
|
|
return r.isEmpty() ? 0 : -1;
|
|
if ( r.isEmpty() )
|
|
return 1;
|
|
|
|
BSONObjIterator i(*this);
|
|
BSONObjIterator j(r);
|
|
unsigned mask = 1;
|
|
while ( 1 ) {
|
|
// so far, equal...
|
|
|
|
BSONElement l = i.next();
|
|
BSONElement r = j.next();
|
|
if ( l.eoo() )
|
|
return r.eoo() ? 0 : -1;
|
|
if ( r.eoo() )
|
|
return 1;
|
|
|
|
int x;
|
|
{
|
|
x = l.woCompare( r, considerFieldName );
|
|
if( o.descending(mask) )
|
|
x = -x;
|
|
}
|
|
if ( x != 0 )
|
|
return x;
|
|
mask <<= 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* well ordered compare */
|
|
int BSONObj::woCompare(const BSONObj &r, const BSONObj &idxKey,
|
|
bool considerFieldName) const {
|
|
if ( isEmpty() )
|
|
return r.isEmpty() ? 0 : -1;
|
|
if ( r.isEmpty() )
|
|
return 1;
|
|
|
|
bool ordered = !idxKey.isEmpty();
|
|
|
|
BSONObjIterator i(*this);
|
|
BSONObjIterator j(r);
|
|
BSONObjIterator k(idxKey);
|
|
while ( 1 ) {
|
|
// so far, equal...
|
|
|
|
BSONElement l = i.next();
|
|
BSONElement r = j.next();
|
|
BSONElement o;
|
|
if ( ordered )
|
|
o = k.next();
|
|
if ( l.eoo() )
|
|
return r.eoo() ? 0 : -1;
|
|
if ( r.eoo() )
|
|
return 1;
|
|
|
|
int x;
|
|
/*
|
|
if( ordered && o.type() == String && strcmp(o.valuestr(), "ascii-proto") == 0 &&
|
|
l.type() == String && r.type() == String ) {
|
|
// note: no negative support yet, as this is just sort of a POC
|
|
x = _stricmp(l.valuestr(), r.valuestr());
|
|
}
|
|
else*/ {
|
|
x = l.woCompare( r, considerFieldName );
|
|
if ( ordered && o.number() < 0 )
|
|
x = -x;
|
|
}
|
|
if ( x != 0 )
|
|
return x;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
BSONObj staticNull = fromjson( "{'':null}" );
|
|
|
|
/* well ordered compare */
|
|
int BSONObj::woSortOrder(const BSONObj& other, const BSONObj& sortKey ) const{
|
|
if ( isEmpty() )
|
|
return other.isEmpty() ? 0 : -1;
|
|
if ( other.isEmpty() )
|
|
return 1;
|
|
|
|
uassert( 10060 , "woSortOrder needs a non-empty sortKey" , ! sortKey.isEmpty() );
|
|
|
|
BSONObjIterator i(sortKey);
|
|
while ( 1 ){
|
|
BSONElement f = i.next();
|
|
if ( f.eoo() )
|
|
return 0;
|
|
|
|
BSONElement l = getField( f.fieldName() );
|
|
if ( l.eoo() )
|
|
l = staticNull.firstElement();
|
|
BSONElement r = other.getField( f.fieldName() );
|
|
if ( r.eoo() )
|
|
r = staticNull.firstElement();
|
|
|
|
int x = l.woCompare( r, false );
|
|
if ( f.number() < 0 )
|
|
x = -x;
|
|
if ( x != 0 )
|
|
return x;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void BSONObj::getFieldsDotted(const char *name, BSONElementSet &ret ) const {
|
|
BSONElement e = getField( name );
|
|
if ( e.eoo() ) {
|
|
const char *p = strchr(name, '.');
|
|
if ( p ) {
|
|
string left(name, p-name);
|
|
const char* next = p+1;
|
|
BSONElement e = getField( left.c_str() );
|
|
|
|
if (e.type() == Object){
|
|
e.embeddedObject().getFieldsDotted(next, ret);
|
|
} else if (e.type() == Array) {
|
|
bool allDigits = false;
|
|
if ( isdigit( *next ) ){
|
|
const char * temp = next + 1;
|
|
while ( isdigit( *temp ) )
|
|
temp++;
|
|
allDigits = *temp == '.';
|
|
}
|
|
if (allDigits) {
|
|
e.embeddedObject().getFieldsDotted(next, ret);
|
|
} else {
|
|
BSONObjIterator i(e.embeddedObject());
|
|
while ( i.more() ){
|
|
BSONElement e2 = i.next();
|
|
if (e2.type() == Object || e2.type() == Array)
|
|
e2.embeddedObject().getFieldsDotted(next, ret);
|
|
}
|
|
}
|
|
} else {
|
|
// do nothing: no match
|
|
}
|
|
}
|
|
} else {
|
|
if (e.type() == Array){
|
|
BSONObjIterator i(e.embeddedObject());
|
|
while ( i.more() )
|
|
ret.insert(i.next());
|
|
} else {
|
|
ret.insert(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
BSONElement BSONObj::getFieldDottedOrArray(const char *&name) const {
|
|
const char *p = strchr(name, '.');
|
|
string left;
|
|
if ( p ) {
|
|
left = string(name, p-name);
|
|
name = p + 1;
|
|
} else {
|
|
left = string(name);
|
|
name = name + strlen(name);
|
|
}
|
|
BSONElement sub = getField(left.c_str());
|
|
if ( sub.eoo() )
|
|
return nullElement;
|
|
else if ( sub.type() == Array || strlen( name ) == 0 )
|
|
return sub;
|
|
else if ( sub.type() == Object )
|
|
return sub.embeddedObject().getFieldDottedOrArray( name );
|
|
else
|
|
return nullElement;
|
|
}
|
|
|
|
/* makes a new BSONObj with the fields specified in pattern.
|
|
fields returned in the order they appear in pattern.
|
|
if any field missing or undefined in the original object, that field
|
|
in the output will be null.
|
|
|
|
n^2 implementation bad if pattern and object have lots
|
|
of fields - normally pattern doesn't so should be fine.
|
|
*/
|
|
BSONObj BSONObj::extractFieldsDotted(BSONObj pattern) const {
|
|
BSONObjBuilder b;
|
|
BSONObjIterator i(pattern);
|
|
while (i.more()) {
|
|
BSONElement e = i.next();
|
|
const char *name = e.fieldName();
|
|
|
|
BSONElement x = getFieldDotted( name );
|
|
if ( x.eoo() || x.type() == Undefined ) {
|
|
b.appendNull(name);
|
|
} else {
|
|
b.appendAs(x, name);
|
|
}
|
|
}
|
|
return b.done();
|
|
}
|
|
|
|
/**
|
|
sets element field names to empty string
|
|
If a field in pattern is missing, it is omitted from the returned
|
|
object.
|
|
*/
|
|
BSONObj BSONObj::extractFieldsUnDotted(BSONObj pattern) const {
|
|
BSONObjBuilder b;
|
|
BSONObjIterator i(pattern);
|
|
while ( i.moreWithEOO() ) {
|
|
BSONElement e = i.next();
|
|
if ( e.eoo() )
|
|
break;
|
|
BSONElement x = getField(e.fieldName());
|
|
if ( !x.eoo() )
|
|
b.appendAs(x, "");
|
|
}
|
|
return b.obj();
|
|
}
|
|
|
|
BSONObj BSONObj::extractFields(const BSONObj& pattern , bool fillWithNull ) const {
|
|
BSONObjBuilder b(32); // scanandorder.h can make a zillion of these, so we start the allocation very small
|
|
BSONObjIterator i(pattern);
|
|
while ( i.moreWithEOO() ) {
|
|
BSONElement e = i.next();
|
|
if ( e.eoo() )
|
|
break;
|
|
BSONElement x = getFieldDotted(e.fieldName());
|
|
if ( ! x.eoo() )
|
|
b.appendAs( x, e.fieldName() );
|
|
else if ( fillWithNull )
|
|
b.appendNull( e.fieldName() );
|
|
}
|
|
return b.obj();
|
|
}
|
|
|
|
BSONObj BSONObj::filterFieldsUndotted( const BSONObj &filter, bool inFilter ) const {
|
|
BSONObjBuilder b;
|
|
BSONObjIterator i( *this );
|
|
while( i.moreWithEOO() ) {
|
|
BSONElement e = i.next();
|
|
if ( e.eoo() )
|
|
break;
|
|
BSONElement x = filter.getField( e.fieldName() );
|
|
if ( ( x.eoo() && !inFilter ) ||
|
|
( !x.eoo() && inFilter ) )
|
|
b.append( e );
|
|
}
|
|
return b.obj();
|
|
}
|
|
|
|
BSONElement BSONObj::getFieldUsingIndexNames(const char *fieldName, const BSONObj &indexKey) const {
|
|
BSONObjIterator i( indexKey );
|
|
int j = 0;
|
|
while( i.moreWithEOO() ) {
|
|
BSONElement f = i.next();
|
|
if ( f.eoo() )
|
|
return BSONElement();
|
|
if ( strcmp( f.fieldName(), fieldName ) == 0 )
|
|
break;
|
|
++j;
|
|
}
|
|
BSONObjIterator k( *this );
|
|
while( k.moreWithEOO() ) {
|
|
BSONElement g = k.next();
|
|
if ( g.eoo() )
|
|
return BSONElement();
|
|
if ( j == 0 ) {
|
|
return g;
|
|
}
|
|
--j;
|
|
}
|
|
return BSONElement();
|
|
}
|
|
|
|
int BSONObj::getIntField(const char *name) const {
|
|
BSONElement e = getField(name);
|
|
return e.isNumber() ? (int) e.number() : INT_MIN;
|
|
}
|
|
|
|
bool BSONObj::getBoolField(const char *name) const {
|
|
BSONElement e = getField(name);
|
|
return e.type() == Bool ? e.boolean() : false;
|
|
}
|
|
|
|
const char * BSONObj::getStringField(const char *name) const {
|
|
BSONElement e = getField(name);
|
|
return e.type() == String ? e.valuestr() : "";
|
|
}
|
|
|
|
/* grab names of all the fields in this object */
|
|
int BSONObj::getFieldNames(set<string>& fields) const {
|
|
int n = 0;
|
|
BSONObjIterator i(*this);
|
|
while ( i.moreWithEOO() ) {
|
|
BSONElement e = i.next();
|
|
if ( e.eoo() )
|
|
break;
|
|
fields.insert(e.fieldName());
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/* note: addFields always adds _id even if not specified
|
|
returns n added not counting _id unless requested.
|
|
*/
|
|
int BSONObj::addFields(BSONObj& from, set<string>& fields) {
|
|
assert( isEmpty() && !isOwned() ); /* partial implementation for now... */
|
|
|
|
BSONObjBuilder b;
|
|
|
|
int N = fields.size();
|
|
int n = 0;
|
|
BSONObjIterator i(from);
|
|
bool gotId = false;
|
|
while ( i.moreWithEOO() ) {
|
|
BSONElement e = i.next();
|
|
const char *fname = e.fieldName();
|
|
if ( fields.count(fname) ) {
|
|
b.append(e);
|
|
++n;
|
|
gotId = gotId || strcmp(fname, "_id")==0;
|
|
if ( n == N && gotId )
|
|
break;
|
|
} else if ( strcmp(fname, "_id")==0 ) {
|
|
b.append(e);
|
|
gotId = true;
|
|
if ( n == N && gotId )
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( n ) {
|
|
int len;
|
|
init( b.decouple(len), true );
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
BSONObj BSONObj::clientReadable() const {
|
|
BSONObjBuilder b;
|
|
BSONObjIterator i( *this );
|
|
while( i.moreWithEOO() ) {
|
|
BSONElement e = i.next();
|
|
if ( e.eoo() )
|
|
break;
|
|
switch( e.type() ) {
|
|
case MinKey: {
|
|
BSONObjBuilder m;
|
|
m.append( "$minElement", 1 );
|
|
b.append( e.fieldName(), m.done() );
|
|
break;
|
|
}
|
|
case MaxKey: {
|
|
BSONObjBuilder m;
|
|
m.append( "$maxElement", 1 );
|
|
b.append( e.fieldName(), m.done() );
|
|
break;
|
|
}
|
|
default:
|
|
b.append( e );
|
|
}
|
|
}
|
|
return b.obj();
|
|
}
|
|
|
|
BSONObj BSONObj::replaceFieldNames( const BSONObj &names ) const {
|
|
BSONObjBuilder b;
|
|
BSONObjIterator i( *this );
|
|
BSONObjIterator j( names );
|
|
BSONElement f = j.moreWithEOO() ? j.next() : BSONObj().firstElement();
|
|
while( i.moreWithEOO() ) {
|
|
BSONElement e = i.next();
|
|
if ( e.eoo() )
|
|
break;
|
|
if ( !f.eoo() ) {
|
|
b.appendAs( e, f.fieldName() );
|
|
f = j.next();
|
|
} else {
|
|
b.append( e );
|
|
}
|
|
}
|
|
return b.obj();
|
|
}
|
|
|
|
bool BSONObj::okForStorage() const {
|
|
BSONObjIterator i( *this );
|
|
while ( i.more() ){
|
|
BSONElement e = i.next();
|
|
const char * name = e.fieldName();
|
|
|
|
if ( strchr( name , '.' ) ||
|
|
strchr( name , '$' ) ){
|
|
return
|
|
strcmp( name , "$ref" ) == 0 ||
|
|
strcmp( name , "$id" ) == 0
|
|
;
|
|
}
|
|
|
|
if ( e.mayEncapsulate() ){
|
|
switch ( e.type() ){
|
|
case Object:
|
|
case Array:
|
|
if ( ! e.embeddedObject().okForStorage() )
|
|
return false;
|
|
break;
|
|
case CodeWScope:
|
|
if ( ! e.codeWScopeObject().okForStorage() )
|
|
return false;
|
|
break;
|
|
default:
|
|
uassert( 12579, "unhandled cases in BSONObj okForStorage" , 0 );
|
|
}
|
|
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void BSONObj::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++;
|
|
}
|
|
}
|
|
|
|
string BSONObj::hexDump() const {
|
|
stringstream ss;
|
|
const char *d = objdata();
|
|
int size = objsize();
|
|
for( int i = 0; i < size; ++i ) {
|
|
ss.width( 2 );
|
|
ss.fill( '0' );
|
|
ss << hex << (unsigned)(unsigned char)( d[ i ] ) << dec;
|
|
if ( ( d[ i ] >= '0' && d[ i ] <= '9' ) || ( d[ i ] >= 'A' && d[ i ] <= 'z' ) )
|
|
ss << '\'' << d[ i ] << '\'';
|
|
if ( i != size - 1 )
|
|
ss << ' ';
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
void nested2dotted(BSONObjBuilder& b, const BSONObj& obj, const string& base){
|
|
BSONObjIterator it(obj);
|
|
while (it.more()){
|
|
BSONElement e = it.next();
|
|
if (e.type() == Object){
|
|
string newbase = base + e.fieldName() + ".";
|
|
nested2dotted(b, e.embeddedObject(), newbase);
|
|
}else{
|
|
string newbase = base + e.fieldName();
|
|
b.appendAs(e, newbase.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void dotted2nested(BSONObjBuilder& b, const BSONObj& obj){
|
|
//use map to sort fields
|
|
BSONMap sorted = bson2map(obj);
|
|
EmbeddedBuilder eb(&b);
|
|
for(BSONMap::const_iterator it=sorted.begin(); it!=sorted.end(); ++it){
|
|
eb.appendAs(it->second, it->first);
|
|
}
|
|
eb.done();
|
|
}
|
|
|
|
/*-- test things ----------------------------------------------------*/
|
|
|
|
#pragma pack(1)
|
|
struct MaxKeyData {
|
|
MaxKeyData() {
|
|
totsize=7;
|
|
maxkey=MaxKey;
|
|
name=0;
|
|
eoo=EOO;
|
|
}
|
|
int totsize;
|
|
char maxkey;
|
|
char name;
|
|
char eoo;
|
|
} maxkeydata;
|
|
BSONObj maxKey((const char *) &maxkeydata);
|
|
|
|
struct MinKeyData {
|
|
MinKeyData() {
|
|
totsize=7;
|
|
minkey=MinKey;
|
|
name=0;
|
|
eoo=EOO;
|
|
}
|
|
int totsize;
|
|
char minkey;
|
|
char name;
|
|
char eoo;
|
|
} minkeydata;
|
|
BSONObj minKey((const char *) &minkeydata);
|
|
|
|
/*
|
|
struct JSObj0 {
|
|
JSObj0() {
|
|
totsize = 5;
|
|
eoo = EOO;
|
|
}
|
|
int totsize;
|
|
char eoo;
|
|
} js0;
|
|
*/
|
|
#pragma pack()
|
|
|
|
struct BsonUnitTest : public UnitTest {
|
|
void testRegex() {
|
|
|
|
BSONObjBuilder b;
|
|
b.appendRegex("x", "foo");
|
|
BSONObj o = b.done();
|
|
|
|
BSONObjBuilder c;
|
|
c.appendRegex("x", "goo");
|
|
BSONObj p = c.done();
|
|
|
|
assert( !o.woEqual( p ) );
|
|
assert( o.woCompare( p ) < 0 );
|
|
|
|
}
|
|
void testoid() {
|
|
OID id;
|
|
id.init();
|
|
// sleepsecs(3);
|
|
|
|
OID b;
|
|
// goes with sleep above...
|
|
// b.init();
|
|
// assert( memcmp(id.getData(), b.getData(), 12) < 0 );
|
|
|
|
b.init( id.str() );
|
|
assert( b == id );
|
|
}
|
|
|
|
void testbounds(){
|
|
BSONObj l , r;
|
|
{
|
|
BSONObjBuilder b;
|
|
b.append( "x" , numeric_limits<long long>::max() );
|
|
l = b.obj();
|
|
}
|
|
{
|
|
BSONObjBuilder b;
|
|
b.append( "x" , numeric_limits<double>::max() );
|
|
r = b.obj();
|
|
}
|
|
assert( l.woCompare( r ) < 0 );
|
|
assert( r.woCompare( l ) > 0 );
|
|
{
|
|
BSONObjBuilder b;
|
|
b.append( "x" , numeric_limits<int>::max() );
|
|
l = b.obj();
|
|
}
|
|
assert( l.woCompare( r ) < 0 );
|
|
assert( r.woCompare( l ) > 0 );
|
|
}
|
|
|
|
void testorder(){
|
|
{
|
|
BSONObj x,y,z;
|
|
{ BSONObjBuilder b; b.append( "x" , (long long)2 ); x = b.obj(); }
|
|
{ BSONObjBuilder b; b.append( "x" , (int)3 ); y = b.obj(); }
|
|
{ BSONObjBuilder b; b.append( "x" , (long long)4 ); z = b.obj(); }
|
|
assert( x.woCompare( y ) < 0 );
|
|
assert( x.woCompare( z ) < 0 );
|
|
assert( y.woCompare( x ) > 0 );
|
|
assert( z.woCompare( x ) > 0 );
|
|
assert( y.woCompare( z ) < 0 );
|
|
assert( z.woCompare( y ) > 0 );
|
|
}
|
|
|
|
{
|
|
BSONObj ll,d,i,n,u;
|
|
{ BSONObjBuilder b; b.append( "x" , (long long)2 ); ll = b.obj(); }
|
|
{ BSONObjBuilder b; b.append( "x" , (double)2 ); d = b.obj(); }
|
|
{ BSONObjBuilder b; b.append( "x" , (int)2 ); i = b.obj(); }
|
|
{ BSONObjBuilder b; b.appendNull( "x" ); n = b.obj(); }
|
|
{ BSONObjBuilder b; u = b.obj(); }
|
|
|
|
assert( ll.woCompare( u ) == d.woCompare( u ) );
|
|
assert( ll.woCompare( u ) == i.woCompare( u ) );
|
|
BSONObj k = BSON( "x" << 1 );
|
|
assert( ll.woCompare( u , k ) == d.woCompare( u , k ) );
|
|
assert( ll.woCompare( u , k ) == i.woCompare( u , k ) );
|
|
|
|
assert( u.woCompare( ll ) == u.woCompare( d ) );
|
|
assert( u.woCompare( ll ) == u.woCompare( i ) );
|
|
assert( u.woCompare( ll , k ) == u.woCompare( d , k ) );
|
|
assert( u.woCompare( ll , k ) == u.woCompare( d , k ) );
|
|
|
|
assert( i.woCompare( n ) == d.woCompare( n ) );
|
|
|
|
assert( ll.woCompare( n ) == d.woCompare( n ) );
|
|
assert( ll.woCompare( n ) == i.woCompare( n ) );
|
|
assert( ll.woCompare( n , k ) == d.woCompare( n , k ) );
|
|
assert( ll.woCompare( n , k ) == i.woCompare( n , k ) );
|
|
|
|
assert( n.woCompare( ll ) == n.woCompare( d ) );
|
|
assert( n.woCompare( ll ) == n.woCompare( i ) );
|
|
assert( n.woCompare( ll , k ) == n.woCompare( d , k ) );
|
|
assert( n.woCompare( ll , k ) == n.woCompare( d , k ) );
|
|
}
|
|
|
|
{
|
|
BSONObj l,r;
|
|
{ BSONObjBuilder b; b.append( "x" , "eliot" ); l = b.obj(); }
|
|
{ BSONObjBuilder b; b.appendSymbol( "x" , "eliot" ); r = b.obj(); }
|
|
assert( l.woCompare( r ) == 0 );
|
|
assert( r.woCompare( l ) == 0 );
|
|
}
|
|
}
|
|
|
|
void run() {
|
|
testRegex();
|
|
BSONObjBuilder A,B,C;
|
|
A.append("x", 2);
|
|
B.append("x", 2.0);
|
|
C.append("x", 2.1);
|
|
BSONObj a = A.done();
|
|
BSONObj b = B.done();
|
|
BSONObj c = C.done();
|
|
assert( !a.woEqual( b ) ); // comments on operator==
|
|
int cmp = a.woCompare(b);
|
|
assert( cmp == 0 );
|
|
cmp = a.woCompare(c);
|
|
assert( cmp < 0 );
|
|
testoid();
|
|
testbounds();
|
|
testorder();
|
|
}
|
|
} bson_unittest;
|
|
|
|
/*
|
|
BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const char * value ) {
|
|
_builder->append( _fieldName , value );
|
|
return *_builder;
|
|
}
|
|
|
|
BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const int value ) {
|
|
_builder->append( _fieldName , value );
|
|
return *_builder;
|
|
}
|
|
|
|
BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const double value ) {
|
|
_builder->append( _fieldName , value );
|
|
return *_builder;
|
|
}
|
|
*/
|
|
|
|
void OID::init() {
|
|
static AtomicUInt inc = getRandomNumber();
|
|
unsigned t = (unsigned) time(0);
|
|
char *T = (char *) &t;
|
|
data[0] = T[3];
|
|
data[1] = T[2];
|
|
data[2] = T[1];
|
|
data[3] = T[0];
|
|
|
|
(unsigned&) data[4] = _machine;
|
|
|
|
int new_inc = inc++;
|
|
T = (char *) &new_inc;
|
|
char * raw = (char*)&b;
|
|
raw[0] = T[3];
|
|
raw[1] = T[2];
|
|
raw[2] = T[1];
|
|
raw[3] = T[0];
|
|
}
|
|
|
|
unsigned OID::_machine = (unsigned) security.getNonceInitSafe();
|
|
void OID::newState(){
|
|
// using fresh Security object to avoid buffered devrandom
|
|
_machine = (unsigned) Security().getNonce();
|
|
}
|
|
|
|
void OID::init( string s ){
|
|
assert( s.size() == 24 );
|
|
const char *p = s.c_str();
|
|
char buf[3];
|
|
buf[2] = 0;
|
|
for( int i = 0; i < 12; i++ ) {
|
|
buf[0] = p[0];
|
|
buf[1] = p[1];
|
|
p += 2;
|
|
stringstream ss(buf);
|
|
unsigned z;
|
|
ss >> hex >> z;
|
|
data[i] = z;
|
|
}
|
|
}
|
|
|
|
void OID::init(Date_t date, bool max){
|
|
int time = (int) (date / 1000);
|
|
char* T = (char *) &time;
|
|
data[0] = T[3];
|
|
data[1] = T[2];
|
|
data[2] = T[1];
|
|
data[3] = T[0];
|
|
|
|
if (max)
|
|
*(long long*)(data + 4) = 0xFFFFFFFFFFFFFFFFll;
|
|
else
|
|
*(long long*)(data + 4) = 0x0000000000000000ll;
|
|
}
|
|
|
|
time_t OID::asTimeT(){
|
|
int time;
|
|
char* T = (char *) &time;
|
|
T[0] = data[3];
|
|
T[1] = data[2];
|
|
T[2] = data[1];
|
|
T[3] = data[0];
|
|
return time;
|
|
}
|
|
|
|
Labeler::Label GT( "$gt" );
|
|
Labeler::Label GTE( "$gte" );
|
|
Labeler::Label LT( "$lt" );
|
|
Labeler::Label LTE( "$lte" );
|
|
Labeler::Label NE( "$ne" );
|
|
Labeler::Label SIZE( "$size" );
|
|
|
|
void BSONElementManipulator::initTimestamp() {
|
|
massert( 10332 , "Expected CurrentTime type", _element.type() == Timestamp );
|
|
unsigned long long ×tamp = *( reinterpret_cast< unsigned long long* >( value() ) );
|
|
if ( timestamp == 0 )
|
|
timestamp = OpTime::now().asDate();
|
|
}
|
|
|
|
|
|
void BSONObjBuilder::appendMinForType( const string& field , int t ){
|
|
switch ( t ){
|
|
case MinKey: appendMinKey( field.c_str() ); return;
|
|
case MaxKey: appendMinKey( field.c_str() ); return;
|
|
case NumberInt:
|
|
case NumberDouble:
|
|
case NumberLong:
|
|
append( field.c_str() , - numeric_limits<double>::max() ); return;
|
|
case jstOID:
|
|
{
|
|
OID o;
|
|
memset(&o, 0, sizeof(o));
|
|
appendOID( field.c_str() , &o);
|
|
return;
|
|
}
|
|
case Bool: appendBool( field.c_str() , false); return;
|
|
case Date: appendDate( field.c_str() , 0); return;
|
|
case jstNULL: appendNull( field.c_str() ); return;
|
|
case Symbol:
|
|
case String: append( field.c_str() , "" ); return;
|
|
case Object: append( field.c_str() , BSONObj() ); return;
|
|
case Array:
|
|
appendArray( field.c_str() , BSONObj() ); return;
|
|
case BinData:
|
|
appendBinData( field.c_str() , 0 , Function , (const char *) 0 ); return;
|
|
case Undefined:
|
|
appendUndefined( field.c_str() ); return;
|
|
case RegEx: appendRegex( field.c_str() , "" ); return;
|
|
case DBRef:
|
|
{
|
|
OID o;
|
|
memset(&o, 0, sizeof(o));
|
|
appendDBRef( field.c_str() , "" , o );
|
|
return;
|
|
}
|
|
case Code: appendCode( field.c_str() , "" ); return;
|
|
case CodeWScope: appendCodeWScope( field.c_str() , "" , BSONObj() ); return;
|
|
case Timestamp: appendTimestamp( field.c_str() , 0); return;
|
|
|
|
};
|
|
log() << "type not support for appendMinElementForType: " << t << endl;
|
|
uassert( 10061 , "type not supported for appendMinElementForType" , false );
|
|
}
|
|
|
|
void BSONObjBuilder::appendMaxForType( const string& field , int t ){
|
|
switch ( t ){
|
|
case MinKey: appendMaxKey( field.c_str() ); break;
|
|
case MaxKey: appendMaxKey( field.c_str() ); break;
|
|
case NumberInt:
|
|
case NumberDouble:
|
|
case NumberLong:
|
|
append( field.c_str() , numeric_limits<double>::max() );
|
|
break;
|
|
case BinData:
|
|
appendMinForType( field , jstOID );
|
|
break;
|
|
case jstOID:
|
|
{
|
|
OID o;
|
|
memset(&o, 0xFF, sizeof(o));
|
|
appendOID( field.c_str() , &o);
|
|
break;
|
|
}
|
|
case Undefined:
|
|
case jstNULL:
|
|
appendMinForType( field , NumberInt );
|
|
case Bool: appendBool( field.c_str() , true); break;
|
|
case Date: appendDate( field.c_str() , 0xFFFFFFFFFFFFFFFFLL ); break;
|
|
case Symbol:
|
|
case String: append( field.c_str() , BSONObj() ); break;
|
|
case Code:
|
|
case CodeWScope:
|
|
appendCodeWScope( field.c_str() , "ZZZ" , BSONObj() ); break;
|
|
case Timestamp:
|
|
appendTimestamp( field.c_str() , numeric_limits<unsigned long long>::max() ); break;
|
|
default:
|
|
appendMinForType( field , t + 1 );
|
|
}
|
|
}
|
|
|
|
const string BSONObjBuilder::numStrs[] = {
|
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
|
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
|
|
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
|
|
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
|
|
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
|
|
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
|
|
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
|
|
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
|
|
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
|
|
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
|
|
};
|
|
|
|
bool BSONObjBuilder::appendAsNumber( const string& fieldName , const string& data ){
|
|
if ( data.size() == 0 )
|
|
return false;
|
|
|
|
unsigned int pos=0;
|
|
if ( data[0] == '-' )
|
|
pos++;
|
|
|
|
bool hasDec = false;
|
|
|
|
for ( ; pos<data.size(); pos++ ){
|
|
if ( isdigit(data[pos]) )
|
|
continue;
|
|
|
|
if ( data[pos] == '.' ){
|
|
if ( hasDec )
|
|
return false;
|
|
hasDec = true;
|
|
continue;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if ( hasDec ){
|
|
double d = atof( data.c_str() );
|
|
append( fieldName.c_str() , d );
|
|
return true;
|
|
}
|
|
|
|
if ( data.size() < 8 ){
|
|
append( fieldName , atoi( data.c_str() ) );
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
long long num = boost::lexical_cast<long long>( data );
|
|
append( fieldName , num );
|
|
return true;
|
|
}
|
|
catch(bad_lexical_cast &){
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
void BSONObjBuilder::appendKeys( const BSONObj& keyPattern , const BSONObj& values ){
|
|
BSONObjIterator i(keyPattern);
|
|
BSONObjIterator j(values);
|
|
|
|
while ( i.more() && j.more() ){
|
|
appendAs( j.next() , i.next().fieldName() );
|
|
}
|
|
|
|
assert( ! i.more() );
|
|
assert( ! j.more() );
|
|
}
|
|
|
|
int BSONElementFieldSorter( const void * a , const void * b ){
|
|
const char * x = *((const char**)a);
|
|
const char * y = *((const char**)b);
|
|
x++; y++;
|
|
return lexNumCmp( x , y );
|
|
}
|
|
|
|
BSONObjIteratorSorted::BSONObjIteratorSorted( const BSONObj& o ){
|
|
_nfields = o.nFields();
|
|
_fields = new const char*[_nfields];
|
|
int x = 0;
|
|
BSONObjIterator i( o );
|
|
while ( i.more() ){
|
|
_fields[x++] = i.next().rawdata();
|
|
assert( _fields[x-1] );
|
|
}
|
|
assert( x == _nfields );
|
|
qsort( _fields , _nfields , sizeof(char*) , BSONElementFieldSorter );
|
|
_cur = 0;
|
|
}
|
|
|
|
/** transform a BSON array into a vector of BSONElements.
|
|
we match array # positions with their vector position, and ignore
|
|
any non-numeric fields.
|
|
*/
|
|
vector<BSONElement> BSONElement::Array() const {
|
|
chk(mongo::Array);
|
|
vector<BSONElement> v;
|
|
BSONObjIterator i(Obj());
|
|
while( i.more() ) {
|
|
BSONElement e = i.next();
|
|
const char *f = e.fieldName();
|
|
try {
|
|
unsigned u = stringToNum(f);
|
|
assert( u < 4096 );
|
|
if( u >= v.size() )
|
|
v.resize(u+1);
|
|
v[u] = e;
|
|
}
|
|
catch(unsigned) { }
|
|
}
|
|
return v;
|
|
}
|
|
|
|
} // namespace mongo
|