From ede1f1e2ba3faaacfa1d315c089aee1210bbc7dd Mon Sep 17 00:00:00 2001 From: Dwight Date: Tue, 18 Nov 2008 15:47:37 -0500 Subject: big change for stopOnMiss --- db/btree.h | 13 +- db/btreecursor.cpp | 5 +- db/clientcursor.h | 1 + db/cursor.h | 2 +- db/db.vcproj | 1964 ++++++++++++++++++++++++++-------------------------- db/jsobj.h | 97 +-- db/matcher.cpp | 156 +++-- db/matcher.h | 126 ++++ db/namespace.cpp | 2 +- db/namespace.h | 4 +- db/query.cpp | 67 +- db/scanandorder.h | 168 ++--- 12 files changed, 1350 insertions(+), 1255 deletions(-) create mode 100644 db/matcher.h (limited to 'db') diff --git a/db/btree.h b/db/btree.h index 05c0c7f62ef..0fddc2faac4 100644 --- a/db/btree.h +++ b/db/btree.h @@ -171,12 +171,13 @@ private: class BtreeCursor : public Cursor { friend class BtreeBucket; +// BSONObj query; // the query we are working on in association with the cursor -- see noMoreMatches() public: - BtreeCursor(IndexDetails&, BSONObj& startKey, int direction, bool stopmiss); + BtreeCursor(IndexDetails&, BSONObj& startKey, int direction, BSONObj& query); virtual bool ok() { return !bucket.isNull(); } bool eof() { return !ok(); } virtual bool advance(); - virtual bool tempStopOnMiss() { return stopmiss; } + virtual void noteLocation(); // updates keyAtKeyOfs... virtual void checkLocation(); @@ -190,6 +191,11 @@ public: assert( !bucket.isNull() ); return bucket.btree()->keyNode(keyOfs); } + BSONObj currKey() { return currKeyNode().key; } + + virtual BSONObj indexKeyPattern() { + return indexDetails.keyPattern(); + } virtual void aboutToDeleteBucket(const DiskLoc& b) { if( bucket == b ) @@ -202,7 +208,7 @@ public: virtual string toString() { string s = string("BtreeCursor ") + indexDetails.indexName(); if( direction < 0 ) s += " reverse"; - if( stopmiss ) s += " stopmiss"; + //if( stopmiss ) s += " stopmiss"; return s; } @@ -212,7 +218,6 @@ private: DiskLoc bucket; int keyOfs; int direction; // 1=fwd,-1=reverse - bool stopmiss; BSONObj keyAtKeyOfs; // so we can tell if things moved around on us between the query and the getMore call DiskLoc locAtKeyOfs; }; diff --git a/db/btreecursor.cpp b/db/btreecursor.cpp index ef52d4fcebf..818768687ef 100644 --- a/db/btreecursor.cpp +++ b/db/btreecursor.cpp @@ -25,9 +25,10 @@ extern int otherTraceLevel; DiskLoc maxDiskLoc(0x7fffffff, 0x7fffffff); DiskLoc minDiskLoc(0, 1); -BtreeCursor::BtreeCursor(IndexDetails& _id, BSONObj& k, int _direction, bool sm) : +BtreeCursor::BtreeCursor(IndexDetails& _id, BSONObj& k, int _direction, BSONObj& _query) : +// query(_query), indexDetails(_id), - direction(_direction), stopmiss(sm) + direction(_direction) { //otherTraceLevel = 999; diff --git a/db/clientcursor.h b/db/clientcursor.h index 62ef1f90604..940a99ec301 100644 --- a/db/clientcursor.h +++ b/db/clientcursor.h @@ -43,6 +43,7 @@ public: ~ClientCursor(); const CursorId cursorid; string ns; + //BSONObj pattern; // the query object auto_ptr matcher; auto_ptr c; int pos; diff --git a/db/cursor.h b/db/cursor.h index 3713e529868..ecd417ed433 100644 --- a/db/cursor.h +++ b/db/cursor.h @@ -47,7 +47,7 @@ public: /* optional to implement. if implemented, means 'this' is a prototype */ virtual Cursor* clone() { return 0; } - virtual bool tempStopOnMiss() { return false; } + virtual BSONObj indexKeyPattern() { return BSONObj(); } /* called after every query block is iterated -- i.e. between getMore() blocks so you can note where we are, if necessary. diff --git a/db/db.vcproj b/db/db.vcproj index fc1923017c2..9de0e1860e4 100644 --- a/db/db.vcproj +++ b/db/db.vcproj @@ -1,980 +1,984 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/db/jsobj.h b/db/jsobj.h index 9f858b04880..e5b9b5928a4 100644 --- a/db/jsobj.h +++ b/db/jsobj.h @@ -332,6 +332,7 @@ public: return BSONElement(objdata() + 4); } BSONElement findElement(const char *name); + bool hasElement(const char *name); OID* getOID() { BSONElement e = firstElement(); @@ -516,89 +517,7 @@ public: #include -class RegexMatcher { -public: - const char *fieldName; - pcrecpp::RE *re; - RegexMatcher() { re = 0; } - ~RegexMatcher() { delete re; } -}; - -// SQL where clause equivalent -class Where; - -/* For when a js object is a query pattern. - - e.g. - db.foo.find( { a : 3 } ); - - { a : 3 } is the pattern object. - - GT/LT: - { a : { $gt : 3 } } - - Not equal: - { a : { $ne : 3 } } - - TODO: we should rewrite the matcher to be more an AST style. -*/ -class JSMatcher : boost::noncopyable { - int matchesDotted( - const char *fieldName, - BSONElement& toMatch, BSONObj& obj, - int compareOp, bool *deep, bool isArr = false); - - struct element_lt - { - bool operator()(const BSONElement& l, const BSONElement& r) const - { - int x = (int) l.type() - (int) r.type(); - if( x < 0 ) return true; - if( x > 0 ) return false; - return compareElementValues(l,r) < 0; - } - }; -public: - enum { - Equality = 0, - LT = 0x1, - LTE = 0x3, - GTE = 0x6, - GT = 0x4, - opIN = 0x8, // { x : { $in : [1,2,3] } } - NE = 0x9 - }; - - static int opDirection(int op) { - return op <= LTE ? -1 : 1; - } - - JSMatcher(BSONObj& pattern); - - ~JSMatcher(); - - /* deep means we looked into arrays for a match */ - bool matches(BSONObj& j, bool *deep = 0); - - int getN() { return n; } - -private: - int valuesMatch(BSONElement& l, BSONElement& r, int op); - - set *in; - Where *where; - BSONObj& jsobj; - vector toMatch; - vector compareOp; - int n; - - RegexMatcher regexs[4]; - int nRegex; - - // so we delete the mem when we're done: - BSONObjBuilder *builders[8]; - int nBuilders; -}; +#include "matcher.h" extern BSONObj maxKey; @@ -654,6 +573,18 @@ inline BSONObj BSONElement::wrap() { return b.doneAndDecouple(); } +inline bool BSONObj::hasElement(const char *name) { + if( !isEmpty() ) { + BSONObjIterator it(*this); + while( it.more() ) { + BSONElement e = it.next(); + if( strcmp(name, e.fieldName()) == 0 ) + return true; + } + } + return false; +} + inline BSONElement BSONObj::findElement(const char *name) { if( !isEmpty() ) { BSONObjIterator it(*this); diff --git a/db/matcher.cpp b/db/matcher.cpp index 6112eaf5603..df0622e2000 100644 --- a/db/matcher.cpp +++ b/db/matcher.cpp @@ -123,9 +123,14 @@ JSMatcher::~JSMatcher() { #include "pdfile.h" -JSMatcher::JSMatcher(BSONObj &_jsobj) : +/* _jsobj - the query pattern + indexKeyPattern - the "key pattern" / template of what is in the keys of the index we are using. + used to set indexMatches return value from matches() +*/ +JSMatcher::JSMatcher(BSONObj &_jsobj, BSONObj indexKeyPattern) : in(0), where(0), jsobj(_jsobj), nRegex(0) { + checkInIndex = !indexKeyPattern.isEmpty(); nBuilders = 0; BSONObjIterator i(jsobj); n = 0; @@ -136,9 +141,9 @@ JSMatcher::JSMatcher(BSONObj &_jsobj) : if( ( e.type() == CodeWScope || e.type() == Code ) && strcmp(e.fieldName(), "$where")==0 ) { // $where: function()... - assert( where == 0 ); + uassert( "$where occurs twice?", where == 0 ); where = new Where(); - massert( "$where query, but jni is disabled", JavaJS ); + uassert( "$where query, but jni is disabled", JavaJS ); where->scope = JavaJS->scopeCreate(); JavaJS->scopeSetString(where->scope, "$client", client->name.c_str()); @@ -171,8 +176,11 @@ JSMatcher::JSMatcher(BSONObj &_jsobj) : options.set_extended(true); flags++; } - regexs[nRegex].re = new pcrecpp::RE(e.regex(), options); - regexs[nRegex].fieldName = e.fieldName(); + RegexMatcher& rm = regexs[nRegex]; + rm.re = new pcrecpp::RE(e.regex(), options); + rm.fieldName = e.fieldName(); + rm.inIndex = indexKeyPattern.hasElement(rm.fieldName); + nRegex++; } continue; @@ -215,9 +223,7 @@ JSMatcher::JSMatcher(BSONObj &_jsobj) : BSONObjBuilder *b = new BSONObjBuilder(); builders[nBuilders++] = b; b->appendAs(fe, e.fieldName()); - toMatch.push_back( b->done().firstElement() ); - compareOp.push_back(op); - n++; + addBasic(b->done().firstElement(), op, indexKeyPattern); ok = true; } } @@ -228,9 +234,7 @@ JSMatcher::JSMatcher(BSONObj &_jsobj) : BSONObjBuilder *b = new BSONObjBuilder(); builders[nBuilders++] = b; b->appendAs(fe, e.fieldName()); - toMatch.push_back( b->done().firstElement() ); - compareOp.push_back(NE); - n++; + addBasic(b->done().firstElement(), NE, indexKeyPattern); ok = true; } else @@ -238,7 +242,7 @@ JSMatcher::JSMatcher(BSONObj &_jsobj) : } else if( fn[1] == 'i' && fn[2] == 'n' && fn[3] == 0 && fe.type() == Array ) { // $in - assert( in == 0 ); // only one per query supported so far. finish... + uassert( "only 1 $in statement per query supported", in == 0 ); // todo... in = new set(); BSONObjIterator i(fe.embeddedObject()); if( i.more() ) { @@ -249,9 +253,7 @@ JSMatcher::JSMatcher(BSONObj &_jsobj) : in->insert(ie); } } - toMatch.push_back(e); // not actually used at the moment - compareOp.push_back(opIN); - n++; + addBasic(e, opIN, indexKeyPattern); // e not actually used at the moment for $in ok = true; } else @@ -266,12 +268,8 @@ JSMatcher::JSMatcher(BSONObj &_jsobj) : continue; } - { - // normal, simple case e.g. { a : "foo" } - toMatch.push_back(e); - compareOp.push_back(Equality); - n++; - } + // normal, simple case e.g. { a : "foo" } + addBasic(e, Equality, indexKeyPattern); } } @@ -409,10 +407,23 @@ inline bool regexMatches(RegexMatcher& rm, BSONElement& e, bool *deep) { return false; } +#define FAILURE(inIndex) { \ + ok=false; \ + if( inIndex ) { indexMatches=false; return false; } \ + if( !checkInIndex ) return false; } + /* See if an object matches the query. - deep - return true when meanswe looked into arrays for a match + deep - return true when means we looked into arrays for a match + indexMatches - true if the index we are using, its fields match, even if other stuff doesn't. + + Wondering if it would be worth having + if( !inIndex && !ok ) continue; + in each loop to bypass those checks. probably not worth checking as usually we are ok. */ -bool JSMatcher::matches(BSONObj& jsobj, bool *deep) { +bool JSMatcher::matches(BSONObj& jsobj, bool& indexMatches, bool *deep) { + indexMatches = true; + bool ok = true; + if( deep ) *deep = false; @@ -421,20 +432,23 @@ bool JSMatcher::matches(BSONObj& jsobj, bool *deep) { // check normal non-regex cases: for( int i = 0; i < n; i++ ) { - BSONElement& m = toMatch[i]; + BasicMatcher& bm = basics[i]; + BSONElement& m = bm.toMatch; // -1=mismatch. 0=missing element. 1=match - int cmp = matchesDotted(toMatch[i].fieldName(), toMatch[i], jsobj, compareOp[i], deep); + int cmp = matchesDotted(m.fieldName(), m, jsobj, bm.compareOp, deep); - if( cmp < 0 ) - return false; + bool res = true; + if( cmp < 0 ) + FAILURE(bm.inIndex) if( cmp == 0 ) { /* missing is ok iff we were looking for null */ if( m.type() == jstNULL || m.type() == Undefined ) { - if( compareOp[i] == NE ) { - return false; + if( bm.compareOp == NE ) { + FAILURE(bm.inIndex) } - } else - return false; + } else { + FAILURE(bm.inIndex) + } } } @@ -442,39 +456,46 @@ bool JSMatcher::matches(BSONObj& jsobj, bool *deep) { RegexMatcher& rm = regexs[r]; BSONElement e = jsobj.getFieldDotted(rm.fieldName); if( e.eoo() ) - return false; + FAILURE(rm.inIndex) if( !regexMatches(rm, e, deep) ) - return false; + FAILURE(rm.inIndex) } - if( where ) { - if( where->func == 0 ) { - uassert("compile failure", false); - return false; // didn't compile - } + if( where ) { + if( !ok ) { + /* we had already mismatched and were just looking for an index mismatch. + as $where doesn't support inIndex yet, no need to keep going here. + */ + return false; + } + + if( where->func == 0 ) { + uassert("$where compile error", false); + return false; // didn't compile + } - /**if( 1 || jsobj.objsize() < 200 || where->fullObject ) */ + /**if( 1 || jsobj.objsize() < 200 || where->fullObject ) */ { - if ( where->jsScope ){ - JavaJS->scopeInit( where->scope , where->jsScope ); - } - JavaJS->scopeSetThis(where->scope, &jsobj); - JavaJS->scopeSetObject(where->scope, "obj", &jsobj); - } - /*else { - BSONObjBuilder b; - where->buildSubset(jsobj, b); - BSONObj temp = b.done(); - JavaJS->scopeSetObject(where->scope, "obj", &temp); - }*/ - if( JavaJS->invoke(where->scope, where->func) ) { - uassert("error in invocation of $where function", false); - return false; - } - return JavaJS->scopeGetBoolean(where->scope, "return") != 0; - } + if ( where->jsScope ){ + JavaJS->scopeInit( where->scope , where->jsScope ); + } + JavaJS->scopeSetThis(where->scope, &jsobj); + JavaJS->scopeSetObject(where->scope, "obj", &jsobj); + } + /*else { + BSONObjBuilder b; + where->buildSubset(jsobj, b); + BSONObj temp = b.done(); + JavaJS->scopeSetObject(where->scope, "obj", &temp); + }*/ + if( JavaJS->invoke(where->scope, where->func) ) { + uassert("error in invocation of $where function", false); + return false; + } + return JavaJS->scopeGetBoolean(where->scope, "return") != 0; + } - return true; + return true; } struct JSObj1 js1; @@ -497,21 +518,22 @@ struct JSObj2 { struct JSUnitTest : public UnitTest { void run() { + bool im;//indexMatches BSONObj j1((const char *) &js1); BSONObj j2((const char *) &js2); - JSMatcher m(j2); - assert( m.matches(j1) ); + JSMatcher m(j2, BSONObj()); + assert( m.matches(j1,im) ); js2.sval[0] = 'z'; - assert( !m.matches(j1) ); - JSMatcher n(j1); - assert( n.matches(j1) ); - assert( !n.matches(j2) ); + assert( !m.matches(j1,im) ); + JSMatcher n(j1, BSONObj()); + assert( n.matches(j1,im) ); + assert( !n.matches(j2,im) ); BSONObj j0 = emptyObj; // BSONObj j0((const char *) &js0); - JSMatcher p(j0); - assert( p.matches(j1) ); - assert( p.matches(j2) ); + JSMatcher p(j0, BSONObj()); + assert( p.matches(j1,im) ); + assert( p.matches(j2,im) ); } } jsunittest; diff --git a/db/matcher.h b/db/matcher.h new file mode 100644 index 00000000000..8728126d4c8 --- /dev/null +++ b/db/matcher.h @@ -0,0 +1,126 @@ +// matcher.h + +/* JSMatcher is our boolean expression evaluator for "where" clauses */ + +/** +* Copyright (C) 2008 10gen Inc. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License, version 3, +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ + +#pragma once + +class RegexMatcher { +public: + const char *fieldName; + pcrecpp::RE *re; + bool inIndex; + RegexMatcher() { re = 0; inIndex = false; } + ~RegexMatcher() { delete re; } +}; + +class BasicMatcher { +public: + BSONElement toMatch; + int compareOp; + bool inIndex; +}; + +// SQL where clause equivalent +class Where; + +/* Match BSON objects against a query pattern. + + e.g. + db.foo.find( { a : 3 } ); + + { a : 3 } is the pattern object. + + GT/LT: + { a : { $gt : 3 } } + + Not equal: + { a : { $ne : 3 } } + + TODO: we should rewrite the matcher to be more an AST style. +*/ +class JSMatcher : boost::noncopyable { + int matchesDotted( + const char *fieldName, + BSONElement& toMatch, BSONObj& obj, + int compareOp, bool *deep, bool isArr = false); + + struct element_lt + { + bool operator()(const BSONElement& l, const BSONElement& r) const + { + int x = (int) l.type() - (int) r.type(); + if( x < 0 ) return true; + if( x > 0 ) return false; + return compareElementValues(l,r) < 0; + } + }; +public: + enum { + Equality = 0, + LT = 0x1, + LTE = 0x3, + GTE = 0x6, + GT = 0x4, + opIN = 0x8, // { x : { $in : [1,2,3] } } + NE = 0x9 + }; + + static int opDirection(int op) { + return op <= LTE ? -1 : 1; + } + + JSMatcher(BSONObj& pattern, BSONObj indexKeyPattern); + + ~JSMatcher(); + + /* indexMatches - true if indexKeyPattern matched ok. + deep - means we looked into arrays for a match + */ + bool matches(BSONObj& j, bool& indexMatches, bool *deep = 0); + + int getN() { return n; } + +private: + void addBasic(BSONElement e, int c, BSONObj& indexKeyPattern) { + BasicMatcher bm; + bm.toMatch = e; + bm.compareOp = c; + bm.inIndex = indexKeyPattern.hasElement(e.fieldName()); + basics.push_back(bm); + n++; + } + + int valuesMatch(BSONElement& l, BSONElement& r, int op); + + set *in; // set if query uses $in + Where *where; // set if query uses $where + BSONObj& jsobj; // the query pattern. e.g., { name: "joe" } + + vector basics; + int n; // # of basicmatcher items + + RegexMatcher regexs[4]; + int nRegex; + + // so we delete the mem when we're done: + BSONObjBuilder *builders[8]; + int nBuilders; + + bool checkInIndex; +}; diff --git a/db/namespace.cpp b/db/namespace.cpp index 7592ad32290..a63e3eae225 100644 --- a/db/namespace.cpp +++ b/db/namespace.cpp @@ -314,7 +314,7 @@ void NamespaceDetailsTransient::computeIndexKeys() { NamespaceDetails *d = nsdetails(ns.c_str()); for( int i = 0; i < d->nIndexes; i++ ) { // set fields; - d->indexes[i].key().getFieldNames(allIndexKeys); + d->indexes[i].keyPattern().getFieldNames(allIndexKeys); // allIndexKeys.insert(fields.begin(),fields.end()); } } diff --git a/db/namespace.h b/db/namespace.h index a880cf8d267..b66e56111bc 100644 --- a/db/namespace.h +++ b/db/namespace.h @@ -78,7 +78,7 @@ public: handles our embedded dot notation too. */ BSONObj getKeyFromQuery(BSONObj& query) { - BSONObj k = key(); + BSONObj k = keyPattern(); BSONObj res = query.extractFieldsUnDotted(k); assert(res.objsize() != 0); // guard against a seg fault if details is 0 return res; @@ -94,7 +94,7 @@ public: /* get the key pattern for this object. e.g., { lastname:1, firstname:1 } */ - BSONObj key() { + BSONObj keyPattern() { return info.obj().getObjectField("key"); } diff --git a/db/query.cpp b/db/query.cpp index bf6faa6d896..cf9af2a545b 100644 --- a/db/query.cpp +++ b/db/query.cpp @@ -67,12 +67,11 @@ auto_ptr getIndexCursor(const char *ns, BSONObj& query, BSONObj& order, if( ii.indexName() == *hint ) { BSONObj startKey = ii.getKeyFromQuery(query); int direction = 1; - bool stopMiss = true; if( simpleKeyMatch ) *simpleKeyMatch = query.nFields() == startKey.nFields(); if( isSorted ) *isSorted = false; return auto_ptr( - new BtreeCursor(ii, startKey, direction, stopMiss)); + new BtreeCursor(ii, startKey, direction, query)); } } } @@ -97,7 +96,7 @@ auto_ptr getIndexCursor(const char *ns, BSONObj& query, BSONObj& order, DEV cout << " using index " << d->indexes[i].indexNamespace() << '\n'; if( isSorted ) *isSorted = true; - return auto_ptr(new BtreeCursor(d->indexes[i], reverse ? maxKey : emptyObj, reverse ? -1 : 1, false)); + return auto_ptr(new BtreeCursor(d->indexes[i], reverse ? maxKey : emptyObj, reverse ? -1 : 1, query)); } } } @@ -168,8 +167,7 @@ auto_ptr getIndexCursor(const char *ns, BSONObj& query, BSONObj& order, return auto_ptr( new BtreeCursor( d->indexes[i], direction == 1 ? emptyObj : maxKey, - direction, - true) ); + direction, query) ); } } @@ -203,7 +201,7 @@ auto_ptr getIndexCursor(const char *ns, BSONObj& query, BSONObj& order, DEV cout << "using index " << d->indexes[i].indexNamespace() << endl; if( simple && simpleKeyMatch ) *simpleKeyMatch = true; return auto_ptr( - new BtreeCursor(d->indexes[i], q2, 1, true)); + new BtreeCursor(d->indexes[i], q2, 1, query)); } } @@ -231,26 +229,29 @@ int deleteObjects(const char *ns, BSONObj pattern, bool justOne, bool god) { } int nDeleted = 0; - JSMatcher matcher(pattern); BSONObj order; auto_ptr c = getIndexCursor(ns, pattern, order); if( c.get() == 0 ) c = theDataFileMgr.findAll(ns); + JSMatcher matcher(pattern, c->indexKeyPattern()); Cursor &tempDebug = *c; while( c->ok() ) { Record *r = c->_current(); DiskLoc rloc = c->currLoc(); - c->advance(); // must advance before deleting as the next ptr will die BSONObj js(r); bool deep; - if( !matcher.matches(js, &deep) ) { - if( c->tempStopOnMiss() ) - break; + bool indexMatches; + if( !matcher.matches(js, indexMatches, &deep) ) { + if( !indexMatches ) + break; + c->advance(); // advance must be after noMoreMatches() because it uses currKey() } - else { + else { + c->advance(); // must advance before deleting as the next ptr will die + assert( !deep || !c->getsetdup(rloc) ); // can't be a dup, we deleted it! if( !justOne ) c->noteLocation(); @@ -366,18 +367,19 @@ int _updateObjects(const char *ns, BSONObj updateobj, BSONObj pattern, bool upse int nscanned = 0; { - JSMatcher matcher(pattern); BSONObj order; auto_ptr c = getIndexCursor(ns, pattern, order); if( c.get() == 0 ) c = theDataFileMgr.findAll(ns); + JSMatcher matcher(pattern, c->indexKeyPattern()); while( c->ok() ) { Record *r = c->_current(); nscanned++; BSONObj js(r); - if( !matcher.matches(js) ) { - if( c->tempStopOnMiss() ) - break; + bool indexMatches; + if( !matcher.matches(js, indexMatches) ) { + if( !indexMatches ) + break; } else { /* note: we only update one row and quit. if you do multiple later, @@ -539,13 +541,14 @@ int runCount(const char *ns, BSONObj& cmd, string& err) { } int count = 0; - auto_ptr matcher(new JSMatcher(query)); + auto_ptr matcher(new JSMatcher(query, c->indexKeyPattern())); while( c->ok() ) { BSONObj js = c->current(); bool deep; - if( !matcher->matches(js, &deep) ) { - if( c->tempStopOnMiss() ) - break; + bool indexMatches; + if( !matcher->matches(js, indexMatches, &deep) ) { + if( !indexMatches ) + break; } else if( !deep || !c->getsetdup(c->currLoc()) ) { // i.e., check for dups on deep items only // got a match. @@ -645,10 +648,6 @@ QueryResult* runQuery(Message& message, const char *ns, int ntoskip, int _ntoret uassert("bad query object", false); } - auto_ptr matcher(new JSMatcher(query)); - JSMatcher &debug1 = *matcher; - assert( debug1.getN() < 5000 ); - bool isSorted = false; auto_ptr c = getSpecialCursor(ns); @@ -657,6 +656,10 @@ QueryResult* runQuery(Message& message, const char *ns, int ntoskip, int _ntoret if( c.get() == 0 ) c = findTableScan(ns, order, &isSorted); + auto_ptr matcher(new JSMatcher(query, c->indexKeyPattern())); + JSMatcher &debug1 = *matcher; + assert( debug1.getN() < 1000 ); + auto_ptr so; bool ordering = false; if( !order.isEmpty() && !isSorted ) { @@ -673,10 +676,10 @@ QueryResult* runQuery(Message& message, const char *ns, int ntoskip, int _ntoret // cout << " checking against:\n " << js.toString() << endl; nscanned++; bool deep; - - if( !matcher->matches(js, &deep) ) { - if( c->tempStopOnMiss() ) - break; + bool indexMatches; + if( !matcher->matches(js, indexMatches, &deep) ) { + if( !indexMatches ) + break; } else if( !deep || !c->getsetdup(c->currLoc()) ) { // i.e., check for dups on deep items only // got a match. @@ -716,6 +719,7 @@ QueryResult* runQuery(Message& message, const char *ns, int ntoskip, int _ntoret cc->c = c; cursorid = cc->cursorid; DEV cout << " query has more, cursorid: " << cursorid << endl; + //cc->pattern = query; cc->matcher = matcher; cc->ns = ns; cc->pos = n; @@ -754,6 +758,7 @@ QueryResult* runQuery(Message& message, const char *ns, int ntoskip, int _ntoret cc->c = c; cursorid = cc->cursorid; DEV cout << " query has no more but tailable, cursorid: " << cursorid << endl; + //cc->pattern = query; cc->matcher = matcher; cc->ns = ns; cc->pos = n; @@ -844,9 +849,9 @@ done: BSONObj js = c->current(); bool deep; - - if( !cc->matcher->matches(js, &deep) ) { - if( c->tempStopOnMiss() ) + bool indexMatches; + if( !cc->matcher->matches(js, indexMatches, &deep) ) { + if( !indexMatches ) goto done; } else { diff --git a/db/scanandorder.h b/db/scanandorder.h index a3f2a613313..6a1619bae6f 100644 --- a/db/scanandorder.h +++ b/db/scanandorder.h @@ -63,92 +63,92 @@ inline bool fillQueryResultFromObj(BufBuilder& b, set *filter, BSONObj& typedef multimap BestMap; class ScanAndOrder { - BestMap best; // key -> full object - int startFrom; - int limit; // max to send back. - KeyType order; - int dir; - unsigned approxSize; - - void _add(BSONObj& k, BSONObj o) { - best.insert(make_pair(k,o)); - } - - // T may be iterator or reverse_iterator - void _addIfBetter(BSONObj& k, BSONObj o, BestMap::iterator i) { - const BSONObj& worstBestKey = i->first; - int c = worstBestKey.woCompare(k); - if( (c<0 && dir<0) || (c>0&&dir>0) ) { - // k is better, 'upgrade' - best.erase(i); - _add(k, o); - } - } + BestMap best; // key -> full object + int startFrom; + int limit; // max to send back. + KeyType order; + int dir; + unsigned approxSize; -public: - ScanAndOrder(int _startFrom, int _limit, BSONObj _order) : - startFrom(_startFrom), order(_order) { - limit = _limit > 0 ? _limit + startFrom : 0x7fffffff; - approxSize = 0; - - // todo: do order right for compound keys. this is temp. - dir = 1; - BSONElement e = order.pattern.firstElement(); - if( e.number() < 0 ) { - dir = -1; + void _add(BSONObj& k, BSONObj o) { + best.insert(make_pair(k,o)); } - } - - int size() const { return best.size(); } - - void add(BSONObj o) { - BSONObj k = order.getKeyFromObject(o); - if( (int) best.size() < limit ) { - approxSize += k.objsize(); - uassert( "too much key data for sort() with no index", approxSize < 1 * 1024 * 1024 ); - _add(k, o); - return; - } - BestMap::iterator i; - if( dir < 0 ) - i = best.begin(); - else { - assert( best.end() != best.begin() ); - i = best.end(); i--; + + // T may be iterator or reverse_iterator + void _addIfBetter(BSONObj& k, BSONObj o, BestMap::iterator i) { + const BSONObj& worstBestKey = i->first; + int c = worstBestKey.woCompare(k); + if( (c<0 && dir<0) || (c>0&&dir>0) ) { + // k is better, 'upgrade' + best.erase(i); + _add(k, o); + } } - _addIfBetter(k, o, i); - } - - template - void _fill(BufBuilder& b, set *filter, int& nout, T begin, T end) { - int n = 0; - int nFilled = 0; - for( T i = begin; i != end; i++ ) { - n++; - if( n <= startFrom ) - continue; - BSONObj& o = i->second; - if( fillQueryResultFromObj(b, filter, o) ) { - nFilled++; - if( nFilled >= limit ) - goto done; - uassert( "too much data for sort() with no index", b.len() < 4000000 ); // appserver limit + +public: + ScanAndOrder(int _startFrom, int _limit, BSONObj _order) : + startFrom(_startFrom), order(_order) { + limit = _limit > 0 ? _limit + startFrom : 0x7fffffff; + approxSize = 0; + + // todo: do order right for compound keys. this is temp. + dir = 1; + BSONElement e = order.pattern.firstElement(); + if( e.number() < 0 ) { + dir = -1; + } } - } - done: - nout = nFilled; - } - - /* scanning complete. stick the query result in b for n objects. */ - void fill(BufBuilder& b, set *filter, int& nout) { - // for( BestMap::iterator i = best.begin(); i != best.end(); i++ ) - // cout << " fill:" << i->first.toString() << endl; - // for( BestMap::reverse_iterator i = best.rbegin(); i != best.rend(); i++ ) - // cout << " fillr:" << i->first.toString() << endl; - if( dir > 0 ) - _fill(b, filter, nout, best.begin(), best.end()); - else - _fill(b, filter, nout, best.rbegin(), best.rend()); - } - + + int size() const { return best.size(); } + + void add(BSONObj o) { + BSONObj k = order.getKeyFromObject(o); + if( (int) best.size() < limit ) { + approxSize += k.objsize(); + uassert( "too much key data for sort() with no index", approxSize < 1 * 1024 * 1024 ); + _add(k, o); + return; + } + BestMap::iterator i; + if( dir < 0 ) + i = best.begin(); + else { + assert( best.end() != best.begin() ); + i = best.end(); i--; + } + _addIfBetter(k, o, i); + } + + template + void _fill(BufBuilder& b, set *filter, int& nout, T begin, T end) { + int n = 0; + int nFilled = 0; + for( T i = begin; i != end; i++ ) { + n++; + if( n <= startFrom ) + continue; + BSONObj& o = i->second; + if( fillQueryResultFromObj(b, filter, o) ) { + nFilled++; + if( nFilled >= limit ) + goto done; + uassert( "too much data for sort() with no index", b.len() < 4000000 ); // appserver limit + } + } +done: + nout = nFilled; + } + + /* scanning complete. stick the query result in b for n objects. */ + void fill(BufBuilder& b, set *filter, int& nout) { + // for( BestMap::iterator i = best.begin(); i != best.end(); i++ ) + // cout << " fill:" << i->first.toString() << endl; + // for( BestMap::reverse_iterator i = best.rbegin(); i != best.rend(); i++ ) + // cout << " fillr:" << i->first.toString() << endl; + if( dir > 0 ) + _fill(b, filter, nout, best.begin(), best.end()); + else + _fill(b, filter, nout, best.rbegin(), best.rend()); + } + }; -- cgit v1.2.1