0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00
mongodb/db/jsobj.cpp

1048 lines
32 KiB
C++
Raw Normal View History

2008-06-06 15:43:15 +02:00
// jsobj.cpp
/**
* Copyright (C) 2008 10gen Inc.
2008-12-29 02:28:49 +01:00
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
2008-12-29 02:28:49 +01:00
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
2008-12-29 02:28:49 +01:00
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2008-06-06 15:43:15 +02:00
#include "stdafx.h"
#include "jsobj.h"
2009-01-17 06:20:47 +01:00
#include "security.h"
2008-06-06 15:43:15 +02:00
#include "../util/goodies.h"
#include <limits>
#include "../util/unittest.h"
2008-06-06 15:43:15 +02:00
2009-01-14 23:09:51 +01:00
namespace mongo {
BSONElement nullElement;
ostream& operator<<( ostream &s, const OID &o ) {
s << o.str();
return s;
}
string BSONElement::toString() const {
stringstream s;
switch ( type() ) {
case EOO:
return "EOO";
case Date:
s << fieldName() << ": Date(" << hex << date() << ')';
break;
case RegEx:
2008-12-29 02:28:49 +01:00
{
s << fieldName() << ": /" << regex() << '/';
const char *p = regexFlags();
if ( p ) s << p;
2008-12-29 02:28:49 +01:00
}
break;
case NumberDouble:
case NumberInt:
s.precision( 16 );
s << fieldName() << ": " << number();
2009-01-14 23:17:24 +01:00
break;
case Bool:
s << fieldName() << ": " << ( boolean() ? "true" : "false" );
2009-01-14 23:17:24 +01:00
break;
case Object:
case Array:
s << fieldName() << ": " << embeddedObject().toString();
2009-01-14 23:17:24 +01:00
break;
case Undefined:
s << fieldName() << ": undefined";
2009-01-14 23:17:24 +01:00
break;
case jstNULL:
s << fieldName() << ": null";
2009-01-14 23:17:24 +01:00
break;
case MaxKey:
s << fieldName() << ": MaxKey";
2009-01-14 23:17:24 +01:00
break;
case MinKey:
s << fieldName() << ": MinKey";
2009-01-14 23:17:24 +01:00
break;
case CodeWScope:
s << fieldName() << ": CodeWScope( "
<< codeWScopeCode() << ", " << codeWScopeObject().toString() << ")";
2009-01-14 23:17:24 +01:00
break;
case Code:
s << fieldName() << ": ";
if ( valuestrsize() > 80 )
s << string(valuestr()).substr(0, 70) << "...";
else {
s << valuestr();
}
break;
case Symbol:
case String:
s << fieldName() << ": ";
if ( valuestrsize() > 80 )
s << '"' << string(valuestr()).substr(0, 70) << "...\"";
else {
s << '"' << valuestr() << '"';
2009-01-14 23:17:24 +01:00
}
break;
case DBRef:
s << fieldName();
s << " : DBRef('" << valuestr() << "',";
{
OID *x = (OID *) (valuestr() + valuestrsize());
s << *x << ')';
}
break;
case jstOID:
s << fieldName() << " : ObjId(";
s << oid() << ')';
break;
case BinData:
s << fieldName() << " : BinData";
break;
default:
s << fieldName() << ": ?type=" << type();
break;
2008-12-30 17:16:14 +01:00
}
return s.str();
2008-12-30 17:16:14 +01:00
}
string escape( string s ) {
stringstream ret;
for ( string::iterator i = s.begin(); i != s.end(); ++i ) {
switch ( *i ) {
case '"':
ret << "\\\"";
break;
case '\\':
ret << "\\\\";
break;
case '/':
ret << "\\/";
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;
}
}
2008-12-30 17:16:14 +01:00
}
return ret.str();
}
typedef boost::archive::iterators::base64_from_binary
< boost::archive::iterators::transform_width
< string::const_iterator, 6, 8 >
> base64_t;
string BSONElement::jsonString( JsonStringFormat format, bool includeFieldNames ) const {
stringstream s;
if ( includeFieldNames )
s << '"' << escape( fieldName() ) << "\" : ";
switch ( type() ) {
case String:
case Symbol:
s << '"' << escape( valuestr() ) << '"';
2008-12-30 17:16:14 +01:00
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( message.c_str(), false );
}
break;
case Bool:
s << ( boolean() ? "true" : "false" );
break;
case jstNULL:
s << "null";
break;
case Object:
s << embeddedObject().jsonString( format );
break;
case Array: {
if ( embeddedObject().isEmpty() ) {
s << "[]";
break;
}
s << "[ ";
BSONObjIterator i( embeddedObject() );
BSONElement e = i.next();
if ( !e.eoo() )
while ( 1 ) {
s << e.jsonString( format, false );
e = i.next();
if ( e.eoo() )
break;
s << ", ";
2009-01-14 23:17:24 +01:00
}
s << " ]";
break;
2009-01-14 23:17:24 +01:00
}
case DBRef: {
OID *x = (OID *) (valuestr() + valuestrsize());
if ( format == TenGen )
s << "Dbref( ";
else
s << "{ \"$ns\" : ";
s << '"' << valuestr() << "\", ";
if ( format != TenGen )
s << "\"$id\" : ";
s << '"' << *x << "\" ";
if ( format == TenGen )
s << ')';
else
s << '}';
break;
}
case jstOID:
if ( format == TenGen )
s << "ObjectId( ";
s << '"' << oid() << '"';
if ( format == TenGen )
s << " )";
break;
case BinData: {
int len = *(int *)( value() );
BinDataType type = BinDataType( *(char *)( (int *)( value() ) + 1 ) );
s << "{ \"$binary\" : \"";
char *start = ( char * )( value() ) + sizeof( int ) + 1;
string temp(start, len);
string base64 = string( base64_t( temp.begin() ), base64_t( temp.end() ) );
s << base64;
int padding = ( 4 - ( base64.length() % 4 ) ) % 4;
for ( int i = 0; i < padding; ++i )
s << '=';
s << "\", \"$type\" : \"" << hex;
s.width( 2 );
s.fill( '0' );
s << type << dec;
s << "\" }";
break;
}
case Date:
if ( format == Strict )
s << "{ \"$date\" : ";
else
s << "Date( ";
s << date();
if ( format == Strict )
s << " }";
else
s << " )";
break;
case RegEx:
if ( format == Strict )
s << "{ \"$regex\" : \"";
else
s << "/";
s << escape( regex() );
if ( format == Strict )
s << "\", \"$options\" : \"" << regexFlags() << "\" }";
else {
s << "/";
// 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;
default:
stringstream ss;
ss << "Cannot create a properly formatted JSON string with "
<< "element: " << toString() << " of type: " << type();
string message = ss.str();
massert( message.c_str(), false );
}
return s.str();
}
2008-12-29 02:28:49 +01:00
2009-01-16 21:13:46 +01:00
int BSONElement::size( int maxLen ) const {
if ( totalSize >= 0 )
return totalSize;
2008-12-29 02:28:49 +01:00
2009-01-16 21:13:46 +01:00
int remain = maxLen - fieldNameSize - 1;
int x = 0;
switch ( type() ) {
case EOO:
case Undefined:
case jstNULL:
case MaxKey:
case MinKey:
break;
case Bool:
2009-01-16 21:13:46 +01:00
x = 1;
break;
case NumberInt:
2009-01-16 21:13:46 +01:00
x = 4;
break;
case Date:
case NumberDouble:
2009-01-16 21:13:46 +01:00
x = 8;
break;
case jstOID:
2009-01-16 21:13:46 +01:00
x = 12;
break;
case Symbol:
case Code:
case String:
2009-01-16 21:13:46 +01:00
massert( "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 );
x = valuestrsize() + 4;
break;
case CodeWScope:
2009-01-16 21:13:46 +01:00
massert( "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 );
x = objsize();
break;
case DBRef:
2009-01-16 21:13:46 +01:00
massert( "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 );
x = valuestrsize() + 4 + 12;
break;
case Object:
case Array:
2009-01-16 21:13:46 +01:00
massert( "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 );
x = objsize();
break;
case BinData:
2009-01-16 21:13:46 +01:00
massert( "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 );
x = valuestrsize() + 4 + 1/*subtype*/;
break;
case RegEx:
{
const char *p = value();
2009-01-16 21:13:46 +01:00
int len1 = ( maxLen == -1 ) ? strlen( p ) : strnlen( p, remain );
massert( "Invalid regex string", len1 != -1 );
p = p + len1 + 1;
2009-01-16 21:13:46 +01:00
int len2 = ( maxLen == -1 ) ? strlen( p ) : strnlen( p, remain - len1 - 1 );
massert( "Invalid regex options string", len2 != -1 );
x = len1 + 1 + len2 + 1;
}
2008-12-29 02:28:49 +01:00
break;
2009-01-16 21:13:46 +01:00
default: {
stringstream ss;
ss << "BSONElement: bad type " << (int) type();
massert(ss.str().c_str(),false);
2008-12-29 02:28:49 +01:00
}
}
2009-01-16 21:13:46 +01:00
totalSize = x + fieldNameSize + 1; // BSONType
2008-12-29 02:28:49 +01:00
return totalSize;
}
2008-06-06 15:43:15 +02:00
int BSONElement::getGtLtOp() const {
const char *fn = fieldName();
if ( fn[0] == '$' && fn[1] ) {
if ( fn[2] == 't' ) {
if ( fn[1] == 'g' ) {
if ( fn[3] == 0 ) return JSMatcher::GT;
else if ( fn[3] == 'e' && fn[4] == 0 ) return JSMatcher::GTE;
}
else if ( fn[1] == 'l' ) {
if ( fn[3] == 0 ) return JSMatcher::LT;
else if ( fn[3] == 'e' && fn[4] == 0 ) return JSMatcher::LTE;
}
2008-12-29 02:28:49 +01:00
}
else if ( fn[2] == 'e' ) {
if ( fn[1] == 'n' && fn[3] == 0 )
return JSMatcher::NE;
2008-12-29 02:28:49 +01:00
}
else if ( fn[1] == 'i' && fn[2] == 'n' && fn[3] == 0 )
return JSMatcher::opIN;
2008-12-29 02:28:49 +01:00
}
return JSMatcher::Equality;
2008-12-29 02:28:49 +01:00
}
int BSONElement::woCompare( const BSONElement &e,
bool considerFieldName ) const {
int lt = (int) type();
if ( lt == NumberInt ) lt = NumberDouble;
int rt = (int) e.type();
if ( rt == NumberInt ) rt = NumberDouble;
2008-12-29 02:28:49 +01:00
int x = lt - rt;
2008-12-29 02:28:49 +01:00
if ( x != 0 )
return x;
if ( considerFieldName ) {
x = strcmp(fieldName(), e.fieldName());
if ( x != 0 )
return x;
}
x = compareElementValues(*this, e);
return x;
2008-12-29 02:28:49 +01:00
}
/* must be same type! */
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.type() - r.type();
if ( f<0 ) return -1;
return f==0 ? 0 : 1;
case Bool:
return *l.value() - *r.value();
case Date:
if ( l.date() < r.date() )
return -1;
return l.date() == r.date() ? 0 : 1;
case NumberInt:
case NumberDouble:
x = l.number() - r.number();
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:
case BinData: {
int lsz = l.valuesize();
int rsz = r.valuesize();
if ( lsz - rsz != 0 ) return lsz - rsz;
return memcmp(l.value(), r.value(), lsz);
}
case RegEx:
{
int c = strcmp(l.regex(), r.regex());
if ( c )
return c;
return strcmp(l.regexFlags(), r.regexFlags());
}
default:
out() << "compareElementValues: bad type " << (int) l.type() << endl;
assert(false);
}
return -1;
}
const char *BSONElement::simpleRegex() const {
if ( *regexFlags() )
return 0;
const char *i = regex();
if ( *i != '^' )
return 0;
++i;
// Empty string matches everything, won't limit our search.
if ( !*i )
return 0;
for( ; *i; ++i )
if (!( *i == ' ' || (*i>='0'&&*i<='9') || (*i>='@'&&*i<='Z') || (*i>='a'&&*i<='z') ))
return 0;
return regex() + 1;
}
2009-01-16 21:13:46 +01:00
void BSONElement::validate() const {
switch( type() ) {
case DBRef:
case Code:
2009-01-16 21:13:46 +01:00
case Symbol:
case String:
massert( "Invalid dbref/code/string/symbol size",
valuestrsize() > 0 &&
2009-01-16 21:13:46 +01:00
valuestrsize() - 1 == strnlen( valuestr(), valuestrsize() ) );
break;
case CodeWScope: {
int totalSize = *( int * )( value() );
massert( "Invalid CodeWScope size", totalSize >= 8 );
int strSizeWNull = *( int * )( value() + 4 );
massert( "Invalid CodeWScope string size", totalSize >= strSizeWNull + 4 + 4 );
massert( "Invalid CodeWScope string size",
strSizeWNull > 0 &&
strSizeWNull - 1 == strnlen( codeWScopeCode(), strSizeWNull ) );
massert( "Invalid CodeWScope size", totalSize >= strSizeWNull + 4 + 4 + 4 );
int objSize = *( int * )( value() + 4 + 4 + strSizeWNull );
massert( "Invalid CodeWScope object size", totalSize == 4 + 4 + strSizeWNull + objSize );
// Subobject validation handled elsewhere.
}
2009-01-16 21:13:46 +01:00
case Object:
// We expect Object size validation to be handled elsewhere.
default:
break;
}
}
/* JSMatcher --------------------------------------*/
2008-06-06 15:43:15 +02:00
// If the element is something like:
// a : { $gt : 3 }
// we append
// a : 3
// else we just append the element.
//
void appendElementHandlingGtLt(BSONObjBuilder& b, 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;
}
2008-12-29 02:28:49 +01:00
}
b.append(e);
2008-12-29 02:28:49 +01:00
}
2009-01-16 21:13:46 +01:00
int getGtLtOp(BSONElement& e) {
if ( e.type() != Object )
return JSMatcher::Equality;
2008-06-06 15:43:15 +02:00
BSONElement fe = e.embeddedObject().firstElement();
return fe.getGtLtOp();
}
2008-06-06 15:43:15 +02:00
/* BSONObj ------------------------------------------------------------*/
2008-06-06 15:43:15 +02:00
string BSONObj::toString() const {
if ( isEmpty() ) return "{}";
2008-12-29 02:28:49 +01:00
stringstream s;
s << "{ ";
BSONObjIterator i(*this);
2009-01-16 21:13:46 +01:00
bool first = true;
while ( 1 ) {
massert( "Object does not end with EOO or Undefined", i.more() );
BSONElement e = i.next( true );
2009-01-20 19:04:18 +01:00
massert( "Invalid element size", e.size() > 0 );
massert( "Element too large", e.size() < ( 1 << 30 ) );
int offset = e.rawdata() - this->objdata();
massert( "Element extends past end of object",
e.size() + offset <= this->objsize() );
2009-01-16 21:13:46 +01:00
e.validate();
2009-01-20 19:04:18 +01:00
bool end = ( e.size() + offset == this->objsize() );
2009-01-16 21:13:46 +01:00
if ( e.eoo() ) {
2009-01-20 19:04:18 +01:00
massert( "EOO Before end of object", end );
2009-01-16 21:13:46 +01:00
break;
} else if ( e.type() == Undefined ) {
2009-01-20 19:04:18 +01:00
massert( "Undefined Before end of object", end );
2009-01-16 21:13:46 +01:00
break;
}
2009-01-16 21:13:46 +01:00
if ( first )
first = false;
else
s << ", ";
s << e.toString();
}
s << " }";
return s.str();
}
string BSONObj::jsonString( JsonStringFormat format ) const {
if ( isEmpty() ) return "{}";
stringstream s;
s << "{ ";
BSONObjIterator i(*this);
BSONElement e = i.next();
if ( !e.eoo() )
while ( 1 ) {
s << e.jsonString( format );
e = i.next();
if ( e.eoo() )
break;
s << ", ";
}
s << " }";
return s.str();
}
// todo: can be a little faster if we don't use toString() here.
bool BSONObj::valid() const {
try {
toString();
}
catch (...) {
return false;
2008-12-30 17:16:14 +01:00
}
return true;
}
/* 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;
2008-12-30 17:16:14 +01:00
bool ordered = !idxKey.isEmpty();
2009-01-14 23:17:24 +01:00
BSONObjIterator i(*this);
BSONObjIterator j(r);
BSONObjIterator k(idxKey);
2008-12-30 17:16:14 +01:00
while ( 1 ) {
// so far, equal...
BSONElement l = i.next();
BSONElement r = j.next();
BSONElement o;
if ( ordered )
o = k.next();
if ( l.eoo() )
return 0;
int x = l.woCompare( r, considerFieldName );
if ( ordered && o.number() < 0 )
x = -x;
if ( x != 0 )
return x;
2008-12-29 02:28:49 +01:00
}
return -1;
}
2008-06-06 15:43:15 +02:00
BSONElement BSONObj::getField(const char *name) const {
if ( details ) {
BSONObjIterator i(*this);
while ( i.more() ) {
BSONElement e = i.next();
if ( e.eoo() )
break;
if ( strcmp(e.fieldName(), name) == 0 )
return e;
}
}
return nullElement;
2008-12-29 02:28:49 +01:00
}
2008-06-06 15:43:15 +02:00
/* return has eoo() true if no match
supports "." notation to reach into embedded objects
*/
BSONElement BSONObj::getFieldDotted(const char *name) const {
BSONElement e = getField( name );
if ( e.eoo() ) {
const char *p = strchr(name, '.');
if ( p ) {
string left(name, p-name);
BSONObj sub = getObjectField(left.c_str());
return sub.isEmpty() ? nullElement : sub.getFieldDotted(p+1);
}
}
return e;
/*
BSONObjIterator i(*this);
while( i.more() ) {
BSONElement e = i.next();
if( e.eoo() )
break;
if( strcmp(e.fieldName(), name) == 0 )
return e;
}
return nullElement;
*/
}
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
return sub.embeddedObject().getFieldDottedOrArray( name );
}
/* makes a new BSONObj with the fields specified in pattern.
fields returned in the order they appear in pattern.
if any field missing, you get back an empty object overall.
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, BSONObjBuilder& b, const char *&nameWithinArray) const {
nameWithinArray = "";
BSONObjIterator i(pattern);
while ( i.more() ) {
BSONElement e = i.next();
if ( e.eoo() )
break;
const char *name = e.fieldName();
BSONElement x = getFieldDottedOrArray( name );
if ( x.eoo() ) {
nameWithinArray = "";
return BSONObj();
} else if ( x.type() == Array ) {
// NOTE: Currently set based on last array discovered.
nameWithinArray = name;
}
b.appendAs(x, "");
}
return b.done();
}
BSONObj BSONObj::extractFieldsUnDotted(BSONObj pattern) {
BSONObjBuilder b;
BSONObjIterator i(pattern);
2008-12-29 02:28:49 +01:00
while ( i.more() ) {
2008-10-21 22:13:48 +02:00
BSONElement e = i.next();
2008-12-29 02:28:49 +01:00
if ( e.eoo() )
break;
BSONElement x = getField(e.fieldName());
if ( x.eoo() )
return BSONObj();
b.appendAs(x, "");
}
return b.doneAndDecouple();
}
2008-10-09 23:06:56 +02:00
BSONObj BSONObj::extractFields(BSONObj& pattern) {
BSONObjBuilder b(32); // scanandorder.h can make a zillion of these, so we start the allocation very small
BSONObjIterator i(pattern);
while ( i.more() ) {
BSONElement e = i.next();
if ( e.eoo() )
break;
BSONElement x = getFieldDotted(e.fieldName());
if ( x.eoo() )
return BSONObj();
b.append(x);
}
return b.doneAndDecouple();
2008-12-29 02:28:49 +01:00
}
2008-08-22 00:05:13 +02:00
int BSONObj::getIntField(const char *name) const {
BSONElement e = getField(name);
return e.isNumber() ? (int) e.number() : INT_MIN;
2008-12-29 02:28:49 +01:00
}
2008-11-17 21:39:03 +01:00
bool BSONObj::getBoolField(const char *name) {
BSONElement e = getField(name);
return e.type() == Bool ? e.boolean() : false;
2008-12-29 02:28:49 +01:00
}
2008-06-06 15:43:15 +02:00
const char * BSONObj::getStringField(const char *name) {
BSONElement e = getField(name);
return e.type() == String ? e.valuestr() : "";
}
2008-12-29 02:28:49 +01:00
BSONObj BSONObj::getObjectField(const char *name) const {
BSONElement e = getField(name);
BSONType t = e.type();
return t == Object || t == Array ? e.embeddedObject() : BSONObj();
}
2008-12-29 02:28:49 +01:00
int BSONObj::nFields() {
int n = 0;
BSONObjIterator i(*this);
while ( i.more() ) {
BSONElement e = i.next();
if ( e.eoo() )
2008-12-29 02:28:49 +01:00
break;
n++;
2008-12-29 02:28:49 +01:00
}
return n;
2008-12-29 02:28:49 +01:00
}
/* grab names of all the fields in this object */
int BSONObj::getFieldNames(set<string>& fields) {
int n = 0;
BSONObjIterator i(*this);
while ( i.more() ) {
BSONElement e = i.next();
if ( e.eoo() )
break;
fields.insert(e.fieldName());
n++;
}
return n;
2008-12-29 02:28:49 +01:00
}
/* 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( details == 0 ); /* partial implementation for now... */
BSONObjBuilder b;
int N = fields.size();
int n = 0;
BSONObjIterator i(from);
bool gotId = false;
while ( i.more() ) {
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 );
}
2008-06-06 15:43:15 +02:00
return n;
}
BSONObj BSONObj::clientReadable() const {
BSONObjBuilder b;
BSONObjIterator i( *this );
while( i.more() ) {
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.doneAndDecouple();
}
BSONObj BSONObj::replaceFieldNames( const vector< string > &names ) const {
BSONObjBuilder b;
BSONObjIterator i( *this );
vector< string >::const_iterator j = names.begin();
while( i.more() ) {
BSONElement e = i.next();
if ( e.eoo() )
break;
if ( j != names.end() ) {
b.appendAs( e, j->c_str() );
++j;
} else {
b.append( e );
}
}
return b.doneAndDecouple();
}
2009-01-16 16:21:23 +01:00
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();
}
ostream& operator<<( ostream &s, const BSONObj &o ) {
return s << o.toString();
}
/*-- test things ----------------------------------------------------*/
2008-06-06 15:43:15 +02:00
2008-08-11 20:53:08 +02:00
#pragma pack(push,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;
2008-09-09 16:19:31 +02:00
#pragma pack(pop)
2008-06-06 15:43:15 +02:00
BSONElement::BSONElement() {
data = &js0.eoo;
fieldNameSize = 0;
totalSize = -1;
}
2008-06-06 15:43:15 +02:00
2008-09-09 16:19:31 +02:00
#pragma pack(push,1)
struct EmptyObject {
EmptyObject() {
len = 5;
jstype = EOO;
}
int len;
char jstype;
} emptyObject;
2008-06-06 15:43:15 +02:00
#pragma pack(pop)
BSONObj emptyObj((char *) &emptyObject);
2008-09-09 16:19:31 +02:00
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 run() {
testRegex();
BSONObjBuilder A,B,C;
A.appendInt("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 );
}
} bson_unittest;
BSONObjBuilderValueStream::BSONObjBuilderValueStream( const char * fieldName , BSONObjBuilder * builder ) {
_fieldName = fieldName;
_builder = builder;
}
BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const char * value ) {
_builder->append( _fieldName , value );
return *_builder;
}
BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const int value ) {
_builder->appendInt( _fieldName , value );
return *_builder;
}
BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const double value ) {
_builder->append( _fieldName , value );
return *_builder;
}
2009-01-16 20:21:46 +01:00
void OID::init(){
// note: this isn't correct - but we're probably deprecating anyway, so not worrying so much right now
2009-01-17 06:20:47 +01:00
static unsigned long long machine = security.getNonce();
2009-01-18 17:53:33 +01:00
static int inc = (int)security.getNonce();
2009-01-16 20:21:46 +01:00
a = time(0);
a = a << 32;
a += machine & 0xffffffff;
b = ++inc;
}
2009-01-17 06:20:47 +01:00
void OID::init( string s ){
assert( s.size() == 24 );
string as = s.substr( 0 , 16 );
string bs = s.substr( 16 );
stringstream ssa(as);
ssa >> hex >> a;
stringstream ssb(bs);
ssb >> hex >> b;
}
2009-01-14 23:09:51 +01:00
} // namespace mongo