summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDwight <dmerriman@gmail.com>2008-11-14 16:19:47 -0500
committerDwight <dmerriman@gmail.com>2008-11-14 16:19:47 -0500
commit8c4778e59fb1911ee5fc2df7c88d71c28b3fb259 (patch)
tree034c1a447427260c5ae79b22f735d3a416515bb5
parent2d95d8889989594d66045d2bc76d6247a774962f (diff)
downloadmongo-8c4778e59fb1911ee5fc2df7c88d71c28b3fb259.tar.gz
beginnings of $explain
-rw-r--r--db/db.vcproj16
-rw-r--r--db/pdfile.h3
-rw-r--r--db/query.cpp103
-rw-r--r--db/queryoptimizer.cpp44
-rw-r--r--db/queryoptimizer.h49
-rw-r--r--db/scanandorder.h20
6 files changed, 192 insertions, 43 deletions
diff --git a/db/db.vcproj b/db/db.vcproj
index 54beb565bfc..db9c0254493 100644
--- a/db/db.vcproj
+++ b/db/db.vcproj
@@ -234,10 +234,6 @@
>
</File>
<File
- RelativePath="..\grid\message.cpp"
- >
- </File>
- <File
RelativePath=".\namespace.cpp"
>
</File>
@@ -250,6 +246,10 @@
>
</File>
<File
+ RelativePath=".\queryoptimizer.cpp"
+ >
+ </File>
+ <File
RelativePath=".\repl.cpp"
>
</File>
@@ -285,6 +285,10 @@
>
</File>
<File
+ RelativePath="..\grid\message.cpp"
+ >
+ </File>
+ <File
RelativePath="..\util\mmap.cpp"
>
</File>
@@ -396,6 +400,10 @@
>
</File>
<File
+ RelativePath=".\queryoptimizer.h"
+ >
+ </File>
+ <File
RelativePath=".\repl.h"
>
</File>
diff --git a/db/pdfile.h b/db/pdfile.h
index 31d134f1ae5..7bd4e5a5bed 100644
--- a/db/pdfile.h
+++ b/db/pdfile.h
@@ -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) {
@@ -362,6 +364,7 @@ public:
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
diff --git a/db/query.cpp b/db/query.cpp
index 67a5fb98b6a..0d5d393330b 100644
--- a/db/query.cpp
+++ b/db/query.cpp
@@ -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 */
@@ -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");
@@ -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.
@@ -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));
@@ -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;
@@ -654,38 +667,43 @@ 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;
+ }
}
}
}
@@ -693,8 +711,17 @@ QueryResult* runQuery(Message& message, const char *ns, int ntoskip, int _ntoret
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();
diff --git a/db/queryoptimizer.cpp b/db/queryoptimizer.cpp
new file mode 100644
index 00000000000..c01d31c0007
--- /dev/null
+++ b/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;
+}
diff --git a/db/queryoptimizer.h b/db/queryoptimizer.h
new file mode 100644
index 00000000000..86963039d83
--- /dev/null
+++ b/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);
+};
diff --git a/db/scanandorder.h b/db/scanandorder.h
index 941e597314e..f046b6cc925 100644
--- a/db/scanandorder.h
+++ b/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