Files
mongo/db/query.cpp

746 lines
20 KiB
C++
Raw Normal View History

2007-10-19 19:35:48 -04:00
// query.cpp
#include "stdafx.h"
#include "query.h"
#include "pdfile.h"
#include "jsobj.h"
#include "../util/builder.h"
2007-11-04 16:17:44 -05:00
#include <time.h>
2007-11-04 20:47:12 -05:00
#include "introspect.h"
2007-11-10 16:46:30 -05:00
#include "btree.h"
2008-01-20 17:42:26 -05:00
#include "../util/lruishmap.h"
//ns->query->DiskLoc
LRUishMap<JSObj,DiskLoc,5> lrutest(123);
2007-10-19 19:35:48 -04:00
int nextCursorId = 1;
2007-12-06 19:03:23 -05:00
JSObj emptyObj;
2008-01-27 14:51:47 -05:00
int getGtLtOp(Element& e);
void appendElementHandlingGtLt(JSObjBuilder& b, Element& e);
2007-11-11 14:21:02 -05:00
/* todo: _ cache query plans
_ use index on partial match with the query
2008-01-27 14:51:47 -05:00
parameters
query - the query, e.g., { name: 'joe' }
order - order by spec, e.g., { name: 1 } 1=ASC, -1=DESC
2007-11-11 14:21:02 -05:00
*/
auto_ptr<Cursor> getIndexCursor(const char *ns, JSObj& query, JSObj& order) {
2008-01-20 17:42:26 -05:00
NamespaceDetails *d = nsdetails(ns);
2007-11-11 14:21:02 -05:00
if( d == 0 ) return auto_ptr<Cursor>();
2008-01-27 14:51:47 -05:00
// queryFields, e.g. { 'name' }
2007-11-11 14:21:02 -05:00
set<string> queryFields;
query.getFieldNames(queryFields);
2008-01-27 14:51:47 -05:00
2007-11-11 14:21:02 -05:00
if( !order.isEmpty() ) {
set<string> orderFields;
order.getFieldNames(orderFields);
// order by
for(int i = 0; i < d->nIndexes; i++ ) {
2008-01-27 14:51:47 -05:00
JSObj idxInfo = d->indexes[i].info.obj(); // { name:, ns:, key: }
2007-11-11 14:21:02 -05:00
assert( strcmp(ns, idxInfo.getStringField("ns")) == 0 );
JSObj idxKey = idxInfo.getObjectField("key");
set<string> keyFields;
idxKey.getFieldNames(keyFields);
if( keyFields == orderFields ) {
2007-11-12 19:22:33 -05:00
bool reverse =
order.firstElement().type() == Number &&
order.firstElement().number() < 0;
2007-11-11 14:21:02 -05:00
JSObjBuilder b;
2008-01-27 14:51:47 -05:00
/* todo: start with the right key, not just beginning of index, when query is also
specified!
*/
2007-12-06 19:03:23 -05:00
return auto_ptr<Cursor>(new BtreeCursor(d->indexes[i].head, reverse ? maxKey : emptyObj, reverse ? -1 : 1, false));
2007-11-11 14:21:02 -05:00
}
}
}
2008-01-27 14:51:47 -05:00
// regular query without order by
2007-11-11 14:21:02 -05:00
for(int i = 0; i < d->nIndexes; i++ ) {
2008-01-27 14:51:47 -05:00
JSObj idxInfo = d->indexes[i].info.obj(); // { name:, ns:, key: }
2007-11-11 14:21:02 -05:00
JSObj idxKey = idxInfo.getObjectField("key");
set<string> keyFields;
idxKey.getFieldNames(keyFields);
if( keyFields == queryFields ) {
JSObjBuilder b;
2007-11-25 18:20:54 -05:00
JSObj q = query.extractFields(idxKey, b);
/* regexp: only supported if form is /^text/ */
JSObjBuilder b2;
JSElemIter it(q);
2008-01-27 14:51:47 -05:00
bool first = true;
2007-11-25 18:20:54 -05:00
while( it.more() ) {
Element e = it.next();
if( e.eoo() )
break;
2008-01-27 14:51:47 -05:00
// GT/LT
if( e.type() == Object ) {
int op = getGtLtOp(e);
if( op ) {
if( !first || !it.next().eoo() ) {
// compound keys with GT/LT not supported yet via index.
goto fail;
}
int direction = - JSMatcher::opDirection(op);
return auto_ptr<Cursor>( new BtreeCursor(
d->indexes[i].head,
direction == 1 ? emptyObj : maxKey,
direction,
true) );
}
}
first = false;
2007-11-25 18:20:54 -05:00
if( e.type() == RegEx ) {
if( *e.regexFlags() )
goto fail;
const char *re = e.regex();
const char *p = re;
if( *p++ != '^' ) goto fail;
while( *p ) {
2007-12-01 14:07:05 -05:00
if( *p == ' ' || (*p>='0'&&*p<='9') || (*p>='@'&&*p<='Z') || (*p>='a'&&*p<='z') )
;
else
2007-11-25 18:20:54 -05:00
goto fail;
p++;
}
if( it.more() && !it.next().eoo() ) // we must be the last part of the key (for now until we are smarter)
goto fail;
// ok!
b2.append(e.fieldName(), re+1);
break;
}
2008-01-27 14:51:47 -05:00
else {
2007-11-25 18:20:54 -05:00
b2.append(e);
2008-01-27 14:51:47 -05:00
//appendElementHandlingGtLt(b2, e);
}
2007-11-25 18:20:54 -05:00
}
JSObj q2 = b2.done();
2007-11-11 14:21:02 -05:00
return auto_ptr<Cursor>(
2007-11-25 18:20:54 -05:00
new BtreeCursor(d->indexes[i].head, q2, 1, true));
2007-11-11 14:21:02 -05:00
}
}
2007-11-25 18:20:54 -05:00
fail:
2007-11-11 14:21:02 -05:00
return auto_ptr<Cursor>();
}
2007-10-30 05:50:14 -04:00
void deleteObjects(const char *ns, JSObj pattern, bool justOne) {
2008-02-24 20:16:36 -05:00
// cout << "TEMP delete ns:" << ns << " queryobjsize:" <<
// pattern.objsize() << endl;
2007-10-30 05:50:14 -04:00
2008-01-20 17:42:26 -05:00
if( strstr(ns, ".system.") ) {
2008-03-08 23:16:41 -05:00
if( strstr(ns, ".system.namespaces") ){
cout << "WARNING: delete on system namespace " << ns << endl;
}
else if( strstr(ns, ".system.indexes") ) {
2008-02-10 16:28:48 -05:00
cout << "WARNING: delete on system namespace " << ns << endl;
}
else {
cout << "ERROR: attempt to delete in system namespace " << ns << endl;
return;
}
2007-11-02 21:30:40 -04:00
}
2007-10-30 05:50:14 -04:00
JSMatcher matcher(pattern);
2007-11-11 14:21:02 -05:00
JSObj order;
auto_ptr<Cursor> c = getIndexCursor(ns, pattern, order);
if( c.get() == 0 )
c = theDataFileMgr.findAll(ns);
2008-02-12 10:12:07 -05:00
Cursor &tempDebug = *c;
int temp = 0;
int tempd = 0;
DiskLoc _tempDelLoc;
2007-11-01 22:34:44 -04:00
while( c->ok() ) {
2008-02-12 10:12:07 -05:00
temp++;
2007-11-01 22:34:44 -04:00
Record *r = c->_current();
DiskLoc rloc = c->currLoc();
c->advance(); // must advance before deleting as the next ptr will die
2007-10-30 05:50:14 -04:00
JSObj js(r);
2008-02-24 20:16:36 -05:00
//cout << "TEMP: " << js.toString() << endl;
2007-11-24 16:15:09 -05:00
bool deep;
if( !matcher.matches(js, &deep) ) {
2007-11-11 14:21:02 -05:00
if( c->tempStopOnMiss() )
break;
}
2007-11-24 16:15:09 -05:00
else {
assert( !deep || !c->dup(rloc) ); // can't be a dup, we deleted it!
// cout << " found match to delete" << endl;
2007-11-11 14:21:02 -05:00
if( !justOne )
c->noteLocation();
2008-02-12 10:12:07 -05:00
_tempDelLoc = rloc;
2007-10-30 05:50:14 -04:00
theDataFileMgr.deleteRecord(ns, r, rloc);
2008-02-12 10:12:07 -05:00
tempd = temp;
2007-10-30 05:50:14 -04:00
if( justOne )
return;
2007-11-11 14:21:02 -05:00
c->checkLocation();
2007-10-30 05:50:14 -04:00
}
}
}
2007-12-24 20:59:58 -05:00
struct Mod {
const char *fieldName;
double n;
};
void applyMods(vector<Mod>& mods, JSObj obj) {
for( vector<Mod>::iterator i = mods.begin(); i != mods.end(); i++ ) {
Mod& m = *i;
Element e = obj.findElement(m.fieldName);
if( e.type() == Number ) {
e.number() += m.n;
}
}
}
/* get special operations like $inc
{ $inc: { a:1, b:1 } }
*/
void getMods(vector<Mod>& mods, JSObj from) {
JSElemIter it(from);
while( it.more() ) {
Element e = it.next();
if( strcmp(e.fieldName(), "$inc") == 0 && e.type() == Object ) {
JSObj j = e.embeddedObject();
JSElemIter jt(j);
while( jt.more() ) {
Element f = jt.next();
if( f.eoo() )
break;
Mod m;
m.fieldName = f.fieldName();
if( f.type() == Number ) {
m.n = f.number();
mods.push_back(m);
}
}
}
}
}
2008-01-20 17:42:26 -05:00
/*
2007-12-24 20:59:58 -05:00
todo:
smart requery find record immediately
*/
2007-10-30 05:50:14 -04:00
void updateObjects(const char *ns, JSObj updateobj, JSObj pattern, bool upsert) {
2008-01-20 17:42:26 -05:00
//cout << "TEMP BAD";
//lrutest.find(updateobj);
2007-12-24 20:59:58 -05:00
// cout << "update ns:" << ns << " objsize:" << updateobj.objsize() << " queryobjsize:" <<
// pattern.objsize();
2007-10-30 05:50:14 -04:00
2008-01-20 17:42:26 -05:00
if( strstr(ns, ".system.") ) {
2007-11-13 16:44:01 -05:00
cout << "\nERROR: attempt to update in system namespace " << ns << endl;
2007-11-02 21:30:40 -04:00
return;
}
2007-11-11 18:28:33 -05:00
{
JSMatcher matcher(pattern);
JSObj order;
auto_ptr<Cursor> c = getIndexCursor(ns, pattern, order);
if( c.get() == 0 )
c = theDataFileMgr.findAll(ns);
while( c->ok() ) {
Record *r = c->_current();
JSObj js(r);
if( !matcher.matches(js) ) {
if( c->tempStopOnMiss() )
break;
}
else {
2007-11-24 16:15:09 -05:00
/* note: we only update one row and quit. if you do multiple later,
be careful or multikeys in arrays could break things badly. best
to only allow updating a single row with a multikey lookup.
*/
2007-12-24 20:59:58 -05:00
/* look for $inc etc. note as listed here, all fields to inc must be this type, you can't set some
regular ones at the moment. */
if( updateobj.firstElement().fieldName()[0] == '$' ) {
vector<Mod> mods;
getMods(mods, updateobj);
applyMods(mods, c->currLoc().obj());
return;
}
2007-11-11 18:28:33 -05:00
theDataFileMgr.update(ns, r, c->currLoc(), updateobj.objdata(), updateobj.objsize());
return;
}
c->advance();
2007-10-30 05:50:14 -04:00
}
}
2007-12-24 20:59:58 -05:00
if( upsert ) {
if( updateobj.firstElement().fieldName()[0] == '$' ) {
/* upsert of an $inc. build a default */
vector<Mod> mods;
getMods(mods, updateobj);
JSObjBuilder b;
b.appendElements(pattern);
for( vector<Mod>::iterator i = mods.begin(); i != mods.end(); i++ )
b.append(i->fieldName, i->n);
JSObj obj = b.done();
theDataFileMgr.insert(ns, (void*) obj.objdata(), obj.objsize());
return;
}
2007-10-30 05:50:14 -04:00
theDataFileMgr.insert(ns, (void*) updateobj.objdata(), updateobj.objsize());
2007-12-24 20:59:58 -05:00
}
2007-10-30 05:50:14 -04:00
}
2007-12-02 11:33:59 -05:00
int queryTraceLevel = 0;
int otherTraceLevel = 0;
2008-02-11 11:23:02 -05:00
int initialExtentSize(int len);
void clean(const char *ns, NamespaceDetails *d) {
for( int i = 0; i < Buckets; i++ )
d->deletedList[i].Null();
}
2008-02-10 16:28:48 -05:00
string validateNS(const char *ns, NamespaceDetails *d) {
stringstream ss;
2008-03-09 09:40:34 -04:00
ss << "\nvalidate ";
if( d->capped )
cout << " capped:" << d->capped << " max:" << d->max;
ss << "\n";
2008-02-11 11:23:02 -05:00
try {
auto_ptr<Cursor> c = theDataFileMgr.findAll(ns);
int n = 0;
long long len = 0;
long long nlen = 0;
while( c->ok() ) {
n++;
Record *r = c->_current();
len += r->lengthWithHeaders;
nlen += r->netLength();
c->advance();
}
2008-03-09 09:40:34 -04:00
ss << " " << n << " objects found, nobj:" << d->nrecords << "\n";
2008-02-11 11:23:02 -05:00
ss << " " << len << " bytes record data w/headers\n";
ss << " " << nlen << " bytes record data wout/headers\n";
ss << " deletedList: ";
for( int i = 0; i < Buckets; i++ ) {
ss << (d->deletedList[i].isNull() ? '0' : '1');
}
ss << endl;
int ndel = 0;
long long delSize = 0;
for( int i = 0; i < Buckets; i++ ) {
DiskLoc loc = d->deletedList[i];
while( !loc.isNull() ) {
ndel++;
DeletedRecord *d = loc.drec();
delSize += d->lengthWithHeaders;
loc = d->nextDeleted;
}
}
ss << " deleted: n: " << ndel << " size: " << delSize << endl;
2008-03-13 15:39:09 -04:00
int idxn = 0;
try {
ss << " nIndexes:" << d->nIndexes << endl;
for( ; idxn < d->nIndexes; idxn++ ) {
ss << " " << d->indexes[idxn].indexNamespace() << " keys:" <<
d->indexes[idxn].head.btree()->fullValidate(d->indexes[idxn].head) << endl;
}
}
catch(...) {
ss << "\n exception during index validate idxn:" << idxn << endl;
}
2008-02-11 11:23:02 -05:00
}
catch(AssertionException) {
ss << "\n exception during validate\n" << endl;
2008-02-10 16:28:48 -05:00
}
2008-02-11 11:23:02 -05:00
2008-02-10 16:28:48 -05:00
return ss.str();
}
2008-03-08 23:16:41 -05:00
bool userCreateNS(const char *ns, JSObj& j);
// e.g.
// system.cmd$.find( { queryTraceLevel: 2 } );
2007-12-24 20:59:58 -05:00
//
// returns true if ran a cmd
//
inline bool runCommands(const char *ns, JSObj& jsobj, stringstream& ss, BufBuilder &b, JSObjBuilder& anObjBuilderForYa) {
2008-02-10 16:28:48 -05:00
const char *p = strchr(ns, '.');
if( !p ) return false;
if( strcmp(p, ".$cmd") != 0 ) return false;
2007-12-02 11:33:59 -05:00
ss << "\n $cmd: " << jsobj.toString();
2007-12-24 20:59:58 -05:00
bool ok = false;
bool valid = false;
2008-03-08 23:16:41 -05:00
cout << jsobj.toString() << endl;
Element e;
// e = jsobj.findElement("create");
//if( e.eoo() )
// e = jsobj.firstElement();
e = jsobj.firstElement();
2007-12-24 20:59:58 -05:00
if( e.eoo() ) goto done;
2007-12-02 11:33:59 -05:00
if( e.type() == Number ) {
2008-03-08 23:16:41 -05:00
if( strcmp(e.fieldName(), "profile") == 0 ) {
anObjBuilderForYa.append("was", (double) client->profile);
client->profile = (int) e.number();
2007-12-24 20:59:58 -05:00
valid = ok = true;
2008-03-08 23:16:41 -05:00
}
else {
if( strncmp(ns, "admin", p-ns) != 0 ) // admin only
return false;
if( strcmp(e.fieldName(),"queryTraceLevel") == 0 ) {
valid = ok = true;
queryTraceLevel = (int) e.number();
} else if( strcmp(e.fieldName(),"traceAll") == 0 ) {
valid = ok = true;
queryTraceLevel = (int) e.number();
otherTraceLevel = (int) e.number();
}
2007-12-02 11:33:59 -05:00
}
}
2008-02-10 16:28:48 -05:00
else if( e.type() == String ) {
string us(ns, p-ns);
2008-02-11 11:23:02 -05:00
2008-03-08 23:16:41 -05:00
if( strcmp( e.fieldName(), "create") == 0 ) {
valid = true;
string ns = us + '.' + e.valuestr();
ok = userCreateNS(ns.c_str(), jsobj);
}
else if( strcmp( e.fieldName(), "clean") == 0 ) {
2008-02-11 11:23:02 -05:00
valid = true;
string dropNs = us + '.' + e.valuestr();
NamespaceDetails *d = nsdetails(dropNs.c_str());
cout << "CMD: clean " << dropNs << endl;
if( d ) {
ok = true;
anObjBuilderForYa.append("ns", dropNs.c_str());
clean(dropNs.c_str(), d);
}
else {
anObjBuilderForYa.append("errmsg", "ns not found");
}
}
2008-02-11 11:37:11 -05:00
else if( strcmp( e.fieldName(), "drop") == 0 ) {
valid = true;
string dropNs = us + '.' + e.valuestr();
NamespaceDetails *d = nsdetails(dropNs.c_str());
cout << "CMD: clean " << dropNs << endl;
if( d ) {
ok = true;
anObjBuilderForYa.append("ns", dropNs.c_str());
client->namespaceIndex->kill(dropNs.c_str());
}
else {
anObjBuilderForYa.append("errmsg", "ns not found");
}
}
2008-02-11 11:23:02 -05:00
else if( strcmp( e.fieldName(), "validate") == 0 ) {
2008-02-10 16:28:48 -05:00
valid = true;
string toValidateNs = us + '.' + e.valuestr();
NamespaceDetails *d = nsdetails(toValidateNs.c_str());
cout << "CMD: validate " << toValidateNs << endl;
if( d ) {
ok = true;
anObjBuilderForYa.append("ns", toValidateNs.c_str());
string s = validateNS(toValidateNs.c_str(), d);
anObjBuilderForYa.append("result", s.c_str());
}
else {
anObjBuilderForYa.append("errmsg", "ns not found");
}
}
2008-02-11 11:23:02 -05:00
else if( strcmp(e.fieldName(),"deleteIndexes") == 0 ) {
2007-12-24 20:59:58 -05:00
valid = true;
/* note: temp implementation. space not reclaimed! */
2008-02-10 16:28:48 -05:00
string toDeleteNs = us + '.' + e.valuestr();
NamespaceDetails *d = nsdetails(toDeleteNs.c_str());
cout << "CMD: deleteIndexes " << toDeleteNs << endl;
if( d ) {
2008-03-06 15:03:12 -05:00
Element f = jsobj.findElement("index");
if( !f.eoo() ) {
// delete a specific index
if( f.type() == String ) {
const char *idxName = f.valuestr();
2008-03-06 18:06:06 -05:00
if( *idxName == '*' && idxName[1] == 0 ) {
ok = true;
2008-03-06 15:03:12 -05:00
cout << " d->nIndexes was " << d->nIndexes << endl;
anObjBuilderForYa.append("nIndexesWas", (double)d->nIndexes);
2008-03-06 18:06:06 -05:00
anObjBuilderForYa.append("msg", "all indexes deleted for collection");
2008-03-06 15:03:12 -05:00
cout << " alpha implementation, space not reclaimed" << endl;
2008-03-06 18:06:06 -05:00
d->nIndexes = 0;
}
else {
// delete just one index
int x = d->findIndexByName(idxName);
if( x >= 0 ) {
cout << " d->nIndexes was " << d->nIndexes << endl;
anObjBuilderForYa.append("nIndexesWas", (double)d->nIndexes);
d->nIndexes--;
for( int i = x; i < d->nIndexes; i++ )
d->indexes[i] = d->indexes[i+1];
ok=true;
cout << " alpha implementation, space not reclaimed" << endl;
} else {
cout << "deleteIndexes: " << idxName << " not found" << endl;
}
2008-03-06 15:03:12 -05:00
}
}
}
}
2007-12-24 20:59:58 -05:00
else {
anObjBuilderForYa.append("errmsg", "ns not found");
}
}
}
2007-12-24 20:59:58 -05:00
done:
if( !valid )
anObjBuilderForYa.append("errmsg", "no such cmd");
anObjBuilderForYa.append("ok", ok?1.0:0.0);
JSObj x = anObjBuilderForYa.done();
b.append((void*) x.objdata(), x.objsize());
return true;
2007-12-02 11:33:59 -05:00
}
2008-02-10 16:28:48 -05:00
int nCaught = 0;
2008-03-05 16:51:27 -05:00
void killCursors(int n, long long *ids) {
int k = 0;
for( int i = 0; i < n; i++ ) {
if( ClientCursor::erase(ids[i]) )
k++;
}
cout << "killCursors: found " << k << " of " << n << endl;
}
2008-03-09 11:05:25 -04:00
auto_ptr<Cursor> findTableScan(const char *ns, JSObj& order);
2008-02-28 13:38:56 -05:00
QueryResult* runQuery(const char *ns, int ntoskip, int _ntoreturn, JSObj jsobj,
auto_ptr< set<string> > filter, stringstream& ss)
{
bool wantMore = true;
int ntoreturn = _ntoreturn;
if( _ntoreturn < 0 ) {
ntoreturn = -_ntoreturn;
wantMore = false;
}
ss << "query:" << ns << " _ntoreturn:" << _ntoreturn;
2008-01-31 19:10:52 -05:00
if( ntoskip ) ss << " ntoskip:" << ntoskip;
2007-12-01 11:44:42 -05:00
if( jsobj.objsize() > 100 )
ss << " querysz:" << jsobj.objsize();
2007-12-02 11:33:59 -05:00
if( queryTraceLevel >= 1 )
cout << "query: " << jsobj.toString() << endl;
2007-10-19 19:35:48 -04:00
2007-12-24 20:59:58 -05:00
int n = 0;
2007-11-21 21:44:57 -05:00
BufBuilder b(32768);
2007-12-24 20:59:58 -05:00
JSObjBuilder cmdResBuf;
long long cursorid = 0;
2007-11-04 16:17:44 -05:00
2007-10-28 16:38:06 -04:00
b.skip(sizeof(QueryResult));
2007-10-19 19:35:48 -04:00
2008-02-10 16:28:48 -05:00
/* we assume you are using findOne() for running a cmd... */
if( ntoreturn == 1 && runCommands(ns, jsobj, ss, b, cmdResBuf) ) {
2007-12-24 20:59:58 -05:00
n = 1;
2007-12-02 11:33:59 -05:00
}
2007-12-24 20:59:58 -05:00
else {
2007-11-01 22:34:44 -04:00
2007-12-24 20:59:58 -05:00
JSObj query = jsobj.getObjectField("query");
JSObj order = jsobj.getObjectField("orderby");
if( query.isEmpty() && order.isEmpty() )
query = jsobj;
2007-12-16 20:45:25 -05:00
2007-12-24 20:59:58 -05:00
auto_ptr<JSMatcher> matcher(new JSMatcher(query));
JSMatcher &debug1 = *matcher;
assert( debug1.getN() < 5000 );
2007-12-24 20:59:58 -05:00
2008-02-10 16:28:48 -05:00
int nscanned = 0;
2007-12-24 20:59:58 -05:00
auto_ptr<Cursor> c = getSpecialCursor(ns);
/*try*/{
2008-02-10 16:28:48 -05:00
if( c.get() == 0 ) {
c = getIndexCursor(ns, query, order);
2007-12-24 20:59:58 -05:00
}
2008-02-10 16:28:48 -05:00
if( c.get() == 0 ) {
2008-03-09 11:05:25 -04:00
//c = theDataFileMgr.findAll(ns);
c = findTableScan(ns, order);
2008-02-10 16:28:48 -05:00
}
while( c->ok() ) {
JSObj js = c->current();
if( queryTraceLevel >= 50 )
cout << " checking against:\n " << js.toString() << endl;
nscanned++;
bool deep;
JSMatcher &debug = *matcher;
assert( debug.getN() < 5000 );
2008-02-10 16:28:48 -05:00
if( !matcher->matches(js, &deep) ) {
if( c->tempStopOnMiss() )
break;
2007-12-24 20:59:58 -05:00
}
2008-02-10 16:28:48 -05:00
else if( !deep || !c->dup(c->currLoc()) ) { // i.e., check for dups on deep items only
// got a match.
if( ntoskip > 0 ) {
ntoskip--;
2008-01-31 19:10:52 -05:00
}
else {
2008-02-10 16:28:48 -05:00
bool ok = true;
if( filter.get() ) {
// we just want certain fields from the object.
JSObj x;
ok = x.addFields(js, *filter) > 0;
if( ok )
b.append((void*) x.objdata(), x.objsize());
}
else {
b.append((void*) js.objdata(), js.objsize());
}
if( ok ) {
n++;
if( (ntoreturn>0 && (n >= ntoreturn || b.len() > 16*1024*1024)) ||
(ntoreturn==0 && b.len()>1*1024*1024) ) {
2008-02-26 16:50:29 -05:00
/* if only 1 requested, no cursor saved for efficiency...we assume it is findOne() */
2008-02-28 13:38:56 -05:00
if( wantMore && ntoreturn != 1 ) {
2008-02-29 11:11:54 -05:00
c->advance();
if( c->ok() ) {
// more...so save a cursor
ClientCursor *cc = new ClientCursor();
cc->c = c;
cursorid = allocCursorId();
cc->cursorid = cursorid;
cc->matcher = matcher;
cc->ns = ns;
cc->pos = n;
ClientCursor::add(cc);
cc->updateLocation();
cc->filter = filter;
}
2008-02-26 16:50:29 -05:00
}
2008-02-10 16:28:48 -05:00
break;
}
2008-01-31 19:10:52 -05:00
}
2007-12-24 20:59:58 -05:00
}
2007-11-04 22:34:37 -05:00
}
2008-02-10 16:28:48 -05:00
c->advance();
2007-11-04 16:17:44 -05:00
}
2007-10-19 19:35:48 -04:00
2008-02-10 16:28:48 -05:00
if( queryTraceLevel >=2 )
cout << " nscanned:" << nscanned << "\n ";
}
/*catch( AssertionException e ) {
2008-02-10 16:28:48 -05:00
if( n )
throw e;
if( nCaught++ >= 1000 ) {
2008-02-10 16:28:48 -05:00
cout << "Too many query exceptions, terminating" << endl;
exit(-8);
}
cout << " Assertion running query, returning an empty result" << endl;
}*/
2007-12-24 20:59:58 -05:00
}
2007-12-02 11:33:59 -05:00
2007-12-24 20:59:58 -05:00
QueryResult *qr = (QueryResult *) b.buf();
qr->_data[0] = 0;
qr->_data[1] = 0;
qr->_data[2] = 0;
qr->_data[3] = 0;
2007-10-19 19:35:48 -04:00
qr->len = b.len();
2007-12-01 11:44:42 -05:00
ss << " resLen:" << b.len();
2007-11-17 21:10:00 -05:00
// qr->channel = 0;
2007-10-19 19:35:48 -04:00
qr->operation = opReply;
2007-11-04 16:17:44 -05:00
qr->cursorId = cursorid;
2007-10-28 14:42:59 -04:00
qr->startingFrom = 0;
2007-10-19 19:35:48 -04:00
qr->nReturned = n;
b.decouple();
2007-12-01 11:44:42 -05:00
ss << " nReturned:" << n;
2007-10-19 19:35:48 -04:00
return qr;
}
2007-11-04 16:17:44 -05:00
QueryResult* getMore(const char *ns, int ntoreturn, long long cursorid) {
2007-12-01 11:44:42 -05:00
// cout << "getMore ns:" << ns << " ntoreturn:" << ntoreturn << " cursorid:" <<
// cursorid << endl;
2007-11-04 16:17:44 -05:00
2007-11-21 21:44:57 -05:00
BufBuilder b(32768);
2007-11-04 16:17:44 -05:00
2008-02-26 16:50:29 -05:00
ClientCursor *cc = ClientCursor::find(cursorid);
2007-11-04 16:17:44 -05:00
b.skip(sizeof(QueryResult));
int start = 0;
int n = 0;
if( cc ) {
start = cc->pos;
Cursor *c = cc->c.get();
while( 1 ) {
if( !c->ok() ) {
2007-11-10 16:46:30 -05:00
done:
2007-11-04 16:17:44 -05:00
// done! kill cursor.
2008-02-26 16:50:29 -05:00
bool ok = ClientCursor::erase(cursorid);
assert(ok);
2007-11-04 16:17:44 -05:00
cursorid = 0;
cc = 0;
break;
}
JSObj js = c->current();
2007-11-24 16:15:09 -05:00
bool deep;
if( !cc->matcher->matches(js, &deep) ) {
2007-11-10 16:46:30 -05:00
if( c->tempStopOnMiss() )
goto done;
}
2007-11-24 16:15:09 -05:00
else if( !deep || !c->dup(c->currLoc()) ) {
2007-11-04 22:34:37 -05:00
bool ok = true;
if( cc->filter.get() ) {
JSObj x;
ok = x.addFields(js, *cc->filter) > 0;
if( ok )
b.append((void*) x.objdata(), x.objsize());
}
else {
b.append((void*) js.objdata(), js.objsize());
}
if( ok ) {
n++;
2007-11-05 11:55:03 -05:00
if( (ntoreturn>0 && (n >= ntoreturn || b.len() > 16*1024*1024)) ||
(ntoreturn==0 && b.len()>1*1024*1024) ) {
2008-02-29 11:11:54 -05:00
c->advance();
2007-11-04 22:34:37 -05:00
cc->pos += n;
cc->updateLocation();
break;
}
2007-11-04 16:17:44 -05:00
}
}
2007-11-26 16:43:28 -05:00
c->advance();
2007-11-04 16:17:44 -05:00
}
}
QueryResult *qr = (QueryResult *) b.buf();
qr->cursorId = cursorid;
qr->startingFrom = start;
qr->len = b.len();
2007-11-17 21:10:00 -05:00
// qr->reserved = 0;
2007-11-04 16:17:44 -05:00
qr->operation = opReply;
qr->nReturned = n;
b.decouple();
return qr;
2008-03-13 15:39:09 -04:00
}