summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron <aaron@10gen.com>2009-02-10 11:21:13 -0500
committerAaron <aaron@10gen.com>2009-02-10 11:21:13 -0500
commitea431a83dc8d09ec585feee94e1b2f5b62cce1fe (patch)
treeebc60aa67ff69385e18c9e2512693dd1e9082e26
parentdea7bbe86d92ee383601b6514fce1161b7d0696c (diff)
downloadmongo-ea431a83dc8d09ec585feee94e1b2f5b62cce1fe.tar.gz
Make labeled subobj specs easier in c++. Now you can do BSON( "a" << GT << 4 << LTE << 22.3 << NE << "foo" )
-rw-r--r--db/jsobj.cpp7
-rw-r--r--db/jsobj.h78
-rw-r--r--dbtests/jsobjtests.cpp84
3 files changed, 158 insertions, 11 deletions
diff --git a/db/jsobj.cpp b/db/jsobj.cpp
index c1d87f00cf5..855e4e34dbf 100644
--- a/db/jsobj.cpp
+++ b/db/jsobj.cpp
@@ -1090,4 +1090,11 @@ namespace mongo {
*/
}
+ Labeler::Label GT( "$gt" );
+ Labeler::Label GTE( "$gte" );
+ Labeler::Label LT( "$lt" );
+ Labeler::Label LTE( "$lte" );
+ Labeler::Label NE( "$ne" );
+ Labeler::Label IN( "$in" );
+
} // namespace mongo
diff --git a/db/jsobj.h b/db/jsobj.h
index b2edb64a2f2..85f78f30623 100644
--- a/db/jsobj.h
+++ b/db/jsobj.h
@@ -36,6 +36,7 @@ namespace mongo {
class BSONObj;
class Record;
class BSONObjBuilder;
+ class BSONObjBuilderValueStream;
#pragma pack(1)
@@ -730,12 +731,36 @@ namespace mongo {
*/
#define BSON(x) (( BSONObjBuilder() << x ).obj())
+ class Labeler {
+ public:
+ struct Label {
+ Label( const char *l ) : l_( l ) {}
+ const char *l_;
+ };
+ Labeler( const Label &l, BSONObjBuilderValueStream *s ) : l_( l ), s_( s ) {}
+ template<class T>
+ BSONObjBuilder& operator<<( T value );
+ private:
+ const Label &l_;
+ BSONObjBuilderValueStream *s_;
+ };
+
+ extern Labeler::Label GT;
+ extern Labeler::Label GTE;
+ extern Labeler::Label LT;
+ extern Labeler::Label LTE;
+ extern Labeler::Label NE;
+ extern Labeler::Label IN;
+
class BSONObjBuilderValueStream {
public:
- BSONObjBuilderValueStream( const char * fieldName , BSONObjBuilder * builder );
+ friend class Labeler;
+ BSONObjBuilderValueStream( BSONObjBuilder * builder );
template<class T>
BSONObjBuilder& operator<<( T value );
+
+ Labeler operator<<( const Labeler::Label &l );
/*
BSONObjBuilder& operator<<( const char * value );
BSONObjBuilder& operator<<( const string& v ) { return (*this << v.c_str()); }
@@ -744,11 +769,15 @@ namespace mongo {
BSONObjBuilder& operator<<( const unsigned long value ){ return (*this << (double)value); }
*/
+ void endField( const char *nextFieldName = 0 );
+ bool subobjStarted() const { return _fieldName; }
+
private:
const char * _fieldName;
BSONObjBuilder * _builder;
+ auto_ptr< BSONObjBuilder > _subobj;
};
-
+
/**
utility for creating a BSONObj
*/
@@ -985,15 +1014,23 @@ namespace mongo {
}
/** Stream oriented way to add field names and values. */
- BSONObjBuilderValueStream operator<<(const char * name ) {
- return BSONObjBuilderValueStream( name , this );
+ BSONObjBuilderValueStream &operator<<(const char * name ) {
+ if ( !s_.get() )
+ s_.reset( new BSONObjBuilderValueStream( this ) );
+ s_->endField( name );
+ return *s_;
}
/** Stream oriented way to add field names and values. */
- BSONObjBuilderValueStream operator<<( string name ) {
- return BSONObjBuilderValueStream( name.c_str() , this );
+ BSONObjBuilderValueStream &operator<<( string name ) {
+ return operator<<( name.c_str() );
}
+ Labeler operator<<( const Labeler::Label &l ) {
+ massert( "Value stream expected", s_.get() );
+ massert( "No subobject started", s_->subobjStarted() );
+ return *s_ << l;
+ }
private:
// Append the provided arr object as an array.
@@ -1004,6 +1041,8 @@ namespace mongo {
}
char* _done() {
+ if ( s_.get() )
+ s_->endField();
b.append((char) EOO);
char *data = b.buf();
*((int*)data) = b.len();
@@ -1011,6 +1050,7 @@ namespace mongo {
}
BufBuilder b;
+ auto_ptr< BSONObjBuilderValueStream > s_;
};
@@ -1187,15 +1227,35 @@ namespace mongo {
return false;
}
- inline BSONObjBuilderValueStream::BSONObjBuilderValueStream( const char * fieldName , BSONObjBuilder * builder ) {
- _fieldName = fieldName;
+ inline BSONObjBuilderValueStream::BSONObjBuilderValueStream( BSONObjBuilder * builder ) {
+ _fieldName = 0;
_builder = builder;
+ _subobj.reset( new BSONObjBuilder() );
}
-
+
template<class T> inline
BSONObjBuilder& BSONObjBuilderValueStream::operator<<( T value ) {
_builder->append(_fieldName, value);
+ _fieldName = 0;
return *_builder;
}
+ inline Labeler BSONObjBuilderValueStream::operator<<( const Labeler::Label &l ) {
+ return Labeler( l, this );
+ }
+
+ inline void BSONObjBuilderValueStream::endField( const char *nextFieldName ) {
+ if ( _fieldName ) {
+ _builder->append( _fieldName, _subobj->done() );
+ _subobj.reset( new BSONObjBuilder() );
+ }
+ _fieldName = nextFieldName;
+ }
+
+ template<class T> inline
+ BSONObjBuilder& Labeler::operator<<( T value ) {
+ s_->_subobj->append( l_.l_, value );
+ return *s_->_builder;
+ }
+
} // namespace mongo
diff --git a/dbtests/jsobjtests.cpp b/dbtests/jsobjtests.cpp
index 1199858f97c..5e201c8de4b 100644
--- a/dbtests/jsobjtests.cpp
+++ b/dbtests/jsobjtests.cpp
@@ -433,15 +433,89 @@ namespace JsobjTests {
OID a;
OID b;
- cout << endl;
a.init();
b.init( a.str() );
ASSERT( a == b );
}
};
- }
+ } // namespace OIDTests
+
+ namespace ValueStreamTests {
+
+ class LabelBase {
+ public:
+ void run() {
+ cout << expected().toString() << endl;
+ cout << actual().toString() << endl;
+ ASSERT( !expected().woCompare( actual() ) );
+ }
+ protected:
+ virtual BSONObj expected() = 0;
+ virtual BSONObj actual() = 0;
+ };
+
+ class LabelBasic : public LabelBase {
+ BSONObj expected() {
+ return BSON( "a" << ( BSON( "$gt" << 1 ) ) );
+ }
+ BSONObj actual() {
+ return BSON( "a" << GT << 1 );
+ }
+ };
+
+ class LabelShares : public LabelBase {
+ BSONObj expected() {
+ return BSON( "z" << "q" << "a" << ( BSON( "$gt" << 1 ) ) << "x" << "p" );
+ }
+ BSONObj actual() {
+ return BSON( "z" << "q" << "a" << GT << 1 << "x" << "p" );
+ }
+ };
+
+ class LabelDouble : public LabelBase {
+ BSONObj expected() {
+ return BSON( "a" << ( BSON( "$gt" << 1 << "$lte" << "x" ) ) );
+ }
+ BSONObj actual() {
+ return BSON( "a" << GT << 1 << LTE << "x" );
+ }
+ };
+
+ class LabelDoubleShares : public LabelBase {
+ BSONObj expected() {
+ return BSON( "z" << "q" << "a" << ( BSON( "$gt" << 1 << "$lte" << "x" ) ) << "x" << "p" );
+ }
+ BSONObj actual() {
+ return BSON( "z" << "q" << "a" << GT << 1 << LTE << "x" << "x" << "p" );
+ }
+ };
+ class LabelMulti : public LabelBase {
+ BSONObj expected() {
+ return BSON( "z" << "q"
+ << "a" << BSON( "$gt" << 1 << "$lte" << "x" )
+ << "b" << BSON( "$ne" << 1 << "$ne" << "f" << "$ne" << 22.3 )
+ << "x" << "p" );
+ }
+ BSONObj actual() {
+ return BSON( "z" << "q"
+ << "a" << GT << 1 << LTE << "x"
+ << "b" << NE << 1 << NE << "f" << NE << 22.3
+ << "x" << "p" );
+ }
+ };
+
+ class Unallowed {
+ public:
+ void run() {
+ ASSERT_EXCEPTION( BSON( GT << 4 ), MsgAssertionException );
+ ASSERT_EXCEPTION( BSON( "a" << 1 << GT << 4 ), MsgAssertionException );
+ }
+ };
+
+ } // namespace ValueStreamTests
+
class All : public UnitTest::Suite {
public:
All() {
@@ -487,6 +561,12 @@ namespace JsobjTests {
add< BSONObjTests::Validation::Fuzz >( .001 );
add< OIDTests::init1 >();
add< OIDTests::initParse1 >();
+ add< ValueStreamTests::LabelBasic >();
+ add< ValueStreamTests::LabelShares >();
+ add< ValueStreamTests::LabelDouble >();
+ add< ValueStreamTests::LabelDoubleShares >();
+ add< ValueStreamTests::LabelMulti >();
+ add< ValueStreamTests::Unallowed >();
}
};