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

624 lines
14 KiB
C++
Raw Normal View History

2008-06-06 15:43:15 +02:00
// jsobj.cpp
/**
* Copyright (C) 2008 10gen Inc.
*
* 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.
*
* 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.
*
* 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"
#include "../util/goodies.h"
#include <limits>
#include "../util/unittest.h"
2008-06-06 15:43:15 +02:00
2008-10-21 22:13:48 +02:00
BSONElement nullElement;
2008-06-06 15:43:15 +02:00
string BSONElement::toString() const {
2008-06-06 15:43:15 +02:00
stringstream s;
switch( type() ) {
case EOO:
return "EOO";
case Date:
s << fieldName() << ": Date(" << hex << date() << ')'; break;
2008-11-14 17:20:23 +01:00
case RegEx:
{
s << fieldName() << ": /" << regex() << '/';
const char *p = regexFlags();
if( p ) s << p;
}
break;
2008-10-13 03:09:59 +02:00
case NumberDouble:
case NumberInt:
2008-06-06 15:43:15 +02:00
s << fieldName() << ": " << number(); break;
case Bool:
s << fieldName() << ": " << ( boolean() ? "true" : "false" ); break;
2008-06-06 15:43:15 +02:00
case Object:
case Array:
s << fieldName() << ": " << embeddedObject().toString(); break;
case Undefined:
s << fieldName() << ": undefined"; break;
case jstNULL:
s << fieldName() << ": null"; break;
case MaxKey:
s << fieldName() << ": MaxKey"; break;
case MinKey:
s << fieldName() << ": MinKey"; break;
2008-12-01 16:59:53 +01:00
case CodeWScope:
s << fieldName() << ": codewscope"; break;
2008-06-06 15:43:15 +02:00
case Code:
s << fieldName() << ": ";
if( valuestrsize() > 80 )
s << string(valuestr()).substr(0, 70) << "...";
else {
s << valuestr();
}
break;
2008-12-01 16:59:53 +01:00
case Symbol:
2008-06-06 15:43:15 +02:00
case String:
s << fieldName() << ": ";
if( valuestrsize() > 80 )
s << '"' << string(valuestr()).substr(0, 70) << "...\"";
else {
s << '"' << valuestr() << '"';
}
break;
case DBRef:
s << fieldName();
s << " : DBRef('" << valuestr() << "',";
{
OID *x = (OID *) (valuestr() + valuestrsize());
s << hex << x->a << x->b << dec << ')';
}
break;
2008-06-06 15:43:15 +02:00
case jstOID:
s << fieldName() << " : ObjId(";
s << hex << oid().a << oid().b << dec << ')';
break;
2008-06-06 15:43:15 +02:00
default:
s << fieldName() << ": ?type=" << type();
break;
}
return s.str();
}
2008-10-21 22:13:48 +02:00
int BSONElement::size() const {
2008-06-06 15:43:15 +02:00
if( totalSize >= 0 )
return totalSize;
int x = 1;
switch( type() ) {
case EOO:
case Undefined:
case jstNULL:
case MaxKey:
case MinKey:
2008-06-06 15:43:15 +02:00
break;
case Bool:
x = 2;
break;
2008-10-13 03:09:59 +02:00
case NumberInt:
x = 5;
break;
2008-06-06 15:43:15 +02:00
case Date:
2008-10-13 03:09:59 +02:00
case NumberDouble:
2008-06-06 15:43:15 +02:00
x = 9;
break;
case jstOID:
x = 13;
break;
2008-08-01 04:21:32 +02:00
case Symbol:
2008-06-06 15:43:15 +02:00
case Code:
case String:
x = valuestrsize() + 4 + 1;
break;
2008-09-14 18:39:49 +02:00
case CodeWScope:
x = objsize() + 1;
break;
2008-06-06 15:43:15 +02:00
case DBRef:
x = valuestrsize() + 4 + 12 + 1;
break;
case Object:
case Array:
x = objsize() + 1;
break;
case BinData:
x = valuestrsize() + 4 + 1 + 1/*subtype*/;
break;
case RegEx:
{
const char *p = value();
int len1 = strlen(p);
p = p + len1 + 1;
x = 1 + len1 + strlen(p) + 2;
}
break;
default:
2008-10-21 22:13:48 +02:00
cout << "BSONElement: bad type " << (int) type() << endl;
2008-06-06 15:43:15 +02:00
assert(false);
}
2008-10-21 22:13:48 +02:00
((BSONElement *) this)->totalSize = x + fieldNameSize;
2008-06-06 15:43:15 +02:00
if( !eoo() ) {
const char *next = data + totalSize;
if( *next < MinKey || ( *next > JSTypeMax && *next != MaxKey ) ) {
2008-06-06 15:43:15 +02:00
// bad type.
2008-10-02 23:25:57 +02:00
cout << "***\n";
2008-10-21 22:13:48 +02:00
cout << "Bad data or size in BSONElement::size()\n";
2008-10-02 23:25:57 +02:00
cout << "bad type:" << (int) *next << '\n';
cout << "totalsize:" << totalSize << " fieldnamesize:" << fieldNameSize << '\n';
2008-06-06 15:43:15 +02:00
cout << "lastrec:" << endl;
2008-10-02 23:25:57 +02:00
//dumpmemory(data, totalSize + 15);
2008-06-06 15:43:15 +02:00
assert(false);
}
}
return totalSize;
}
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;
}
}
else if( fn[2] == 'e' ) {
if( fn[1] == 'n' && fn[3] == 0 )
return JSMatcher::NE;
}
else if( fn[1] == 'i' && fn[2] == 'n' && fn[3] == 0 )
return JSMatcher::opIN;
}
return JSMatcher::Equality;
}
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;
int x = lt - rt;
if( x != 0 )
return x;
if( considerFieldName ) {
x = strcmp(fieldName(), e.fieldName());
if( x != 0 )
return x;
}
x = compareElementValues(*this, e);
return x;
}
2008-06-06 15:43:15 +02:00
/* must be same type! */
2008-10-21 22:13:48 +02:00
int compareElementValues(const BSONElement& l, const BSONElement& r) {
2008-06-06 15:43:15 +02:00
int f;
double x;
switch( l.type() ) {
case EOO:
case Undefined:
case jstNULL:
case MaxKey:
case MinKey:
2008-06-06 15:43:15 +02:00
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;
2008-10-13 03:09:59 +02:00
case NumberInt:
case NumberDouble:
2008-06-06 15:43:15 +02:00
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:
2008-08-01 04:21:32 +02:00
case Symbol:
2008-06-06 15:43:15 +02:00
case String:
/* todo: utf version */
return strcmp(l.valuestr(), r.valuestr());
case Object:
case Array:
return l.embeddedObject().woCompare( r.embeddedObject() );
2008-06-06 15:43:15 +02:00
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:
case RegEx:
cout << "compareElementValues: can't compare this type:" << (int) l.type() << endl;
assert(false);
break;
default:
cout << "compareElementValues: bad type " << (int) l.type() << endl;
assert(false);
}
return -1;
}
/* JSMatcher --------------------------------------*/
// If the element is something like:
// a : { $gt : 3 }
// we append
// a : 3
// else we just append the element.
//
2008-10-21 22:13:48 +02:00
void appendElementHandlingGtLt(BSONObjBuilder& b, BSONElement& e) {
2008-06-06 15:43:15 +02:00
if( e.type() == Object ) {
2008-10-21 22:13:48 +02:00
BSONElement fe = e.embeddedObject().firstElement();
2008-06-06 15:43:15 +02:00
const char *fn = fe.fieldName();
if( fn[0] == '$' && fn[1] && fn[2] == 't' ) {
b.appendAs(fe, e.fieldName());
return;
}
}
b.append(e);
}
2008-10-21 22:13:48 +02:00
int getGtLtOp(BSONElement& e) {
2008-06-06 15:43:15 +02:00
if( e.type() != Object )
return JSMatcher::Equality;
2008-06-06 15:43:15 +02:00
2008-10-21 22:13:48 +02:00
BSONElement fe = e.embeddedObject().firstElement();
return fe.getGtLtOp();
2008-06-06 15:43:15 +02:00
}
2008-10-21 22:13:48 +02:00
/* BSONObj ------------------------------------------------------------*/
2008-06-06 15:43:15 +02:00
2008-10-21 22:13:48 +02:00
string BSONObj::toString() const {
if( isEmpty() ) return "{}";
2008-06-06 15:43:15 +02:00
stringstream s;
s << "{ ";
2008-10-21 22:13:48 +02:00
BSONObjIterator i(*this);
BSONElement e = i.next();
2008-06-06 15:43:15 +02:00
if( !e.eoo() )
while( 1 ) {
s << e.toString();
e = i.next();
if( e.eoo() )
break;
s << ", ";
}
s << " }";
return s.str();
}
2008-10-09 01:04:44 +02:00
// todo: can be a little faster if we don't use toString() here.
2008-10-21 22:13:48 +02:00
bool BSONObj::valid() const {
2008-10-09 01:04:44 +02:00
try {
toString();
}
catch(...) {
return false;
}
return true;
}
2008-06-06 15:43:15 +02:00
/* well ordered compare */
int BSONObj::woCompare(const BSONObj& r, bool considerFieldName) const {
2008-06-06 15:43:15 +02:00
if( isEmpty() )
return r.isEmpty() ? 0 : -1;
if( r.isEmpty() )
return 1;
2008-10-21 22:13:48 +02:00
BSONObjIterator i(*this);
BSONObjIterator j(r);
2008-06-06 15:43:15 +02:00
while( 1 ) {
// so far, equal...
2008-10-21 22:13:48 +02:00
BSONElement l = i.next();
BSONElement r = j.next();
if ( l.eoo() )
return 0;
int x = l.woCompare( r, considerFieldName );
if ( x != 0 )
2008-06-06 15:43:15 +02:00
return x;
}
return -1;
}
BSONElement BSONObj::getField(const char *name) const {
if( details ) {
2008-10-21 22:13:48 +02:00
BSONObjIterator i(*this);
while( i.more() ) {
2008-10-21 22:13:48 +02:00
BSONElement e = i.next();
if( e.eoo() )
break;
if( strcmp(e.fieldName(), name) == 0 )
return e;
}
}
2008-10-09 23:06:56 +02:00
return nullElement;
}
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() ) {
2008-06-06 15:43:15 +02:00
const char *p = strchr(name, '.');
if( p ) {
string left(name, p-name);
2008-10-21 22:13:48 +02:00
BSONObj sub = getObjectField(left.c_str());
2008-06-06 15:43:15 +02:00
return sub.isEmpty() ? nullElement : sub.getFieldDotted(p+1);
}
}
return e;
2008-10-09 23:06:56 +02:00
/*
2008-10-21 22:13:48 +02:00
BSONObjIterator i(*this);
2008-06-06 15:43:15 +02:00
while( i.more() ) {
2008-10-21 22:13:48 +02:00
BSONElement e = i.next();
2008-06-06 15:43:15 +02:00
if( e.eoo() )
break;
if( strcmp(e.fieldName(), name) == 0 )
return e;
}
return nullElement;
2008-10-09 23:06:56 +02:00
*/
2008-06-06 15:43:15 +02:00
}
2008-12-24 19:57:30 +01:00
BSONElement BSONObj::getFieldDottedOrArray(const char *&name) const {
2008-12-26 02:22:24 +01:00
const char *p = strchr(name, '.');
2008-12-24 19:57:30 +01:00
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 );
}
2008-10-21 22:13:48 +02:00
/* makes a new BSONObj with the fields specified in pattern.
2008-06-06 15:43:15 +02:00
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.
*/
2008-12-24 19:57:30 +01:00
BSONObj BSONObj::extractFieldsDotted(BSONObj pattern, BSONObjBuilder& b, const char *&nameWithinArray) const {
nameWithinArray = "";
2008-10-21 22:13:48 +02:00
BSONObjIterator i(pattern);
2008-06-06 15:43:15 +02:00
while( i.more() ) {
2008-10-21 22:13:48 +02:00
BSONElement e = i.next();
2008-06-06 15:43:15 +02:00
if( e.eoo() )
break;
2008-12-24 19:57:30 +01:00
const char *name = e.fieldName();
BSONElement x = getFieldDottedOrArray( name );
if( x.eoo() ) {
nameWithinArray = "";
2008-10-21 22:13:48 +02:00
return BSONObj();
2008-12-24 19:57:30 +01:00
} else if ( x.type() == Array ) {
// NOTE: Currently set based on last array discovered.
nameWithinArray = name;
}
b.appendAs(x, "");
2008-06-06 15:43:15 +02:00
}
return b.done();
2008-08-22 00:05:13 +02:00
}
2008-11-17 21:39:03 +01:00
BSONObj BSONObj::extractFieldsUnDotted(BSONObj pattern) {
BSONObjBuilder b;
2008-11-04 21:05:01 +01:00
BSONObjIterator i(pattern);
while( i.more() ) {
BSONElement e = i.next();
if( e.eoo() )
break;
BSONElement x = getField(e.fieldName());
if( x.eoo() )
return BSONObj();
b.appendAs(x, "");
2008-11-04 21:05:01 +01:00
}
2008-11-17 21:39:03 +01:00
return b.doneAndDecouple();
2008-11-04 21:05:01 +01:00
}
2008-08-22 00:05:13 +02:00
2008-10-21 22:13:48 +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);
2008-08-22 00:05:13 +02:00
while( i.more() ) {
2008-10-21 22:13:48 +02:00
BSONElement e = i.next();
2008-08-22 00:05:13 +02:00
if( e.eoo() )
break;
2008-10-21 22:13:48 +02:00
BSONElement x = getFieldDotted(e.fieldName());
2008-08-22 00:05:13 +02:00
if( x.eoo() )
2008-10-21 22:13:48 +02:00
return BSONObj();
2008-08-22 00:05:13 +02:00
b.append(x);
}
return b.doneAndDecouple();
2008-06-06 15:43:15 +02:00
}
2008-10-21 22:13:48 +02:00
int BSONObj::getIntField(const char *name) {
BSONElement e = getField(name);
2008-10-13 03:09:59 +02:00
return e.isNumber() ? (int) e.number() : INT_MIN;
}
2008-10-21 22:13:48 +02:00
bool BSONObj::getBoolField(const char *name) {
BSONElement e = getField(name);
2008-07-29 23:54:54 +02:00
return e.type() == Bool ? e.boolean() : false;
}
2008-10-21 22:13:48 +02:00
const char * BSONObj::getStringField(const char *name) {
BSONElement e = getField(name);
return e.type() == String ? e.valuestr() : "";
2008-06-06 15:43:15 +02:00
}
BSONObj BSONObj::getObjectField(const char *name) const {
2008-10-21 22:13:48 +02:00
BSONElement e = getField(name);
BSONType t = e.type();
return t == Object || t == Array ? e.embeddedObject() : BSONObj();
2008-06-06 15:43:15 +02:00
}
2008-11-17 21:39:03 +01:00
int BSONObj::nFields() {
int n = 0;
BSONObjIterator i(*this);
while( i.more() ) {
BSONElement e = i.next();
if( e.eoo() )
break;
n++;
}
return n;
}
2008-10-21 22:13:48 +02:00
/* grab names of all the fields in this object */
int BSONObj::getFieldNames(set<string>& fields) {
2008-06-06 15:43:15 +02:00
int n = 0;
2008-10-21 22:13:48 +02:00
BSONObjIterator i(*this);
2008-06-06 15:43:15 +02:00
while( i.more() ) {
2008-10-21 22:13:48 +02:00
BSONElement e = i.next();
2008-06-06 15:43:15 +02:00
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.
*/
2008-10-21 22:13:48 +02:00
int BSONObj::addFields(BSONObj& from, set<string>& fields) {
assert( details == 0 ); /* partial implementation for now... */
2008-06-06 15:43:15 +02:00
2008-10-21 22:13:48 +02:00
BSONObjBuilder b;
2008-06-06 15:43:15 +02:00
int N = fields.size();
int n = 0;
2008-10-21 22:13:48 +02:00
BSONObjIterator i(from);
2008-06-06 15:43:15 +02:00
bool gotId = false;
while( i.more() ) {
2008-10-21 22:13:48 +02:00
BSONElement e = i.next();
2008-06-06 15:43:15 +02:00
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;
}
ostream& operator<<( ostream &s, const BSONObj &o ) {
return s << o.toString();
}
2008-06-06 15:43:15 +02:00
/*-- test things ----------------------------------------------------*/
2008-08-11 20:53:08 +02:00
#pragma pack(push,1)
2008-06-06 15:43:15 +02:00
struct MaxKeyData {
MaxKeyData() { totsize=7; maxkey=MaxKey; name=0; eoo=EOO; }
int totsize;
char maxkey;
char name;
char eoo;
} maxkeydata;
2008-10-21 22:13:48 +02:00
BSONObj maxKey((const char *) &maxkeydata);
2008-06-06 15:43:15 +02:00
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);
2008-06-06 15:43:15 +02:00
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
2008-10-21 22:13:48 +02:00
BSONElement::BSONElement() {
2008-06-06 15:43:15 +02:00
data = &js0.eoo;
fieldNameSize = 0;
totalSize = -1;
}
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)
2008-10-21 22:13:48 +02:00
BSONObj emptyObj((char *) &emptyObject);
2008-09-09 16:19:31 +02:00
struct BsonUnitTest : public UnitTest {
2008-12-26 02:05:31 +01:00
void testRegex() {
BSONObjBuilder b;
b.appendRegex("x", "foo");
BSONObj o = b.done();
cout << o.toString() << endl;
exit(1);
}
void run() {
// testRegex();
2008-11-21 20:14:40 +01:00
BSONObjBuilder A,B,C;
A.appendInt("x", 2);
B.append("x", 2.0);
2008-11-21 20:14:40 +01:00
C.append("x", 2.1);
BSONObj a = A.done();
BSONObj b = B.done();
2008-11-21 20:14:40 +01:00
BSONObj c = C.done();
assert( !(a==b) ); // comments on operator==
2008-11-21 20:14:40 +01:00
int cmp = a.woCompare(b);
assert( cmp == 0 );
cmp = a.woCompare(c);
assert( cmp < 0 );
}
} bsonut;