Permalink
Browse files

$pull uses a matcher now, so partial matches are supported

and things like { $pull : { a : { x : { $gt : 5 } } } }
SERVER-452
  • Loading branch information...
1 parent b07d09c commit 85799367e759ed309a1103659560799bd37dbcc8 @erh erh committed Dec 11, 2009
Showing with 78 additions and 13 deletions.
  1. +32 −13 db/update.cpp
  2. +15 −0 db/update.h
  3. +31 −0 jstests/pull2.js
View
@@ -26,6 +26,22 @@
namespace mongo {
+ bool Mod::_pullElementMatch( BSONElement& toMatch ) const {
+
+ if ( elt.type() != Object ){
+ // if elt isn't an object, then comparison will work
+ return toMatch.woCompare( elt , false ) == 0;
+ }
+
+ if ( toMatch.type() != Object ){
+ // looking for an object, so this can't match
+ return false;
+ }
+
+ // now we have an object on both sides
+ return matcher->matches( toMatch.embeddedObject() );
+ }
+
void Mod::apply( BSONObjBuilder& b , BSONElement in ){
switch ( op ){
@@ -95,7 +111,7 @@ namespace mongo {
bool allowed = true;
if ( op == PULL ){
- allowed = e.woCompare( elt , false ) != 0;
+ allowed = ! _pullElementMatch( e );
}
else {
BSONObjIterator j( elt.embeddedObject() );
@@ -194,9 +210,8 @@ namespace mongo {
while( inPlacePossible && i.more() ) {
BSONElement arrI = i.next();
if ( m.op == Mod::PULL ) {
- if ( arrI.woCompare( m.elt, false ) == 0 ) {
+ if ( m._pullElementMatch( arrI ) )
inPlacePossible = false;
- }
}
else if ( m.op == Mod::PULL_ALL ) {
BSONObjIterator j( m.elt.embeddedObject() );
@@ -359,7 +374,7 @@ namespace mongo {
createNewFromMods( "" , b , obj );
return b.obj();
}
-
+
/* get special operations like $inc
{ $inc: { a:1, b:1 } }
{ $set: { a:77 } }
@@ -382,16 +397,19 @@ namespace mongo {
strcpy((char *) fn, "$set"); // rewrite for op log
while ( jt.more() ) {
BSONElement f = jt.next();
- Mod m;
- m.op = op;
- 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 ] != '.' );
- uassert( "Field name duplication not allowed with modifiers", ! haveModForField( m.fieldName ) );
- uassert( "have conflict mod" , ! haveConflictingMod( m.fieldName ) );
+
+ const char * fieldName = f.fieldName();
+
+ uassert( "Mod on _id not allowed", strcmp( fieldName, "_id" ) != 0 );
+ uassert( "Invalid mod field name, may not end in a period", fieldName[ strlen( fieldName ) - 1 ] != '.' );
+ uassert( "Field name duplication not allowed with modifiers", ! haveModForField( fieldName ) );
+ uassert( "have conflict mod" , ! haveConflictingMod( 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;
+
+ Mod m;
+ m.init( op , f );
+ m.setFieldName( f.fieldName() );
// horrible - to be cleaned up
if ( f.type() == NumberDouble ) {
@@ -406,11 +424,12 @@ namespace mongo {
m.nint = 0;
m.nlong = (long long *) f.value();
}
+
_mods[m.fieldName] = m;
}
}
}
-
+
void checkNoMods( BSONObj o ) {
BSONObjIterator i( o );
while( i.moreWithEOO() ) {
View
@@ -19,6 +19,7 @@
#include "../stdafx.h"
#include "jsobj.h"
#include "../util/embedded_builder.h"
+#include "matcher.h"
namespace mongo {
@@ -36,6 +37,14 @@ namespace mongo {
BSONElement elt;
int pushStartSize;
+ boost::shared_ptr<JSMatcher> matcher;
+
+ void init( Op o , BSONElement& e ){
+ op = o;
+ elt = e;
+ if ( op == PULL && e.type() == Object )
+ matcher.reset( new JSMatcher( e.embeddedObject() ) );
+ }
void setFieldName( const char * s ){
fieldName = s;
@@ -107,6 +116,11 @@ namespace mongo {
}
void apply( BSONObjBuilder& b , BSONElement in );
+
+ /**
+ * @return true iff toMatch should be removed from the array
+ */
+ bool _pullElementMatch( BSONElement& toMatch ) const;
};
class ModSet {
@@ -188,6 +202,7 @@ namespace mongo {
uassert( "Invalid modifier specified " + string( fn ), false );
return Mod::INC;
}
+
public:
void getMods( const BSONObj &from );
View
@@ -0,0 +1,31 @@
+
+t = db.pull2;
+t.drop();
+
+t.save( { a : [ { x : 1 } , { x : 1 , b : 2 } ] } );
+assert.eq( 2 , t.findOne().a.length , "A" );
+
+t.update( {} , { $pull : { a : { x : 1 } } } );
+assert.eq( 0 , t.findOne().a.length , "B" );
+
+assert.eq( 1 , t.find().count() , "C1" )
+
+t.update( {} , { $push : { a : { x : 1 } } } )
+t.update( {} , { $push : { a : { x : 1 , b : 2 } } } )
+assert.eq( 2 , t.findOne().a.length , "C" );
+
+t.update( {} , { $pullAll : { a : [ { x : 1 } ] } } );
+assert.eq( 1 , t.findOne().a.length , "D" );
+
+t.update( {} , { $push : { a : { x : 2 , b : 2 } } } )
+t.update( {} , { $push : { a : { x : 3 , b : 2 } } } )
+t.update( {} , { $push : { a : { x : 4 , b : 2 } } } )
+assert.eq( 4 , t.findOne().a.length , "E" );
+
+assert.eq( 1 , t.find().count() , "C2" )
+
+
+t.update( {} , { $pull : { a : { x : { $lt : 3 } } } } );
+assert.eq( 2 , t.findOne().a.length , "F" );
+assert.eq( [ 3 , 4 ] , t.findOne().a.map( function(z){ return z.x; } ) , "G" )
+

0 comments on commit 8579936

Please sign in to comment.