diff --git a/bson/bson-inl.h b/bson/bson-inl.h index ee7010dc06f..8d2e34a5b74 100644 --- a/bson/bson-inl.h +++ b/bson/bson-inl.h @@ -89,16 +89,18 @@ namespace mongo { return b.obj(); } - inline bool BSONObj::hasElement(const char *name) const { - if ( !isEmpty() ) { - BSONObjIterator it(*this); - while ( it.moreWithEOO() ) { - BSONElement e = it.next(); - if ( strcmp(name, e.fieldName()) == 0 ) - return true; + inline void BSONObj::getFields(unsigned n, const char **fieldNames, BSONElement *fields) const { + BSONObjIterator i(*this); + while ( i.more() ) { + BSONElement e = i.next(); + const char *p = e.fieldName(); + for( unsigned i = 0; i < n; i++ ) { + if( strcmp(p, fieldNames[i]) == 0 ) { + fields[i] = e; + break; + } } } - return false; } inline BSONElement BSONObj::getField(const StringData& name) const { @@ -111,6 +113,21 @@ namespace mongo { return BSONElement(); } + inline int BSONObj::getIntField(const char *name) const { + BSONElement e = getField(name); + return e.isNumber() ? (int) e.number() : INT_MIN; + } + + inline bool BSONObj::getBoolField(const char *name) const { + BSONElement e = getField(name); + return e.type() == Bool ? e.boolean() : false; + } + + inline const char * BSONObj::getStringField(const char *name) const { + BSONElement e = getField(name); + return e.type() == String ? e.valuestr() : ""; + } + /* add all the fields from the object specified to this object */ inline BSONObjBuilder& BSONObjBuilder::appendElements(BSONObj x) { BSONObjIterator it(x); diff --git a/bson/bsonelement.h b/bson/bsonelement.h index 2b63a869c21..a6bf23e825b 100644 --- a/bson/bsonelement.h +++ b/bson/bsonelement.h @@ -153,9 +153,11 @@ namespace mongo { You must assure element is a boolean before calling. */ bool boolean() const { - return *value() ? true : false; + return *value(); // ? true : false; } + bool booleanSafe() const { return isBoolean() && boolean(); } + /** Retrieve a java style date value from the element. Ensure element is of type Date before calling. @see Bool(), trueValue() @@ -495,7 +497,7 @@ namespace mongo { return true; } - /** True if element is of a numeric type. */ + /** @return true if element is of a numeric type. */ inline bool BSONElement::isNumber() const { switch( type() ) { case NumberLong: diff --git a/bson/bsonobj.h b/bson/bsonobj.h index cf402bdbb33..548d7f07bbe 100644 --- a/bson/bsonobj.h +++ b/bson/bsonobj.h @@ -124,7 +124,9 @@ namespace mongo { */ bool isOwned() const { return _holder.get() != 0; } - /* make sure the data buffer is under the control of this BSONObj and not a remote buffer */ + /** assure the data buffer is under the control of this BSONObj and not a remote buffer + @see isOwned() + */ BSONObj getOwned() const; /** @return a new full (and owned) copy of the object. */ @@ -152,18 +154,20 @@ namespace mongo { /** adds the field names to the fields set. does NOT clear it (appends). */ int getFieldNames(set& fields) const; - /** return has eoo() true if no match - supports "." notation to reach into embedded objects + /** @return the specified element. element.eoo() will be true if not found. + @param name field to find. supports dot (".") notation to reach into embedded objects. + for example "x.y" means "in the nested object in field x, retrieve field y" */ BSONElement getFieldDotted(const char *name) const; - /** return has eoo() true if no match - supports "." notation to reach into embedded objects + /** @return the specified element. element.eoo() will be true if not found. + @param name field to find. supports dot (".") notation to reach into embedded objects. + for example "x.y" means "in the nested object in field x, retrieve field y" */ BSONElement getFieldDotted(const string& name) const { return getFieldDotted( name.c_str() ); } - /** Like getFieldDotted(), but expands multikey arrays and returns all matching objects. + /** Like getFieldDotted(), but expands arrays and returns all matching objects. * Turning off expandLastArray allows you to retrieve nested array objects instead of * their contents. */ @@ -180,6 +184,14 @@ namespace mongo { */ BSONElement getField(const StringData& name) const; + /** Get several fields at once. This is faster than separate getField() calls as the size of + elements iterated can then be calculated only once each. + @param n number of fieldNames, and number of elements in the fields array + @param fields if a field is found its element is stored in its corresponding position in this array. + if not found the array element is unchanged. + */ + void getFields(unsigned n, const char **fieldNames, BSONElement *fields) const; + /** Get the field of the specified name. eoo() is true on the returned element if not found. */ @@ -199,7 +211,9 @@ namespace mongo { } /** @return true if field exists */ - bool hasField( const char * name ) const { return ! getField( name ).eoo(); } + bool hasField( const char * name ) const { return !getField(name).eoo(); } + /** @return true if field exists */ + bool hasElement(const char *name) const { return hasField(name); } /** @return "" if DNE or wrong type */ const char * getStringField(const char *name) const; @@ -210,7 +224,9 @@ namespace mongo { /** @return INT_MIN if not present - does some type conversions */ int getIntField(const char *name) const; - /** @return false if not present */ + /** @return false if not present + @see BSONElement::trueValue() + */ bool getBoolField(const char *name) const; /** @@ -270,7 +286,6 @@ namespace mongo { int woCompare(const BSONObj& r, const BSONObj &ordering = BSONObj(), bool considerFieldName=true) const; - bool operator<( const BSONObj& other ) const { return woCompare( other ) < 0; } bool operator<=( const BSONObj& other ) const { return woCompare( other ) <= 0; } bool operator>( const BSONObj& other ) const { return woCompare( other ) > 0; } @@ -295,8 +310,13 @@ namespace mongo { /** @return first field of the object */ BSONElement firstElement() const { return BSONElement(objdata() + 4); } - /** @return true if field exists in the object */ - bool hasElement(const char *name) const; + /** faster than firstElement().fieldName() - for the first element we can easily find the fieldname without + computing the element size. + */ + const char * firstElementFieldName() const { + const char *p = objdata() + 4; + return *p == EOO ? "" : p+1; + } /** Get the _id field from the object. For good performance drivers should assure that _id is the first element of the object; however, correct operation diff --git a/dbtests/perftests.cpp b/dbtests/perftests.cpp index 95a9b691de0..89ecc512f3b 100644 --- a/dbtests/perftests.cpp +++ b/dbtests/perftests.cpp @@ -128,7 +128,7 @@ namespace PerfTests { public: void say(unsigned long long n, int ms, string s) { unsigned long long rps = n*1000/ms; - cout << "stats " << setw(33) << left << s << ' ' << setw(8) << rps << ' ' << right << setw(6) << ms << "ms "; + cout << "stats " << setw(33) << left << s << ' ' << right << setw(9) << rps << ' ' << right << setw(6) << ms << "ms "; if( showDurStats() ) cout << dur::stats.curr->_asCSV(); cout << endl; @@ -267,7 +267,13 @@ namespace PerfTests { unsigned dontOptimizeOutHopefully; - class BSONIter : public B { + class NonDurTest : public B { + public: + virtual int howLongMillis() { return 3000; } + virtual bool showDurStats() { return false; } + }; + + class BSONIter : public NonDurTest { public: int n; bo b, sub; @@ -277,7 +283,6 @@ namespace PerfTests { bo sub = bob().appendTimeT("t", time(0)).appendBool("abool", true).appendBinData("somebin", 3, BinDataGeneral, "abc").appendNull("anullone").obj(); b = BSON( "_id" << OID() << "x" << 3 << "yaaaaaa" << 3.00009 << "zz" << 1 << "q" << false << "obj" << sub << "zzzzzzz" << "a string a string" ); } - virtual bool showDurStats() { return false; } void timed() { for( bo::iterator i = b.begin(); i.more(); ) if( i.next().fieldName() ) @@ -288,6 +293,42 @@ namespace PerfTests { } }; + class BSONGetFields1 : public NonDurTest { + public: + int n; + bo b, sub; + string name() { return "BSONGetFields1By1"; } + BSONGetFields1() { + n = 0; + bo sub = bob().appendTimeT("t", time(0)).appendBool("abool", true).appendBinData("somebin", 3, BinDataGeneral, "abc").appendNull("anullone").obj(); + b = BSON( "_id" << OID() << "x" << 3 << "yaaaaaa" << 3.00009 << "zz" << 1 << "q" << false << "obj" << sub << "zzzzzzz" << "a string a string" ); + } + void timed() { + if( b["x"].eoo() ) + n++; + if( b["q"].eoo() ) + n++; + if( b["zzz"].eoo() ) + n++; + } + }; + + class BSONGetFields2 : public BSONGetFields1 { + public: + string name() { return "BSONGetFields"; } + void timed() { + static const char *names[] = { "x", "q", "zzz" }; + BSONElement elements[3]; + b.getFields(3, names, elements); + if( elements[0].eoo() ) + n++; + if( elements[1].eoo() ) + n++; + if( elements[2].eoo() ) + n++; + } + }; + class KeyTest : public B { public: KeyV1Owned a,b,c; @@ -608,6 +649,8 @@ namespace PerfTests { add< Bldr >(); add< StkBldr >(); add< BSONIter >(); + add< BSONGetFields1 >(); + add< BSONGetFields2 >(); add< ChecksumTest >(); add< TaskQueueTest >(); add< InsertDup >();