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

array match value mod update( {a.x : 5 } , { a.~.y ... } ) SERVER-631

This commit is contained in:
Eliot Horowitz 2010-03-06 22:12:58 -05:00
parent da4507b7c6
commit d3da0d7bbc
5 changed files with 117 additions and 39 deletions

View File

@ -29,8 +29,8 @@
namespace mongo {
//#include "minilex.h"
//MiniLex minilex;
//#define DEBUGMATCHER(x) cout << x << endl;
#define DEBUGMATCHER(x)
class Where {
public:
@ -162,7 +162,7 @@ namespace mongo {
if ( details )
details->loadedObject = true;
return _docMatcher.matches(recLoc.rec());
return _docMatcher.matches(recLoc.rec() , details );
}
@ -423,8 +423,8 @@ namespace mongo {
return (op & z);
}
int Matcher::matchesNe(const char *fieldName, const BSONElement &toMatch, const BSONObj &obj, const ElementMatcher& bm ) {
int ret = matchesDotted( fieldName, toMatch, obj, BSONObj::Equality, bm );
int Matcher::matchesNe(const char *fieldName, const BSONElement &toMatch, const BSONObj &obj, const ElementMatcher& bm , MatchDetails * details ) {
int ret = matchesDotted( fieldName, toMatch, obj, BSONObj::Equality, bm , false , details );
if ( bm.toMatch.type() != jstNULL )
return ( ret <= 0 ) ? 1 : 0;
else
@ -456,7 +456,8 @@ namespace mongo {
0 missing element
1 match
*/
int Matcher::matchesDotted(const char *fieldName, const BSONElement& toMatch, const BSONObj& obj, int compareOp, const ElementMatcher& em , bool isArr) {
int Matcher::matchesDotted(const char *fieldName, const BSONElement& toMatch, const BSONObj& obj, int compareOp, const ElementMatcher& em , bool isArr, MatchDetails * details ) {
DEBUGMATCHER( "\t matchesDotted : " << fieldName << " hasDetails: " << ( details ? "yes" : "no" ) );
if ( compareOp == BSONObj::opALL ) {
if ( em.allMatchers.size() ){
@ -507,10 +508,10 @@ namespace mongo {
}
if ( compareOp == BSONObj::NE )
return matchesNe( fieldName, toMatch, obj, em );
return matchesNe( fieldName, toMatch, obj, em , details );
if ( compareOp == BSONObj::NIN ) {
for( set<BSONElement,element_lt>::const_iterator i = em.myset->begin(); i != em.myset->end(); ++i ) {
int ret = matchesNe( fieldName, *i, obj, em );
int ret = matchesNe( fieldName, *i, obj, em , details );
if ( ret != 1 )
return ret;
}
@ -540,12 +541,12 @@ namespace mongo {
;
else {
BSONObj eo = se.embeddedObject();
return matchesDotted(p+1, toMatch, eo, compareOp, em, se.type() == Array);
return matchesDotted(p+1, toMatch, eo, compareOp, em, se.type() == Array , details );
}
}
if ( isArr ) {
DEBUGMATCHER( "\t\t isArr 1 : obj : " << obj );
BSONObjIterator ai(obj);
bool found = false;
while ( ai.moreWithEOO() ) {
@ -553,15 +554,20 @@ namespace mongo {
if( strcmp(z.fieldName(),fieldName) == 0 && valuesMatch(z, toMatch, compareOp, em) ) {
// "field.<n>" array notation was used
if ( details )
details->elemMatchKey = z.fieldName();
return 1;
}
if ( z.type() == Object ) {
BSONObj eo = z.embeddedObject();
int cmp = matchesDotted(fieldName, toMatch, eo, compareOp, em, false);
int cmp = matchesDotted(fieldName, toMatch, eo, compareOp, em, false, details );
if ( cmp > 0 ) {
if ( details )
details->elemMatchKey = z.fieldName();
return 1;
} else if ( cmp < 0 ) {
}
else if ( cmp < 0 ) {
found = true;
}
}
@ -583,7 +589,7 @@ namespace mongo {
valuesMatch(e, toMatch, compareOp, em ) ) {
return 1;
} else if ( e.type() == Array && compareOp != BSONObj::opSIZE ) {
cout << "YES1" << endl;
BSONObjIterator ai(e.embeddedObject());
while ( ai.moreWithEOO() ) {
@ -591,11 +597,17 @@ namespace mongo {
if ( compareOp == BSONObj::opELEM_MATCH ){
// SERVER-377
if ( z.type() == Object && em.subMatcher->matches( z.embeddedObject() ) )
if ( z.type() == Object && em.subMatcher->matches( z.embeddedObject() ) ){
if ( details )
details->elemMatchKey = z.fieldName();
return 1;
}
}
else {
if ( valuesMatch( z, toMatch, compareOp, em) ) {
cout << "YO : " << z << endl;
if ( details )
details->elemMatchKey = z.fieldName();
return 1;
}
}
@ -634,7 +646,7 @@ namespace mongo {
/* See if an object matches the query.
*/
bool Matcher::matches(const BSONObj& jsobj ) {
bool Matcher::matches(const BSONObj& jsobj , MatchDetails * details ) {
/* assuming there is usually only one thing to match. if more this
could be slow sometimes. */
@ -643,7 +655,7 @@ namespace mongo {
ElementMatcher& bm = basics[i];
BSONElement& m = bm.toMatch;
// -1=mismatch. 0=missing element. 1=match
int cmp = matchesDotted(m.fieldName(), m, jsobj, bm.compareOp, bm );
int cmp = matchesDotted(m.fieldName(), m, jsobj, bm.compareOp, bm , false , details );
if ( bm.compareOp != BSONObj::opEXISTS && bm.isNot )
cmp = -cmp;
if ( cmp < 0 )

View File

@ -91,11 +91,18 @@ namespace mongo {
void reset(){
loadedObject = false;
elemMatchKey = BSONElement();
elemMatchKey = 0;
}
string toString() const {
stringstream ss;
ss << "loadedObject: " << loadedObject << " ";
ss << "elemMatchKey: " << ( elemMatchKey ? elemMatchKey : "NULL" ) << " ";
return ss.str();
}
bool loadedObject;
BSONElement elemMatchKey;
const char * elemMatchKey; // warning, this may go out of scope if matched object does
};
/* Match BSON objects against a query pattern.
@ -116,12 +123,12 @@ namespace mongo {
int matchesDotted(
const char *fieldName,
const BSONElement& toMatch, const BSONObj& obj,
int compareOp, const ElementMatcher& bm, bool isArr = false);
int compareOp, const ElementMatcher& bm, bool isArr , MatchDetails * details );
int matchesNe(
const char *fieldName,
const BSONElement &toMatch, const BSONObj &obj,
const ElementMatcher&bm);
const ElementMatcher&bm, MatchDetails * details );
public:
static int opDirection(int op) {
@ -134,7 +141,7 @@ namespace mongo {
~Matcher();
bool matches(const BSONObj& j);
bool matches(const BSONObj& j, MatchDetails * details = 0 );
bool keyMatch() const { return !all && !haveSize && !hasArray && !haveNeg; }

View File

@ -592,7 +592,7 @@ namespace mongo {
const BSONObj &from ,
const set<string>& idxKeys,
const set<string> *backgroundKeys)
: _isIndexed(0) {
: _isIndexed(0) , _hasDynamicArray( false ) {
BSONObjIterator it(from);
@ -618,6 +618,8 @@ namespace mongo {
uassert( 10152 , "Modifier $inc allowed for numbers only", f.isNumber() || op != Mod::INC );
uassert( 10153 , "Modifier $pushAll/pullAll allowed for arrays only", f.type() == Array || ( op != Mod::PUSH_ALL && op != Mod::PULL_ALL ) );
_hasDynamicArray = _hasDynamicArray || strstr( fieldName , ".~" ) > 0;
Mod m;
m.init( op , f );
m.setFieldName( f.fieldName() );
@ -633,6 +635,27 @@ namespace mongo {
}
ModSet * ModSet::fixDynamicArray( const char * elemMatchKey ) const {
ModSet * n = new ModSet();
n->_isIndexed = _isIndexed;
n->_hasDynamicArray = _hasDynamicArray;
for ( ModHolder::const_iterator i=_mods.begin(); i!=_mods.end(); i++ ){
string s = i->first;
size_t idx = s.find( ".~" );
if ( idx == string::npos ){
n->_mods[s] = i->second;
continue;
}
StringBuilder buf(s.size()+strlen(elemMatchKey));
buf << s.substr(0,idx+1) << elemMatchKey << s.substr(idx+2);
string fixed = buf.str();
n->_mods[fixed] = i->second;
ModHolder::iterator temp = n->_mods.find( fixed );
temp->second.setFieldName( temp->first.c_str() );
}
return n;
}
void checkNoMods( BSONObj o ) {
BSONObjIterator i( o );
while( i.moreWithEOO() ) {
@ -645,40 +668,42 @@ namespace mongo {
class UpdateOp : public QueryOp {
public:
UpdateOp() : nscanned_() {}
UpdateOp() : _nscanned() {}
virtual void init() {
BSONObj pattern = qp().query();
c_.reset( qp().newCursor().release() );
if ( !c_->ok() )
_c.reset( qp().newCursor().release() );
if ( ! _c->ok() )
setComplete();
else
matcher_.reset( new CoveredIndexMatcher( pattern, qp().indexKey() ) );
_matcher.reset( new CoveredIndexMatcher( pattern, qp().indexKey() ) );
}
virtual void next() {
if ( !c_->ok() ) {
if ( ! _c->ok() ) {
setComplete();
return;
}
nscanned_++;
if ( matcher_->matches(c_->currKey(), c_->currLoc()) ) {
_nscanned++;
if ( _matcher->matches(_c->currKey(), _c->currLoc(), &_details ) ) {
setComplete();
return;
}
c_->advance();
_c->advance();
}
bool curMatches(){
return matcher_->matches(c_->currKey(), c_->currLoc() );
return _matcher->matches(_c->currKey(), _c->currLoc() , &_details );
}
virtual bool mayRecordPlan() const { return false; }
virtual QueryOp *clone() const {
return new UpdateOp();
}
shared_ptr< Cursor > c() { return c_; }
long long nscanned() const { return nscanned_; }
shared_ptr< Cursor > c() { return _c; }
long long nscanned() const { return _nscanned; }
MatchDetails& getMatchDetails(){ return _details; }
private:
shared_ptr< Cursor > c_;
long long nscanned_;
auto_ptr< CoveredIndexMatcher > matcher_;
shared_ptr< Cursor > _c;
long long _nscanned;
auto_ptr< CoveredIndexMatcher > _matcher;
MatchDetails _details;
};
@ -776,7 +801,16 @@ namespace mongo {
const BSONObj& onDisk = loc.obj();
auto_ptr<ModSetState> mss = mods->prepare( onDisk );
ModSet * useMods = mods.get();
auto_ptr<ModSet> mymodset;
if ( u->getMatchDetails().elemMatchKey && mods->hasDynamicArray() ){
useMods = mods->fixDynamicArray( u->getMatchDetails().elemMatchKey );
mymodset.reset( useMods );
}
auto_ptr<ModSetState> mss = useMods->prepare( onDisk );
if ( modsIsIndexed <= 0 && mss->canApplyInPlace() ){
mss->applyModsInPlace();// const_cast<BSONObj&>(onDisk) );

View File

@ -153,6 +153,7 @@ namespace mongo {
s.insert( i.next() );
}
}
};
/**
@ -163,6 +164,7 @@ namespace mongo {
typedef map<string,Mod> ModHolder;
ModHolder _mods;
int _isIndexed;
bool _hasDynamicArray;
static void extractFields( map< string, BSONElement > &fields, const BSONElement &top, const string &base );
@ -255,6 +257,8 @@ namespace mongo {
return Mod::INC;
}
ModSet(){}
public:
ModSet( const BSONObj &from ,
@ -262,6 +266,11 @@ namespace mongo {
const set<string>* backgroundKeys = 0
);
// TODO: this is inefficient - should probably just handle when iterating
ModSet * fixDynamicArray( const char * elemMatchKey ) const;
bool hasDynamicArray() const { return _hasDynamicArray; }
/**
* creates a ModSetState suitable for operation on obj
* doesn't change or modify this ModSet or any underying Mod

View File

@ -0,0 +1,16 @@
t = db.update_arraymatch1
t.drop();
o = { _id : 1 , a : [ { x : 1 , y : 1 } , { x : 2 , y : 2 } , { x : 3 , y : 3 } ] }
t.insert( o );
assert.eq( o , t.findOne() , "A1" );
q = { "a.x" : 2 }
t.update( q , { $set : { b : 5 } } )
o.b = 5
assert.eq( o , t.findOne() , "A2" )
t.update( { "a.x" : 2 } , { $inc : { "a.~.y" : 1 } } )
o.a[1].y++;
assert.eq( o , t.findOne() , "A3" );