Skip to content

Commit

Permalink
beginnings of $explain
Browse files Browse the repository at this point in the history
  • Loading branch information
dwight committed Nov 14, 2008
1 parent 2d95d88 commit 8c4778e
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 43 deletions.
16 changes: 12 additions & 4 deletions db/db.vcproj
Expand Up @@ -233,10 +233,6 @@
RelativePath=".\matcher.cpp"
>
</File>
<File
RelativePath="..\grid\message.cpp"
>
</File>
<File
RelativePath=".\namespace.cpp"
>
Expand All @@ -249,6 +245,10 @@
RelativePath=".\query.cpp"
>
</File>
<File
RelativePath=".\queryoptimizer.cpp"
>
</File>
<File
RelativePath=".\repl.cpp"
>
Expand Down Expand Up @@ -284,6 +284,10 @@
RelativePath="..\util\background.cpp"
>
</File>
<File
RelativePath="..\grid\message.cpp"
>
</File>
<File
RelativePath="..\util\mmap.cpp"
>
Expand Down Expand Up @@ -395,6 +399,10 @@
RelativePath=".\query.h"
>
</File>
<File
RelativePath=".\queryoptimizer.h"
>
</File>
<File
RelativePath=".\repl.h"
>
Expand Down
3 changes: 3 additions & 0 deletions db/pdfile.h
Expand Up @@ -309,6 +309,8 @@ inline BtreeBucket* DiskLoc::btree() const {

// A Client is a psuedonym for a database.

#include "queryoptimizer.h"

class Client {
public:
Client(const char *nm, bool& justCreated) : name(nm) {
Expand Down Expand Up @@ -362,6 +364,7 @@ class Client {
NamespaceIndex namespaceIndex;
int profile; // 0=off.
string profileName; // "alleyinsider.system.profile"
QueryOptimizer optimizer;
};

// tempish...move to TLS or pass all the way down as a parm
Expand Down
103 changes: 65 additions & 38 deletions db/query.cpp
Expand Up @@ -537,7 +537,8 @@ int runCount(const char *ns, BSONObj& cmd, string& err) {
return count;
}

/* [ { a : 1 } , { b : 1 } ] -> { a : 1, b : 1 }
/* This is for languages whose "objects" are not well ordered (JSON is well ordered).
[ { a : ... } , { b : ... } ] -> { a : ..., b : ... }
*/
inline BSONObj transformOrderFromArrayFormat(BSONObj order) {
/* note: this is slow, but that is ok as order will have very few pieces */
Expand Down Expand Up @@ -586,7 +587,16 @@ QueryResult* runQuery(Message& message, const char *ns, int ntoskip, int _ntoret

uassert("not master", isMaster() || (queryOptions & Option_SlaveOk));

BSONObj query = jsobj.getObjectField("query");
bool explain = false;
bool _gotquery = false;
BSONObj query;// = jsobj.getObjectField("query");
{
BSONElement e = jsobj.findElement("query");
if( !e.eoo() && (e.type() == Object || e.type() == Array) ) {
query = e.embeddedObject();
_gotquery = true;
}
}
BSONObj order;
{
BSONElement e = jsobj.findElement("orderby");
Expand All @@ -596,8 +606,11 @@ QueryResult* runQuery(Message& message, const char *ns, int ntoskip, int _ntoret
order = transformOrderFromArrayFormat(order);
}
}
if( query.isEmpty() && order.isEmpty() )
if( !_gotquery && order.isEmpty() )
query = jsobj;
else {
explain = jsobj.getBoolField("$explain");
}

/* The ElemIter will not be happy if this isn't really an object. So throw exception
here when that is true.
Expand All @@ -607,7 +620,7 @@ QueryResult* runQuery(Message& message, const char *ns, int ntoskip, int _ntoret
cout << "Bad query object?\n jsobj:";
cout << jsobj.toString() << "\n query:";
cout << query.toString() << endl;
assert(false);
uassert("bad query object", false);
}

auto_ptr<JSMatcher> matcher(new JSMatcher(query));
Expand Down Expand Up @@ -635,8 +648,8 @@ QueryResult* runQuery(Message& message, const char *ns, int ntoskip, int _ntoret

while( c->ok() ) {
BSONObj js = c->current();
if( queryTraceLevel >= 50 )
cout << " checking against:\n " << js.toString() << endl;
//if( queryTraceLevel >= 50 )
// cout << " checking against:\n " << js.toString() << endl;
nscanned++;
bool deep;

Expand All @@ -654,47 +667,61 @@ QueryResult* runQuery(Message& message, const char *ns, int ntoskip, int _ntoret
else if( ntoskip > 0 ) {
ntoskip--;
} else {
bool ok = fillQueryResultFromObj(b, filter.get(), js);
if( ok ) n++;
if( ok ) {
if( (ntoreturn>0 && (n >= ntoreturn || b.len() > MaxBytesToReturnToClientAtOnce)) ||
(ntoreturn==0 && (b.len()>1*1024*1024 || n>=101)) ) {
/* if ntoreturn is zero, we return up to 101 objects. on the subsequent getmore, there
is only a size limit. The idea is that on a find() where one doesn't use much results,
we don't return much, but once getmore kicks in, we start pushing significant quantities.
The n limit (vs. size) is important when someone fetches only one small field from big
objects, which causes massive scanning server-side.
*/
/* if only 1 requested, no cursor saved for efficiency...we assume it is findOne() */
if( wantMore && ntoreturn != 1 ) {
if( useCursors ) {
c->advance();
if( c->ok() ) {
// more...so save a cursor
ClientCursor *cc = new ClientCursor();
cc->c = c;
cursorid = cc->cursorid;
DEV cout << " query has more, cursorid: " << cursorid << endl;
cc->matcher = matcher;
cc->ns = ns;
cc->pos = n;
cc->filter = filter;
cc->originalMessage = message;
cc->updateLocation();
if( explain ) {
n++;
}
else {
bool ok = fillQueryResultFromObj(b, filter.get(), js);
if( ok ) n++;
if( ok ) {
if( (ntoreturn>0 && (n >= ntoreturn || b.len() > MaxBytesToReturnToClientAtOnce)) ||
(ntoreturn==0 && (b.len()>1*1024*1024 || n>=101)) ) {
/* if ntoreturn is zero, we return up to 101 objects. on the subsequent getmore, there
is only a size limit. The idea is that on a find() where one doesn't use much results,
we don't return much, but once getmore kicks in, we start pushing significant quantities.
The n limit (vs. size) is important when someone fetches only one small field from big
objects, which causes massive scanning server-side.
*/
/* if only 1 requested, no cursor saved for efficiency...we assume it is findOne() */
if( wantMore && ntoreturn != 1 ) {
if( useCursors ) {
c->advance();
if( c->ok() ) {
// more...so save a cursor
ClientCursor *cc = new ClientCursor();
cc->c = c;
cursorid = cc->cursorid;
DEV cout << " query has more, cursorid: " << cursorid << endl;
cc->matcher = matcher;
cc->ns = ns;
cc->pos = n;
cc->filter = filter;
cc->originalMessage = message;
cc->updateLocation();
}
}
}
}
break;
break;
}
}
}
}
}
c->advance();
} // end while

if( ordering ) {
so->fill(b, filter.get(), n);
if( explain ) {
BSONObjBuilder builder;
builder.append("cursor", c->toString());
builder.append("nscanned", nscanned);
builder.append("n", n);
if( ordering )
builder.append("scanAndOrder", true);
BSONObj obj = builder.done();
fillQueryResultFromObj(b, 0, obj);
} else if( ordering ) {
so->fill(b, filter.get(), n);
}
else if( cursorid == 0 && (queryOptions & Option_CursorTailable) && c->tailable() ) {
c->setAtTail();
Expand Down
44 changes: 44 additions & 0 deletions db/queryoptimizer.cpp
@@ -0,0 +1,44 @@
/* queryoptimizer.cpp */

/**
* 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 <http://www.gnu.org/licenses/>.
*/

#include "stdafx.h"
#include "query.h"
#include "pdfile.h"
#include "jsobj.h"
#include "../util/builder.h"
#include <time.h>
#include "btree.h"
#include "../util/lruishmap.h"
#include "json.h"
#include "repl.h"
#include "replset.h"
#include "scanandorder.h"
#include "queryoptimizer.h"

QueryPlan QueryOptimizer::getPlan(
const char *ns,
BSONObj* query,
BSONObj* order,
BSONObj* hint)
{
QueryPlan plan;



return plan;
}
49 changes: 49 additions & 0 deletions db/queryoptimizer.h
@@ -0,0 +1,49 @@
/* queryoptimizer.h */

/**
* 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 <http://www.gnu.org/licenses/>.
*/

#pragma once

class QueryPlan {
public:
QueryPlan() {
scanAndOrderRequired = false;
simpleKeyMatch = false;
}

auto_ptr<Cursor> cursor;

/* ScanAndOrder processing will be required if true */
bool scanAndOrderRequired;

/* When true, the index we are using has keys such that it can completely resolve the
query expression to match by itself without ever checking the main object.
*/
bool simpleKeyMatch;
};

/* We put these objects inside the Client objects: that way later if we want to do
stats, it's in the right place.
*/
class QueryOptimizer {
public:
QueryPlan getPlan(
const char *ns,
BSONObj* query,
BSONObj* order = 0,
BSONObj* hint = 0);
};
20 changes: 19 additions & 1 deletion db/scanandorder.h
@@ -1,4 +1,22 @@
// scanandorder.h
/* scanandorder.h
Order results (that aren't already indexes and in order.)
*/

/**
* 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 <http://www.gnu.org/licenses/>.
*/

#pragma once

Expand Down

0 comments on commit 8c4778e

Please sign in to comment.