mirror of
https://github.com/mongodb/mongo.git
synced 2024-12-01 09:32:32 +01:00
update re-write almost done, faster and cleaner
This commit is contained in:
parent
42aef472ec
commit
d672078581
177
db/update.cpp
177
db/update.cpp
@ -26,12 +26,25 @@
|
||||
|
||||
namespace mongo {
|
||||
|
||||
void Mod::apply( BSONObjBuilder& b , BSONElement in ){
|
||||
switch ( op ){
|
||||
case INC:
|
||||
inc( in );
|
||||
b.appendAs( elt , shortFieldName ); // TODO: this is horrible
|
||||
break;
|
||||
default:
|
||||
stringstream ss;
|
||||
ss << "Mod::apply can't handle type: " << op;
|
||||
throw UserException( ss.str() );
|
||||
}
|
||||
}
|
||||
|
||||
bool ModSet::canApplyInPlaceAndVerify(const BSONObj &obj) const {
|
||||
bool inPlacePossible = true;
|
||||
|
||||
// Perform this check first, so that we don't leave a partially modified object on uassert.
|
||||
for ( vector<Mod>::const_iterator i = _mods.begin(); i != _mods.end(); ++i ) {
|
||||
const Mod& m = *i;
|
||||
for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.end(); ++i ) {
|
||||
const Mod& m = i->second;
|
||||
BSONElement e = obj.getFieldDotted(m.fieldName);
|
||||
|
||||
if ( e.eoo() ) {
|
||||
@ -95,8 +108,8 @@ namespace mongo {
|
||||
}
|
||||
|
||||
void ModSet::applyModsInPlace(const BSONObj &obj) const {
|
||||
for ( vector<Mod>::const_iterator i = _mods.begin(); i != _mods.end(); ++i ) {
|
||||
const Mod& m = *i;
|
||||
for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.end(); ++i ) {
|
||||
const Mod& m = i->second;
|
||||
BSONElement e = obj.getFieldDotted(m.fieldName);
|
||||
|
||||
switch ( m.op ){
|
||||
@ -142,29 +155,109 @@ namespace mongo {
|
||||
fields[ base + top.fieldName() ] = top;
|
||||
}
|
||||
|
||||
void ModSet::_appendNewFromMods( const string& root , Mod& m , BSONObjBuilder& b , set<string>& onedownseen ){
|
||||
const char * temp = m.fieldName;
|
||||
temp += root.size();
|
||||
const char * dot = index( temp , '.' );
|
||||
if ( dot ){
|
||||
string nr( m.fieldName , 0 , 1 + ( dot - m.fieldName ) );
|
||||
string nf( temp , 0 , dot - temp );
|
||||
if ( onedownseen.count( nf ) )
|
||||
return;
|
||||
onedownseen.insert( nf );
|
||||
BSONObjBuilder bb ( b.subobjStart( nf.c_str() ) );
|
||||
createNewFromMods( nr , bb , BSONObj() );
|
||||
bb.done();
|
||||
}
|
||||
else
|
||||
appendNewFromMod( m , b );
|
||||
|
||||
}
|
||||
|
||||
void ModSet::createNewFromMods( const string& root , BSONObjBuilder& b , const BSONObj &obj ){
|
||||
BSONObjIterator es( obj );
|
||||
BSONElement e = es.next();
|
||||
|
||||
ModHolder::iterator m = _mods.lower_bound( root );
|
||||
ModHolder::iterator mend = _mods.lower_bound( root + "{" );
|
||||
|
||||
set<string> onedownseen;
|
||||
list<Mod*> toadd; // TODO: remove. this is a hack to make new and old impls. identical. when testing is complete, we should remove
|
||||
|
||||
while ( e.type() && m != mend ){
|
||||
string field = root + e.fieldName();
|
||||
FieldCompareResult cmp = compareDottedFieldNames( m->second.fieldName , field );
|
||||
|
||||
switch ( cmp ){
|
||||
case LEFT_SUBFIELD: {
|
||||
uassert( "LEFT_SUBFIELD only supports Object" , e.type() == Object );
|
||||
BSONObjBuilder bb ( b.subobjStart( e.fieldName() ) );
|
||||
stringstream nr; nr << root << e.fieldName() << ".";
|
||||
createNewFromMods( nr.str() , bb , e.embeddedObject() );
|
||||
bb.done();
|
||||
// inc both as we handled both
|
||||
e = es.next();
|
||||
m++;
|
||||
continue;
|
||||
}
|
||||
case LEFT_BEFORE:
|
||||
toadd.push_back( &(m->second) );
|
||||
m++;
|
||||
continue;
|
||||
case SAME:
|
||||
m->second.apply( b , e );
|
||||
e = es.next();
|
||||
m++;
|
||||
continue;
|
||||
case RIGHT_BEFORE:
|
||||
b.append( e );
|
||||
e = es.next();
|
||||
continue;
|
||||
case RIGHT_SUBFIELD:
|
||||
massert( "ModSet::createNewFromMods - RIGHT_SUBFIELD should be impossible" , 0 );
|
||||
break;
|
||||
default:
|
||||
massert( "unhandled case" , 0 );
|
||||
}
|
||||
}
|
||||
|
||||
while ( e.type() ){
|
||||
b.append( e );
|
||||
e = es.next();
|
||||
}
|
||||
|
||||
for ( list<Mod*>::iterator i=toadd.begin(); i!=toadd.end(); i++ )
|
||||
_appendNewFromMods( root , **i , b , onedownseen );
|
||||
|
||||
|
||||
for ( ; m != mend; m++ ){
|
||||
_appendNewFromMods( root , m->second , b , onedownseen );
|
||||
}
|
||||
}
|
||||
|
||||
BSONObj ModSet::createNewFromMods_r( const BSONObj &obj ) {
|
||||
return obj;
|
||||
BSONObjBuilder b;
|
||||
createNewFromMods( "" , b , obj );
|
||||
return b.obj();
|
||||
}
|
||||
|
||||
BSONObj ModSet::createNewFromMods_l( const BSONObj &obj ) {
|
||||
sortMods();
|
||||
map< string, BSONElement > existing;
|
||||
|
||||
BSONObjBuilder b;
|
||||
BSONObjIterator i( obj );
|
||||
while( i.moreWithEOO() ) {
|
||||
while( i.more() ) {
|
||||
BSONElement e = i.next();
|
||||
if ( e.eoo() )
|
||||
break;
|
||||
if ( !haveModForFieldOrSubfield( e.fieldName() ) ) {
|
||||
if ( ! haveModForFieldOrSubfield( e.fieldName() ) ) {
|
||||
b.append( e );
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
extractFields( existing, e, "" );
|
||||
}
|
||||
}
|
||||
|
||||
EmbeddedBuilder b2( &b );
|
||||
vector< Mod >::iterator m = _mods.begin();
|
||||
ModHolder::iterator m = _mods.begin();
|
||||
map< string, BSONElement >::iterator p = existing.begin();
|
||||
while( m != _mods.end() || p != existing.end() ) {
|
||||
|
||||
@ -180,28 +273,28 @@ namespace mongo {
|
||||
if ( p == existing.end() ){
|
||||
uassert( "Modifier spec implies existence of an encapsulating object with a name that already represents a non-object,"
|
||||
" or is referenced in another $set clause",
|
||||
mayAddEmbedded( existing, m->fieldName ) );
|
||||
mayAddEmbedded( existing, m->second.fieldName ) );
|
||||
// $ modifier applied to missing field -- create field from scratch
|
||||
appendNewFromMod( *m , b2 );
|
||||
appendNewFromMod( m->second , b2 );
|
||||
m++;
|
||||
continue;
|
||||
}
|
||||
|
||||
FieldCompareResult cmp = compareDottedFieldNames( m->fieldName , p->first );
|
||||
FieldCompareResult cmp = compareDottedFieldNames( m->second.fieldName , p->first );
|
||||
if ( cmp <= 0 )
|
||||
uassert( "Modifier spec implies existence of an encapsulating object with a name that already represents a non-object,"
|
||||
" or is referenced in another $set clause",
|
||||
mayAddEmbedded( existing, m->fieldName ) );
|
||||
mayAddEmbedded( existing, m->second.fieldName ) );
|
||||
if ( cmp == 0 ) {
|
||||
BSONElement e = p->second;
|
||||
if ( m->op == Mod::INC ) {
|
||||
m->inc(e);
|
||||
if ( m->second.op == Mod::INC ) {
|
||||
m->second.inc(e);
|
||||
//m->setn( m->getn() + e.number() );
|
||||
b2.appendAs( m->elt, m->fieldName );
|
||||
} else if ( m->op == Mod::SET ) {
|
||||
b2.appendAs( m->elt, m->fieldName );
|
||||
} else if ( m->op == Mod::PUSH || m->op == Mod::PUSH_ALL ) {
|
||||
BSONObjBuilder arr( b2.subarrayStartAs( m->fieldName ) );
|
||||
b2.appendAs( m->second.elt, m->second.fieldName );
|
||||
} else if ( m->second.op == Mod::SET ) {
|
||||
b2.appendAs( m->second.elt, m->second.fieldName );
|
||||
} else if ( m->second.op == Mod::PUSH || m->second.op == Mod::PUSH_ALL ) {
|
||||
BSONObjBuilder arr( b2.subarrayStartAs( m->second.fieldName ) );
|
||||
BSONObjIterator i( e.embeddedObject() );
|
||||
int startCount = 0;
|
||||
while( i.moreWithEOO() ) {
|
||||
@ -211,13 +304,13 @@ namespace mongo {
|
||||
arr.append( arrI );
|
||||
++startCount;
|
||||
}
|
||||
if ( m->op == Mod::PUSH ) {
|
||||
if ( m->second.op == Mod::PUSH ) {
|
||||
stringstream ss;
|
||||
ss << startCount;
|
||||
string nextIndex = ss.str();
|
||||
arr.appendAs( m->elt, nextIndex.c_str() );
|
||||
arr.appendAs( m->second.elt, nextIndex.c_str() );
|
||||
} else {
|
||||
BSONObjIterator i( m->elt.embeddedObject() );
|
||||
BSONObjIterator i( m->second.elt.embeddedObject() );
|
||||
int count = startCount;
|
||||
while( i.moreWithEOO() ) {
|
||||
BSONElement arrI = i.next();
|
||||
@ -230,9 +323,9 @@ namespace mongo {
|
||||
}
|
||||
}
|
||||
arr.done();
|
||||
m->pushStartSize = startCount;
|
||||
} else if ( m->op == Mod::PULL || m->op == Mod::PULL_ALL ) {
|
||||
BSONObjBuilder arr( b2.subarrayStartAs( m->fieldName ) );
|
||||
m->second.pushStartSize = startCount;
|
||||
} else if ( m->second.op == Mod::PULL || m->second.op == Mod::PULL_ALL ) {
|
||||
BSONObjBuilder arr( b2.subarrayStartAs( m->second.fieldName ) );
|
||||
BSONObjIterator i( e.embeddedObject() );
|
||||
int count = 0;
|
||||
while( i.moreWithEOO() ) {
|
||||
@ -240,10 +333,10 @@ namespace mongo {
|
||||
if ( arrI.eoo() )
|
||||
break;
|
||||
bool allowed = true;
|
||||
if ( m->op == Mod::PULL ) {
|
||||
allowed = ( arrI.woCompare( m->elt, false ) != 0 );
|
||||
if ( m->second.op == Mod::PULL ) {
|
||||
allowed = ( arrI.woCompare( m->second.elt, false ) != 0 );
|
||||
} else {
|
||||
BSONObjIterator j( m->elt.embeddedObject() );
|
||||
BSONObjIterator j( m->second.elt.embeddedObject() );
|
||||
while( allowed && j.moreWithEOO() ) {
|
||||
BSONElement arrJ = j.next();
|
||||
if ( arrJ.eoo() )
|
||||
@ -260,11 +353,11 @@ namespace mongo {
|
||||
}
|
||||
arr.done();
|
||||
}
|
||||
else if ( m->op == Mod::POP ){
|
||||
else if ( m->second.op == Mod::POP ){
|
||||
int startCount = 0;
|
||||
BSONObjBuilder arr( b2.subarrayStartAs( m->fieldName ) );
|
||||
BSONObjBuilder arr( b2.subarrayStartAs( m->second.fieldName ) );
|
||||
BSONObjIterator i( e.embeddedObject() );
|
||||
if ( m->elt.isNumber() && m->elt.number() < 0 ){
|
||||
if ( m->second.elt.isNumber() && m->second.elt.number() < 0 ){
|
||||
if ( i.more() ) i.next();
|
||||
int count = 0;
|
||||
startCount++;
|
||||
@ -283,14 +376,14 @@ namespace mongo {
|
||||
}
|
||||
}
|
||||
arr.done();
|
||||
m->pushStartSize = startCount;
|
||||
m->second.pushStartSize = startCount;
|
||||
}
|
||||
++m;
|
||||
++p;
|
||||
}
|
||||
else if ( cmp < 0 ) {
|
||||
// $ modifier applied to missing field -- create field from scratch
|
||||
appendNewFromMod( *m , b2 );
|
||||
appendNewFromMod( m->second , b2 );
|
||||
m++;
|
||||
}
|
||||
else if ( cmp > 0 ) {
|
||||
@ -329,13 +422,11 @@ namespace mongo {
|
||||
BSONElement f = jt.next();
|
||||
Mod m;
|
||||
m.op = op;
|
||||
m.fieldName = f.fieldName();
|
||||
m.setFieldName( f.fieldName() );
|
||||
uassert( "Mod on _id not allowed", strcmp( m.fieldName, "_id" ) != 0 );
|
||||
uassert( "Invalid mod field name, may not end in a period", m.fieldName[ strlen( m.fieldName ) - 1 ] != '.' );
|
||||
for ( vector<Mod>::iterator i = _mods.begin(); i != _mods.end(); i++ ) {
|
||||
uassert( "Field name duplication not allowed with modifiers",
|
||||
strcmp( m.fieldName, i->fieldName ) != 0 );
|
||||
}
|
||||
uassert( "Field name duplication not allowed with modifiers", ! haveModForField( m.fieldName ) );
|
||||
|
||||
uassert( "Modifier $inc allowed for numbers only", f.isNumber() || op != Mod::INC );
|
||||
uassert( "Modifier $pushAll/pullAll allowed for arrays only", f.type() == Array || ( op != Mod::PUSH_ALL && op != Mod::PULL_ALL ) );
|
||||
m.elt = f;
|
||||
@ -353,7 +444,7 @@ namespace mongo {
|
||||
m.nint = 0;
|
||||
m.nlong = (long long *) f.value();
|
||||
}
|
||||
_mods.push_back( m );
|
||||
_mods[m.fieldName] = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
115
db/update.h
115
db/update.h
@ -27,7 +27,8 @@ namespace mongo {
|
||||
struct Mod {
|
||||
enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP } op;
|
||||
const char *fieldName;
|
||||
|
||||
const char *shortFieldName;
|
||||
|
||||
// kind of lame; fix one day?
|
||||
double *ndouble;
|
||||
int *nint;
|
||||
@ -36,6 +37,15 @@ namespace mongo {
|
||||
BSONElement elt;
|
||||
int pushStartSize;
|
||||
|
||||
void setFieldName( const char * s ){
|
||||
fieldName = s;
|
||||
shortFieldName = rindex( fieldName , '.' );
|
||||
if ( shortFieldName )
|
||||
shortFieldName++;
|
||||
else
|
||||
shortFieldName = fieldName;
|
||||
}
|
||||
|
||||
/* [dm] why is this const? (or rather, why was setn const?) i see why but think maybe clearer if were not. */
|
||||
void inc(BSONElement& n) const {
|
||||
uassert( "$inc value is not a number", n.isNumber() );
|
||||
@ -95,22 +105,18 @@ namespace mongo {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void apply( BSONObjBuilder& b , BSONElement in );
|
||||
};
|
||||
|
||||
class ModSet {
|
||||
vector< Mod > _mods;
|
||||
typedef map<string,Mod> ModHolder;
|
||||
ModHolder _mods;
|
||||
bool _sorted;
|
||||
|
||||
void sortMods() {
|
||||
if ( ! _sorted ){
|
||||
sort( _mods.begin(), _mods.end() );
|
||||
_sorted = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void extractFields( map< string, BSONElement > &fields, const BSONElement &top, const string &base );
|
||||
|
||||
FieldCompareResult compare( const vector< Mod >::iterator &m, map< string, BSONElement >::iterator &p, const map< string, BSONElement >::iterator &pEnd ) const {
|
||||
FieldCompareResult compare( const ModHolder::iterator &m, map< string, BSONElement >::iterator &p, const map< string, BSONElement >::iterator &pEnd ) const {
|
||||
bool mDone = ( m == _mods.end() );
|
||||
bool pDone = ( p == pEnd );
|
||||
assert( ! mDone );
|
||||
@ -123,8 +129,10 @@ namespace mongo {
|
||||
if ( pDone )
|
||||
return LEFT_BEFORE;
|
||||
|
||||
return compareDottedFieldNames( m->fieldName, p->first.c_str() );
|
||||
return compareDottedFieldNames( m->first, p->first.c_str() );
|
||||
}
|
||||
|
||||
void _appendNewFromMods( const string& root , Mod& m , BSONObjBuilder& b , set<string>& onedownseen );
|
||||
|
||||
void appendNewFromMod( Mod& m , EmbeddedBuilder& b ){
|
||||
if ( m.op == Mod::PUSH ) {
|
||||
@ -142,13 +150,38 @@ namespace mongo {
|
||||
}
|
||||
}
|
||||
|
||||
void appendNewFromMod( Mod& m , BSONObjBuilder& b ){
|
||||
if ( m.op == Mod::PUSH ) {
|
||||
/*
|
||||
BSONObjBuilder arr( b.subarrayStartAs( m.fieldName ) );
|
||||
arr.appendAs( m.elt, "0" );
|
||||
arr.done();
|
||||
m.pushStartSize = -1;
|
||||
*/
|
||||
uassert( "appendNewFromMod push not done" , 0 );
|
||||
}
|
||||
else if ( m.op == Mod::PUSH_ALL ) {
|
||||
//b.appendAs( m.elt, m.fieldName );
|
||||
//m.pushStartSize = -1;
|
||||
uassert( "appendNewFromMod push_all not done" , 0 );
|
||||
}
|
||||
else if ( m.op == Mod::PULL || m.op == Mod::PULL_ALL ) {
|
||||
}
|
||||
else if ( m.op == Mod::INC || m.op == Mod::SET ){
|
||||
b.appendAs( m.elt, m.shortFieldName );
|
||||
}
|
||||
else {
|
||||
uassert( "unknonw mod" , 0 );
|
||||
}
|
||||
}
|
||||
|
||||
bool mayAddEmbedded( map< string, BSONElement > &existing, string right ) {
|
||||
for( string left = EmbeddedBuilder::splitDot( right );
|
||||
left.length() > 0 && left[ left.length() - 1 ] != '.';
|
||||
left += "." + EmbeddedBuilder::splitDot( right ) ) {
|
||||
if ( existing.count( left ) > 0 && existing[ left ].type() != Object )
|
||||
return false;
|
||||
if ( modForField( left.c_str() ) )
|
||||
if ( haveModForField( left.c_str() ) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -175,6 +208,9 @@ namespace mongo {
|
||||
// old linear version
|
||||
BSONObj createNewFromMods_l( const BSONObj &obj );
|
||||
|
||||
// new recursive version, will replace at some point
|
||||
void createNewFromMods( const string& root , BSONObjBuilder& b , const BSONObj &obj );
|
||||
|
||||
// new recursive version, will replace at some point
|
||||
BSONObj createNewFromMods_r( const BSONObj &obj );
|
||||
|
||||
@ -187,8 +223,8 @@ namespace mongo {
|
||||
*/
|
||||
int isIndexed( const set<string>& idxKeys ) const {
|
||||
int numIndexes = 0;
|
||||
for ( vector<Mod>::const_iterator i = _mods.begin(); i != _mods.end(); i++ ){
|
||||
if ( i->isIndexed( idxKeys ) )
|
||||
for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.end(); i++ ){
|
||||
if ( i->second.isIndexed( idxKeys ) )
|
||||
numIndexes++;
|
||||
}
|
||||
return numIndexes;
|
||||
@ -196,44 +232,37 @@ namespace mongo {
|
||||
|
||||
unsigned size() const { return _mods.size(); }
|
||||
bool haveModForField( const char *fieldName ) const {
|
||||
// Presumably the number of mods is small, so this loop isn't too expensive.
|
||||
for( vector<Mod>::const_iterator i = _mods.begin(); i != _mods.end(); ++i ) {
|
||||
if ( strlen( fieldName ) == strlen( i->fieldName ) && strcmp( fieldName, i->fieldName ) == 0 )
|
||||
return true;
|
||||
return _mods.find( fieldName ) != _mods.end();
|
||||
}
|
||||
bool haveModForFieldOrSubfield( const string& fieldName ) const {
|
||||
ModHolder::const_iterator start = _mods.lower_bound(fieldName);
|
||||
for ( ; start != _mods.end(); start++ ){
|
||||
FieldCompareResult r = compareDottedFieldNames( fieldName , start->first );
|
||||
switch ( r ){
|
||||
case LEFT_SUBFIELD: assert(0); break;
|
||||
case LEFT_BEFORE: continue;
|
||||
case SAME: return true;
|
||||
case RIGHT_BEFORE: continue;
|
||||
case RIGHT_SUBFIELD: return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool haveModForFieldOrSubfield( const char *fieldName ) const {
|
||||
// Presumably the number of mods is small, so this loop isn't too expensive.
|
||||
for( vector<Mod>::const_iterator i = _mods.begin(); i != _mods.end(); ++i ) {
|
||||
const char *dot = strchr( i->fieldName, '.' );
|
||||
size_t len = dot ? dot - i->fieldName : strlen( i->fieldName );
|
||||
if ( len == strlen( fieldName ) && strncmp( fieldName, i->fieldName, len ) == 0 )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const Mod *modForField( const char *fieldName ) const {
|
||||
// Presumably the number of mods is small, so this loop isn't too expensive.
|
||||
for( vector<Mod>::const_iterator i = _mods.begin(); i != _mods.end(); ++i ) {
|
||||
if ( strcmp( fieldName, i->fieldName ) == 0 )
|
||||
return &*i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool haveArrayDepMod() const {
|
||||
for ( vector<Mod>::const_iterator i = _mods.begin(); i != _mods.end(); i++ )
|
||||
if ( i->arrayDep() )
|
||||
for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.end(); i++ )
|
||||
if ( i->second.arrayDep() )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
void appendSizeSpecForArrayDepMods( BSONObjBuilder &b ) const {
|
||||
for ( vector<Mod>::const_iterator i = _mods.begin(); i != _mods.end(); i++ ) {
|
||||
if ( i->arrayDep() ){
|
||||
if ( i->pushStartSize == -1 )
|
||||
b.appendNull( i->fieldName );
|
||||
for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.end(); i++ ) {
|
||||
const Mod& m = i->second;
|
||||
if ( m.arrayDep() ){
|
||||
if ( m.pushStartSize == -1 )
|
||||
b.appendNull( m.fieldName );
|
||||
else
|
||||
b << i->fieldName << BSON( "$size" << i->pushStartSize );
|
||||
b << m.fieldName << BSON( "$size" << m.pushStartSize );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -521,6 +521,28 @@ namespace UpdateTests {
|
||||
|
||||
namespace ModSetTests {
|
||||
|
||||
class internal1 {
|
||||
public:
|
||||
void run(){
|
||||
BSONObj b = BSON( "$inc" << BSON( "x" << 1 << "a.b" << 1 ) );
|
||||
ModSet m;
|
||||
m.getMods( b );
|
||||
|
||||
ASSERT( m.haveModForField( "x" ) );
|
||||
ASSERT( m.haveModForField( "a.b" ) );
|
||||
ASSERT( ! m.haveModForField( "y" ) );
|
||||
ASSERT( ! m.haveModForField( "a.c" ) );
|
||||
ASSERT( ! m.haveModForField( "a" ) );
|
||||
|
||||
ASSERT( m.haveModForFieldOrSubfield( "x" ) );
|
||||
ASSERT( m.haveModForFieldOrSubfield( "a" ) );
|
||||
ASSERT( m.haveModForFieldOrSubfield( "a.b" ) );
|
||||
ASSERT( ! m.haveModForFieldOrSubfield( "a.bc" ) );
|
||||
ASSERT( ! m.haveModForFieldOrSubfield( "a.c" ) );
|
||||
ASSERT( ! m.haveModForFieldOrSubfield( "a.a" ) );
|
||||
}
|
||||
};
|
||||
|
||||
class Base {
|
||||
public:
|
||||
|
||||
@ -529,19 +551,43 @@ namespace UpdateTests {
|
||||
|
||||
void test( BSONObj morig , BSONObj in , BSONObj wanted ){
|
||||
|
||||
int its = 1000;
|
||||
double o = _test( morig , in , wanted , false , its );
|
||||
double n = _test( morig , in , wanted , true , its );
|
||||
double r = o / n;
|
||||
cout << " new is : " << r << " x faster" << endl;
|
||||
|
||||
BSONObj m = morig.copy();
|
||||
ModSet set;
|
||||
set.getMods( m );
|
||||
|
||||
BSONObj out = set.createNewFromMods( in );
|
||||
ASSERT_EQUALS( wanted , out );
|
||||
|
||||
m = morig.copy();
|
||||
BSONObj outr = set.createNewFromMods_r( in );
|
||||
cout << "out_l: " << out << endl;
|
||||
cout << "out_r: " << outr << endl;
|
||||
}
|
||||
|
||||
unsigned long long _test( BSONObj morig , BSONObj in , BSONObj wanted , bool newVersion , int its ){
|
||||
Timer t;
|
||||
for ( int i=0; i<its; i++ ){
|
||||
BSONObj m = morig.copy();
|
||||
ModSet set;
|
||||
set.getMods( m );
|
||||
BSONObj out;
|
||||
if ( newVersion )
|
||||
out = set.createNewFromMods_r( in );
|
||||
else
|
||||
out = set.createNewFromMods_l( in );
|
||||
|
||||
if ( wanted == out )
|
||||
continue;
|
||||
|
||||
cout << "wanted: " << wanted << " got: " << out << " newVersion: " << newVersion << " mod: " << morig << endl;
|
||||
|
||||
ASSERT_EQUALS( wanted , out );
|
||||
}
|
||||
return t.micros();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
class inc1 : public Base {
|
||||
@ -550,6 +596,7 @@ namespace UpdateTests {
|
||||
BSONObj m = BSON( "$inc" << BSON( "x" << 1 ) );
|
||||
test( m , BSON( "x" << 5 ) , BSON( "x" << 6 ) );
|
||||
test( m , BSON( "a" << 5 ) , BSON( "a" << 5 << "x" << 1 ) );
|
||||
test( m , BSON( "z" << 5 ) , BSON( "z" << 5 << "x" << 1 ) );
|
||||
}
|
||||
};
|
||||
|
||||
@ -559,6 +606,10 @@ namespace UpdateTests {
|
||||
BSONObj m = BSON( "$inc" << BSON( "a.b" << 1 ) );
|
||||
test( m , BSONObj() , BSON( "a" << BSON( "b" << 1 ) ) );
|
||||
test( m , BSON( "a" << BSON( "b" << 2 ) ) , BSON( "a" << BSON( "b" << 3 ) ) );
|
||||
|
||||
m = BSON( "$inc" << BSON( "a.b" << 1 << "a.c" << 1 ) );
|
||||
test( m , BSONObj() , BSON( "a" << BSON( "b" << 1 << "c" << 1 ) ) );
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
@ -620,6 +671,7 @@ namespace UpdateTests {
|
||||
add< CheckNoMods >();
|
||||
add< UpdateMissingToNull >();
|
||||
|
||||
add< ModSetTests::internal1 >();
|
||||
add< ModSetTests::inc1 >();
|
||||
add< ModSetTests::inc2 >();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user