Files
mongo/db/jsobj.cpp

1258 lines
40 KiB
C++
Raw Normal View History

/** @file jsobj.cpp - BSON implementation
http://www.mongodb.org/display/DOCS/BSON
*/
2008-06-06 09:43:15 -04:00
/* 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.
*/
2010-04-27 15:27:52 -04:00
#include "pch.h"
#include "../bson/oid.h"
2008-06-06 09:43:15 -04:00
#include "jsobj.h"
2009-01-23 11:28:29 -05:00
#include "nonce.h"
2010-04-24 19:24:07 -04:00
#include "../bson/util/atomic_int.h"
2009-10-01 08:34:50 -04:00
#include "../util/base64.h"
#include "../util/md5.hpp"
#include <limits>
#include "../util/unittest.h"
#include "../util/embedded_builder.h"
2011-06-25 23:08:33 -04:00
#include "../util/stringutils.h"
2009-02-24 18:53:34 -05:00
#include "json.h"
2009-05-11 14:57:26 -04:00
#include "jsobjmanipulator.h"
#include "../util/optime.h"
2009-10-13 17:14:32 -04:00
#include <boost/static_assert.hpp>
#undef assert
2010-04-23 20:09:32 -04:00
#define assert MONGO_assert
2009-10-13 17:14:32 -04:00
// make sure our assumptions are valid
2010-12-15 13:07:09 -05:00
BOOST_STATIC_ASSERT( sizeof(short) == 2 );
2009-10-13 17:14:32 -04:00
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 );
2009-10-14 13:21:25 -04:00
BOOST_STATIC_ASSERT( sizeof(mongo::OID) == 12 );
2008-06-06 09:43:15 -04:00
2009-01-14 17:09:51 -05:00
namespace mongo {
BSONElement eooElement;
2010-04-24 15:45:50 -04:00
GENOIDLabeler GENOID;
2009-10-13 12:33:25 -04:00
DateNowLabeler DATENOW;
MinKeyLabeler MINKEY;
MaxKeyLabeler MAXKEY;
2011-06-28 01:44:43 -04:00
// need to move to bson/, but has dependency on base64 so move that to bson/util/ first.
inline string BSONElement::jsonString( JsonStringFormat format, bool includeFieldNames, int pretty ) const {
2010-04-24 22:23:36 -04:00
BSONType t = type();
if ( t == Undefined )
2011-05-13 15:27:25 -04:00
return "undefined";
2010-04-24 22:23:36 -04:00
stringstream s;
if ( includeFieldNames )
s << '"' << escape( fieldName() ) << "\" : ";
switch ( type() ) {
2010-04-21 14:41:09 -04:00
case mongo::String:
case Symbol:
s << '"' << escape( string(valuestr(), valuestrsize()-1) ) << '"';
2008-12-30 11:16:14 -05:00
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();
2011-01-04 00:40:41 -05:00
}
else {
2010-07-27 15:33:27 -04:00
StringBuilder ss;
ss << "Number " << number() << " cannot be represented in JSON";
string message = ss.str();
massert( 10311 , message.c_str(), false );
}
break;
2010-04-21 14:41:09 -04:00
case mongo::Bool:
s << ( boolean() ? "true" : "false" );
break;
case jstNULL:
s << "null";
break;
case Object:
2010-04-19 21:05:46 -04:00
s << embeddedObject().jsonString( format, pretty );
break;
2010-04-21 16:14:28 -04:00
case mongo::Array: {
if ( embeddedObject().isEmpty() ) {
s << "[]";
break;
}
s << "[ ";
BSONObjIterator i( embeddedObject() );
BSONElement e = i.next();
2011-05-13 15:27:25 -04:00
if ( !e.eoo() ) {
int count = 0;
while ( 1 ) {
2010-04-20 12:29:00 -04:00
if( pretty ) {
s << '\n';
for( int x = 0; x < pretty; x++ )
s << " ";
}
2011-05-13 15:27:25 -04:00
if (strtol(e.fieldName(), 0, 10) > count) {
s << "undefined";
}
else {
s << e.jsonString( format, false, pretty?pretty+1:0 );
e = i.next();
}
count++;
if ( e.eoo() )
break;
s << ", ";
2009-01-14 17:17:24 -05:00
}
2011-05-13 15:27:25 -04:00
}
s << " ]";
break;
2009-01-14 17:17:24 -05:00
}
case DBRef: {
2010-04-21 15:03:05 -04:00
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( ";
2011-01-04 00:40:41 -05:00
}
else {
s << "{ \"$oid\" : ";
}
2009-01-25 19:53:51 -05:00
s << '"' << __oid() << '"';
if ( format == TenGen ) {
s << " )";
2011-01-04 00:40:41 -05:00
}
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;
2009-10-01 08:34:50 -04:00
base64::encode( s , start , len );
s << "\", \"$type\" : \"" << hex;
s.width( 2 );
s.fill( '0' );
s << type << dec;
s << "\" }";
break;
}
2010-04-21 14:41:09 -04:00
case mongo::Date:
if ( format == Strict )
s << "{ \"$date\" : ";
else
s << "Date( ";
2010-04-20 15:30:37 -04:00
if( pretty ) {
Date_t d = date();
if( d == 0 ) s << '0';
else
s << '"' << date().toString() << '"';
2011-01-04 00:40:41 -05:00
}
else
2010-04-20 15:30:37 -04:00
s << date();
if ( format == Strict )
s << " }";
else
s << " )";
break;
case RegEx:
2011-01-04 00:40:41 -05:00
if ( format == Strict ) {
s << "{ \"$regex\" : \"" << escape( regex() );
s << "\", \"$options\" : \"" << regexFlags() << "\" }";
2011-01-04 00:40:41 -05:00
}
else {
s << "/" << escape( regex() , true ) << "/";
// FIXME Worry about alpha order?
2011-01-04 00:40:41 -05:00
for ( const char *f = regexFlags(); *f; ++f ) {
switch ( *f ) {
case 'g':
case 'i':
case 'm':
s << *f;
default:
break;
}
}
}
break;
2010-01-16 10:39:51 -05:00
2010-05-03 16:36:04 -04:00
case CodeWScope: {
BSONObj scope = codeWScopeObject();
2011-01-04 00:40:41 -05:00
if ( ! scope.isEmpty() ) {
2010-05-03 16:36:04 -04:00
s << "{ \"$code\" : " << _asCode() << " , "
<< " \"$scope\" : " << scope.jsonString() << " }";
break;
}
}
2010-01-16 10:39:51 -05:00
case Code:
2010-04-24 17:14:44 -04:00
s << _asCode();
2010-01-16 10:39:51 -05:00
break;
2011-01-04 00:40:41 -05:00
2010-01-19 10:16:45 -05:00
case Timestamp:
s << "{ \"t\" : " << timestampTime() << " , \"i\" : " << timestampInc() << " }";
break;
2010-04-24 22:48:09 -04:00
case MinKey:
s << "{ \"$minKey\" : 1 }";
break;
case MaxKey:
s << "{ \"$maxKey\" : 1 }";
break;
default:
2010-07-27 15:33:27 -04:00
StringBuilder ss;
ss << "Cannot create a properly formatted JSON string with "
2011-01-04 00:40:41 -05:00
<< "element: " << toString() << " of type: " << type();
string message = ss.str();
massert( 10312 , message.c_str(), false );
}
return s.str();
}
2008-12-28 20:28:49 -05:00
2009-10-12 12:58:43 -04:00
int BSONElement::getGtLtOp( int def ) const {
const char *fn = fieldName();
if ( fn[0] == '$' && fn[1] ) {
if ( fn[2] == 't' ) {
if ( fn[1] == 'g' ) {
2009-05-11 14:57:26 -04:00
if ( fn[3] == 0 ) return BSONObj::GT;
else if ( fn[3] == 'e' && fn[4] == 0 ) return BSONObj::GTE;
}
else if ( fn[1] == 'l' ) {
2009-05-11 14:57:26 -04:00
if ( fn[3] == 0 ) return BSONObj::LT;
else if ( fn[3] == 'e' && fn[4] == 0 ) return BSONObj::LTE;
}
2008-12-28 20:28:49 -05:00
}
2011-01-04 00:40:41 -05:00
else if ( fn[1] == 'n' && fn[2] == 'e' ) {
2010-02-25 16:24:34 -05:00
if ( fn[3] == 0 )
return BSONObj::NE;
2010-09-02 14:58:10 -04:00
if ( fn[3] == 'a' && fn[4] == 'r') // matches anything with $near prefix
2010-02-25 16:24:34 -05:00
return BSONObj::opNEAR;
}
2011-01-04 00:40:41 -05:00
else if ( fn[1] == 'm' ) {
2010-04-01 17:17:32 -04:00
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;
}
2009-10-12 11:58:14 -04:00
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 )
2009-05-11 14:57:26 -04:00
return BSONObj::opIN;
2009-04-13 19:07:10 -04:00
else if ( fn[1] == 'n' && fn[2] == 'i' && fn[3] == 'n' && fn[4] == 0 )
2009-05-11 14:57:26 -04:00
return BSONObj::NIN;
2009-04-06 11:27:43 -04:00
else if ( fn[1] == 'a' && fn[2] == 'l' && fn[3] == 'l' && fn[4] == 0 )
2009-05-11 14:57:26 -04:00
return BSONObj::opALL;
2009-03-25 14:47:04 -04:00
else if ( fn[1] == 's' && fn[2] == 'i' && fn[3] == 'z' && fn[4] == 'e' && fn[5] == 0 )
2009-05-11 14:57:26 -04:00
return BSONObj::opSIZE;
2011-01-04 00:40:41 -05:00
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;
2010-03-15 23:50:14 -04:00
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;
2008-12-28 20:28:49 -05:00
}
2009-10-12 12:58:43 -04:00
return def;
2008-12-28 20:28:49 -05:00
}
2010-01-19 11:04:52 -05:00
/* Matcher --------------------------------------*/
2008-06-06 09:43:15 -04:00
// If the element is something like:
// a : { $gt : 3 }
// we append
// a : 3
// else we just append the element.
//
2009-03-20 16:20:46 -04:00
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;
}
2008-12-28 20:28:49 -05:00
}
b.append(e);
2008-12-28 20:28:49 -05:00
}
2009-09-14 16:55:57 -04:00
2009-03-20 16:20:46 -04:00
int getGtLtOp(const BSONElement& e) {
if ( e.type() != Object )
2009-05-11 14:57:26 -04:00
return BSONObj::Equality;
BSONElement fe = e.embeddedObject().firstElement();
return fe.getGtLtOp();
}
2008-06-06 09:43:15 -04:00
2011-01-04 00:40:41 -05:00
FieldCompareResult compareDottedFieldNames( const string& l , const string& r ) {
2010-08-22 16:32:14 -04:00
static int maxLoops = 1024 * 1024;
2011-01-04 00:40:41 -05:00
size_t lstart = 0;
size_t rstart = 0;
2010-08-22 16:32:14 -04:00
2011-01-04 00:40:41 -05:00
for ( int i=0; i<maxLoops; i++ ) {
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;
}
2010-08-22 16:32:14 -04:00
log() << "compareDottedFieldNames ERROR l: " << l << " r: " << r << " TOO MANY LOOPS" << endl;
assert(0);
return SAME; // will never get here
}
/* BSONObj ------------------------------------------------------------*/
2008-06-06 09:43:15 -04:00
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 );
}
2010-04-19 21:05:46 -04:00
string BSONObj::jsonString( JsonStringFormat format, int pretty ) const {
2009-02-26 12:02:43 -05:00
if ( isEmpty() ) return "{}";
2010-07-27 15:33:27 -04:00
StringBuilder s;
s << "{ ";
BSONObjIterator i(*this);
BSONElement e = i.next();
if ( !e.eoo() )
while ( 1 ) {
2010-04-20 12:29:00 -04:00
s << e.jsonString( format, true, pretty?pretty+1:0 );
e = i.next();
if ( e.eoo() )
break;
2010-04-19 23:24:57 -04:00
s << ",";
if ( pretty ) {
2010-04-20 12:29:00 -04:00
s << '\n';
2010-04-19 23:24:57 -04:00
for( int x = 0; x < pretty; x++ )
s << " ";
}
else {
s << " ";
}
}
s << " }";
return s.str();
}
bool BSONObj::valid() const {
2011-01-04 00:40:41 -05:00
try {
2010-04-07 13:48:56 -04:00
BSONObjIterator it(*this);
2011-01-04 00:40:41 -05:00
while( it.moreWithEOO() ) {
2010-04-07 00:57:46 -04:00
// both throw exception on failure
2010-04-07 00:07:28 -04:00
BSONElement e = it.next(true);
2010-04-07 00:57:46 -04:00
e.validate();
2010-04-07 00:07:28 -04:00
2011-01-04 00:40:41 -05:00
if (e.eoo()) {
2010-04-07 00:07:28 -04:00
if (it.moreWithEOO())
return false;
2010-04-07 13:48:56 -04:00
return true;
2011-01-04 00:40:41 -05:00
}
else if (e.isABSONObj()) {
2010-04-07 13:48:56 -04:00
if(!e.embeddedObject().valid())
return false;
2011-01-04 00:40:41 -05:00
}
else if (e.type() == CodeWScope) {
2010-04-07 13:48:56 -04:00
if(!e.codeWScopeObject().valid())
return false;
2010-04-07 00:07:28 -04:00
}
}
2011-01-04 00:40:41 -05:00
}
catch (...) {
2008-12-30 11:16:14 -05:00
}
2010-04-07 14:12:28 -04:00
return false;
}
2011-01-04 00:40:41 -05:00
int BSONObj::woCompare(const BSONObj& r, const Ordering &o, bool considerFieldName) const {
2010-04-20 18:12:30 -04:00
if ( isEmpty() )
return r.isEmpty() ? 0 : -1;
if ( r.isEmpty() )
return 1;
BSONObjIterator i(*this);
BSONObjIterator j(r);
2010-04-20 19:42:53 -04:00
unsigned mask = 1;
2010-04-20 18:12:30 -04:00
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 );
2010-04-20 19:42:53 -04:00
if( o.descending(mask) )
2010-04-20 18:12:30 -04:00
x = -x;
}
if ( x != 0 )
return x;
2010-04-20 23:11:35 -04:00
mask <<= 1;
2010-04-20 18:12:30 -04:00
}
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;
2008-12-30 11:16:14 -05:00
bool ordered = !idxKey.isEmpty();
2009-01-14 17:17:24 -05:00
BSONObjIterator i(*this);
BSONObjIterator j(r);
BSONObjIterator k(idxKey);
2008-12-30 11:16:14 -05:00
while ( 1 ) {
// so far, equal...
BSONElement l = i.next();
BSONElement r = j.next();
BSONElement o;
if ( ordered )
o = k.next();
if ( l.eoo() )
2009-02-26 11:20:19 -05:00
return r.eoo() ? 0 : -1;
if ( r.eoo() )
return 1;
2010-02-01 12:19:51 -05:00
int x;
2011-01-04 00:40:41 -05:00
/*
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*/ {
2010-02-01 12:19:51 -05:00
x = l.woCompare( r, considerFieldName );
if ( ordered && o.number() < 0 )
x = -x;
}
if ( x != 0 )
return x;
2008-12-28 20:28:49 -05:00
}
return -1;
}
2008-06-06 09:43:15 -04:00
2009-02-24 18:53:34 -05:00
BSONObj staticNull = fromjson( "{'':null}" );
BSONObj makeUndefined() {
BSONObjBuilder b;
b.appendUndefined( "" );
return b.obj();
}
BSONObj staticUndefined = makeUndefined();
2009-09-14 16:55:57 -04:00
/* well ordered compare */
2011-01-04 00:40:41 -05:00
int BSONObj::woSortOrder(const BSONObj& other, const BSONObj& sortKey , bool useDotted ) const {
if ( isEmpty() )
return other.isEmpty() ? 0 : -1;
if ( other.isEmpty() )
return 1;
uassert( 10060 , "woSortOrder needs a non-empty sortKey" , ! sortKey.isEmpty() );
2009-09-14 16:55:57 -04:00
BSONObjIterator i(sortKey);
2011-01-04 00:40:41 -05:00
while ( 1 ) {
BSONElement f = i.next();
if ( f.eoo() )
return 0;
2009-09-14 16:55:57 -04:00
BSONElement l = useDotted ? getFieldDotted( f.fieldName() ) : getField( f.fieldName() );
if ( l.eoo() )
2009-02-24 18:53:34 -05:00
l = staticNull.firstElement();
BSONElement r = useDotted ? other.getFieldDotted( f.fieldName() ) : other.getField( f.fieldName() );
if ( r.eoo() )
2009-02-24 18:53:34 -05:00
r = staticNull.firstElement();
2009-09-14 16:55:57 -04:00
2009-02-24 18:53:34 -05:00
int x = l.woCompare( r, false );
if ( f.number() < 0 )
x = -x;
if ( x != 0 )
return x;
}
return -1;
}
template <typename BSONElementColl>
void _getFieldsDotted( const BSONObj* obj, const StringData& name, BSONElementColl &ret, bool expandLastArray ) {
BSONElement e = obj->getField( name );
2010-03-29 16:23:34 -04:00
if ( e.eoo() ) {
2010-07-18 15:04:01 -04:00
const char *p = strchr(name.data(), '.');
2010-03-29 16:23:34 -04:00
if ( p ) {
2010-07-18 15:04:01 -04:00
string left(name.data(), p-name.data());
2010-03-29 16:23:34 -04:00
const char* next = p+1;
BSONElement e = obj->getField( left.c_str() );
2010-03-29 16:23:34 -04:00
2011-01-04 00:40:41 -05:00
if (e.type() == Object) {
e.embeddedObject().getFieldsDotted(next, ret, expandLastArray );
2011-01-04 00:40:41 -05:00
}
else if (e.type() == Array) {
2010-03-29 16:23:34 -04:00
bool allDigits = false;
2011-01-04 00:40:41 -05:00
if ( isdigit( *next ) ) {
2010-03-29 16:23:34 -04:00
const char * temp = next + 1;
while ( isdigit( *temp ) )
temp++;
allDigits = (*temp == '.' || *temp == '\0');
}
2010-03-29 16:23:34 -04:00
if (allDigits) {
e.embeddedObject().getFieldsDotted(next, ret, expandLastArray );
2011-01-04 00:40:41 -05:00
}
else {
2010-03-29 16:23:34 -04:00
BSONObjIterator i(e.embeddedObject());
2011-01-04 00:40:41 -05:00
while ( i.more() ) {
2010-03-29 16:23:34 -04:00
BSONElement e2 = i.next();
if (e2.type() == Object || e2.type() == Array)
e2.embeddedObject().getFieldsDotted(next, ret, expandLastArray );
2010-03-29 16:23:34 -04:00
}
}
2011-01-04 00:40:41 -05:00
}
else {
2010-03-29 16:23:34 -04:00
// do nothing: no match
}
2010-03-22 13:37:56 -04:00
}
2011-01-04 00:40:41 -05:00
}
else {
if (e.type() == Array && expandLastArray) {
2010-03-29 16:23:34 -04:00
BSONObjIterator i(e.embeddedObject());
while ( i.more() )
ret.insert(i.next());
2011-01-04 00:40:41 -05:00
}
else {
2010-03-29 16:23:34 -04:00
ret.insert(e);
}
}
2009-09-14 16:55:57 -04:00
}
void BSONObj::getFieldsDotted(const StringData& name, BSONElementSet &ret, bool expandLastArray ) const {
_getFieldsDotted( this, name, ret, expandLastArray );
}
void BSONObj::getFieldsDotted(const StringData& name, BSONElementMSet &ret, bool expandLastArray ) const {
_getFieldsDotted( this, name, ret, expandLastArray );
}
BSONElement BSONObj::getFieldDottedOrArray(const char *&name) const {
const char *p = strchr(name, '.');
2011-01-31 00:12:04 -05:00
BSONElement sub;
if ( p ) {
2011-01-31 00:12:04 -05:00
sub = getField( string(name, p-name) );
name = p + 1;
2011-01-04 00:40:41 -05:00
}
else {
2011-01-31 00:12:04 -05:00
sub = getField( name );
name = name + strlen(name);
}
2011-01-31 00:12:04 -05:00
if ( sub.eoo() )
return eooElement;
else if ( sub.type() == Array || name[0] == '\0' )
return sub;
2009-01-25 22:25:01 -05:00
else if ( sub.type() == Object )
return sub.embeddedObject().getFieldDottedOrArray( name );
2009-01-25 22:25:01 -05:00
else
return eooElement;
}
/**
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() ) {
2008-10-21 16:13:48 -04:00
BSONElement e = i.next();
2008-12-28 20:28:49 -05:00
if ( e.eoo() )
break;
BSONElement x = getField(e.fieldName());
if ( !x.eoo() )
b.appendAs(x, "");
}
2009-02-09 13:04:32 -05:00
return b.obj();
}
2008-10-09 17:06:56 -04:00
2009-09-10 15:45:31 -04:00
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() );
2009-09-10 15:45:31 -04:00
else if ( fillWithNull )
b.appendNull( e.fieldName() );
}
2009-02-09 13:04:32 -05:00
return b.obj();
2008-12-28 20:28:49 -05:00
}
2008-08-21 18:05:13 -04:00
2009-03-02 11:31:13 -05:00
BSONObj BSONObj::filterFieldsUndotted( const BSONObj &filter, bool inFilter ) const {
BSONObjBuilder b;
BSONObjIterator i( *this );
while( i.moreWithEOO() ) {
2009-03-02 11:31:13 -05:00
BSONElement e = i.next();
if ( e.eoo() )
break;
BSONElement x = filter.getField( e.fieldName() );
if ( ( x.eoo() && !inFilter ) ||
2011-01-04 00:40:41 -05:00
( !x.eoo() && inFilter ) )
2009-03-02 11:31:13 -05:00
b.append( e );
}
return b.obj();
}
2009-09-14 16:55:57 -04:00
2009-03-02 16:03:48 -05:00
BSONElement BSONObj::getFieldUsingIndexNames(const char *fieldName, const BSONObj &indexKey) const {
BSONObjIterator i( indexKey );
int j = 0;
while( i.moreWithEOO() ) {
2009-03-02 16:03:48 -05:00
BSONElement f = i.next();
if ( f.eoo() )
return BSONElement();
if ( strcmp( f.fieldName(), fieldName ) == 0 )
break;
++j;
}
BSONObjIterator k( *this );
while( k.moreWithEOO() ) {
2009-03-02 16:03:48 -05:00
BSONElement g = k.next();
if ( g.eoo() )
return BSONElement();
if ( j == 0 ) {
return g;
}
--j;
}
return BSONElement();
}
2009-09-14 16:55:57 -04:00
/* 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;
2008-12-28 20:28:49 -05: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) {
2009-03-19 16:23:04 -04:00
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;
2011-01-04 00:40:41 -05:00
}
else if ( strcmp(fname, "_id")==0 ) {
b.append(e);
gotId = true;
if ( n == N && gotId )
break;
}
}
if ( n ) {
*this = b.obj();
}
2008-06-06 09:43:15 -04:00
return n;
}
bool BSONObj::couldBeArray() const {
BSONObjIterator i( *this );
int index = 0;
while( i.moreWithEOO() ){
BSONElement e = i.next();
if( e.eoo() ) break;
// TODO: If actually important, may be able to do int->char* much faster
if( strcmp( e.fieldName(), ((string)( str::stream() << index )).c_str() ) != 0 )
return false;
index++;
}
return true;
}
BSONObj BSONObj::clientReadable() const {
BSONObjBuilder b;
BSONObjIterator i( *this );
while( i.moreWithEOO() ) {
BSONElement e = i.next();
if ( e.eoo() )
break;
switch( e.type() ) {
2011-01-04 00:40:41 -05:00
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 );
}
}
2009-02-09 13:04:32 -05:00
return b.obj();
}
2009-09-14 16:55:57 -04:00
2009-02-25 15:31:38 -05:00
BSONObj BSONObj::replaceFieldNames( const BSONObj &names ) const {
BSONObjBuilder b;
BSONObjIterator i( *this );
2009-02-25 15:31:38 -05:00
BSONObjIterator j( names );
BSONElement f = j.moreWithEOO() ? j.next() : BSONObj().firstElement();
while( i.moreWithEOO() ) {
BSONElement e = i.next();
if ( e.eoo() )
break;
2009-02-25 15:31:38 -05:00
if ( !f.eoo() ) {
b.appendAs( e, f.fieldName() );
f = j.next();
2011-01-04 00:40:41 -05:00
}
else {
b.append( e );
}
}
2009-02-09 13:04:32 -05:00
return b.obj();
}
2009-09-14 16:55:57 -04:00
bool BSONObj::okForStorage() const {
BSONObjIterator i( *this );
2011-01-04 00:40:41 -05:00
while ( i.more() ) {
BSONElement e = i.next();
const char * name = e.fieldName();
2011-01-04 00:40:41 -05:00
if ( strchr( name , '.' ) ||
2011-01-04 00:40:41 -05:00
strchr( name , '$' ) ) {
return
2010-02-11 11:36:08 -05:00
strcmp( name , "$ref" ) == 0 ||
strcmp( name , "$id" ) == 0
;
}
2011-01-04 00:40:41 -05:00
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 );
}
2011-01-04 00:40:41 -05:00
}
}
return true;
}
2010-04-24 18:25:58 -04:00
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++;
}
}
2011-01-04 00:40:41 -05:00
void nested2dotted(BSONObjBuilder& b, const BSONObj& obj, const string& base) {
BSONObjIterator it(obj);
2011-01-04 00:40:41 -05:00
while (it.more()) {
BSONElement e = it.next();
2011-01-04 00:40:41 -05:00
if (e.type() == Object) {
string newbase = base + e.fieldName() + ".";
nested2dotted(b, e.embeddedObject(), newbase);
2011-01-04 00:40:41 -05:00
}
else {
string newbase = base + e.fieldName();
2010-07-20 12:39:35 -04:00
b.appendAs(e, newbase);
}
}
}
2011-01-04 00:40:41 -05:00
void dotted2nested(BSONObjBuilder& b, const BSONObj& obj) {
//use map to sort fields
BSONMap sorted = bson2map(obj);
EmbeddedBuilder eb(&b);
2011-01-04 00:40:41 -05:00
for(BSONMap::const_iterator it=sorted.begin(); it!=sorted.end(); ++it) {
eb.appendAs(it->second, it->first);
}
eb.done();
}
/*-- test things ----------------------------------------------------*/
2008-06-06 09:43:15 -04:00
#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);
2011-01-04 00:40:41 -05:00
/*
struct JSObj0 {
JSObj0() {
totsize = 5;
eoo = EOO;
}
int totsize;
char eoo;
} js0;
*/
#pragma pack()
2008-06-06 09:43:15 -04: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.binaryEqual( p ) );
assert( o.woCompare( p ) < 0 );
}
2009-09-14 16:55:57 -04:00
void testoid() {
OID id;
id.init();
// sleepsecs(3);
2009-09-14 16:55:57 -04:00
OID b;
2009-09-14 16:55:57 -04:00
// goes with sleep above...
// b.init();
// assert( memcmp(id.getData(), b.getData(), 12) < 0 );
b.init( id.str() );
assert( b == id );
}
2009-08-24 23:37:12 -04:00
2011-01-04 00:40:41 -05:00
void testbounds() {
2009-08-24 23:37:12 -04:00
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 );
}
2009-08-25 10:30:35 -04:00
2011-01-04 00:40:41 -05:00
void testorder() {
2009-08-25 10:30:35 -04:00
{
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 ) );
2009-09-14 16:55:57 -04:00
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 ) );
2009-09-14 16:55:57 -04:00
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 ) );
2009-08-25 10:30:35 -04:00
}
2009-08-27 09:41:21 -04:00
{
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 );
}
2009-08-25 10:30:35 -04:00
}
2009-09-14 16:55:57 -04:00
void run() {
testRegex();
BSONObjBuilder A,B,C;
2009-02-09 15:38:26 -05:00
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.binaryEqual( b ) ); // comments on operator==
int cmp = a.woCompare(b);
assert( cmp == 0 );
cmp = a.woCompare(c);
assert( cmp < 0 );
testoid();
2009-08-24 23:37:12 -04:00
testbounds();
2009-08-25 10:30:35 -04:00
testorder();
}
} bson_unittest;
Labeler::Label GT( "$gt" );
Labeler::Label GTE( "$gte" );
Labeler::Label LT( "$lt" );
Labeler::Label LTE( "$lte" );
Labeler::Label NE( "$ne" );
2009-03-27 13:31:13 -04:00
Labeler::Label SIZE( "$size" );
2009-02-10 12:40:17 -05:00
2011-01-04 00:40:41 -05:00
void BSONObjBuilder::appendMinForType( const StringData& fieldName , int t ) {
switch ( t ) {
// Shared canonical types
case NumberInt:
case NumberDouble:
case NumberLong:
append( fieldName , - numeric_limits<double>::max() ); return;
case Symbol:
case String:
append( fieldName , "" ); return;
case Date:
// min varies with V0 and V1 indexes, so we go one type lower.
appendBool(fieldName, true);
//appendDate( fieldName , numeric_limits<long long>::min() );
return;
case Timestamp: // TODO integrate with Date SERVER-3304
appendTimestamp( fieldName , 0 ); return;
case Undefined: // shared with EOO
appendUndefined( fieldName ); return;
// Separate canonical types
case MinKey:
appendMinKey( fieldName ); return;
case MaxKey:
appendMaxKey( fieldName ); return;
2011-01-04 00:40:41 -05:00
case jstOID: {
OID o;
memset(&o, 0, sizeof(o));
appendOID( fieldName , &o);
return;
}
case Bool:
appendBool( fieldName , false); return;
case jstNULL:
appendNull( fieldName ); return;
case Object:
append( fieldName , BSONObj() ); return;
2009-09-14 16:55:57 -04:00
case Array:
appendArray( fieldName , BSONObj() ); return;
2009-09-14 16:55:57 -04:00
case BinData:
appendBinData( fieldName , 0 , BinDataGeneral , (const char *) 0 ); return;
case RegEx:
appendRegex( fieldName , "" ); return;
2011-01-04 00:40:41 -05:00
case DBRef: {
OID o;
memset(&o, 0, sizeof(o));
appendDBRef( fieldName , "" , o );
return;
}
case Code:
appendCode( fieldName , "" ); return;
case CodeWScope:
appendCodeWScope( fieldName , "" , BSONObj() ); return;
};
log() << "type not supported for appendMinElementForType: " << t << endl;
uassert( 10061 , "type not supported for appendMinElementForType" , false );
}
2009-09-14 16:55:57 -04:00
2011-01-04 00:40:41 -05:00
void BSONObjBuilder::appendMaxForType( const StringData& fieldName , int t ) {
switch ( t ) {
// Shared canonical types
2009-09-14 16:55:57 -04:00
case NumberInt:
case NumberDouble:
case NumberLong:
append( fieldName , numeric_limits<double>::max() ); return;
case Symbol:
case String:
appendMinForType( fieldName, Object ); return;
case Date:
appendDate( fieldName , numeric_limits<long long>::max() ); return;
case Timestamp: // TODO integrate with Date SERVER-3304
appendTimestamp( fieldName , numeric_limits<unsigned long long>::max() ); return;
case Undefined: // shared with EOO
appendUndefined( fieldName ); return;
// Separate canonical types
case MinKey:
appendMinKey( fieldName ); return;
case MaxKey:
appendMaxKey( fieldName ); return;
2011-01-04 00:40:41 -05:00
case jstOID: {
OID o;
memset(&o, 0xFF, sizeof(o));
appendOID( fieldName , &o);
return;
2011-01-04 00:40:41 -05:00
}
case Bool:
appendBool( fieldName , true ); return;
2009-09-09 11:51:23 -04:00
case jstNULL:
appendNull( fieldName ); return;
case Object:
appendMinForType( fieldName, Array ); return;
case Array:
appendMinForType( fieldName, BinData ); return;
case BinData:
appendMinForType( fieldName, jstOID ); return;
case RegEx:
appendMinForType( fieldName, DBRef ); return;
case DBRef:
appendMinForType( fieldName, Code ); return;
2009-08-27 10:09:55 -04:00
case Code:
appendMinForType( fieldName, CodeWScope ); return;
2009-08-27 10:09:55 -04:00
case CodeWScope:
// This upper bound may change if a new bson type is added.
appendMinForType( fieldName , MaxKey ); return;
}
log() << "type not supported for appendMaxElementForType: " << t << endl;
2011-06-21 22:09:04 -04:00
uassert( 14853 , "type not supported for appendMaxElementForType" , false );
}
2009-09-14 16:55:57 -04:00
2011-06-28 01:44:43 -04:00
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 );
}
bool fieldsMatch(const BSONObj& lhs, const BSONObj& rhs) {
BSONObjIterator l(lhs);
BSONObjIterator r(rhs);
while (l.more() && r.more()){
if (strcmp(l.next().fieldName(), r.next().fieldName())) {
return false;
}
}
return !(l.more() || r.more()); // false if lhs and rhs have diff nFields()
}
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;
}
2011-01-04 00:40:41 -05:00
bool BSONObjBuilder::appendAsNumber( const StringData& fieldName , const string& data ) {
if ( data.size() == 0 || data == "-" || data == ".")
return false;
2011-01-04 00:40:41 -05:00
unsigned int pos=0;
if ( data[0] == '-' )
pos++;
2011-01-04 00:40:41 -05:00
bool hasDec = false;
2011-01-04 00:40:41 -05:00
for ( ; pos<data.size(); pos++ ) {
if ( isdigit(data[pos]) )
continue;
2011-01-04 00:40:41 -05:00
if ( data[pos] == '.' ) {
if ( hasDec )
return false;
hasDec = true;
continue;
}
2011-01-04 00:40:41 -05:00
return false;
}
2011-01-04 00:40:41 -05:00
if ( hasDec ) {
double d = atof( data.c_str() );
append( fieldName , d );
return true;
}
2011-01-04 00:40:41 -05:00
if ( data.size() < 8 ) {
append( fieldName , atoi( data.c_str() ) );
return true;
}
2011-01-04 00:40:41 -05:00
try {
long long num = boost::lexical_cast<long long>( data );
append( fieldName , num );
return true;
}
2011-01-04 00:40:41 -05:00
catch(bad_lexical_cast &) {
return false;
}
}
2009-01-14 17:09:51 -05:00
} // namespace mongo