diff options
author | Dwight <dmerriman@gmail.com> | 2008-11-14 16:19:47 -0500 |
---|---|---|
committer | Dwight <dmerriman@gmail.com> | 2008-11-14 16:19:47 -0500 |
commit | 8c4778e59fb1911ee5fc2df7c88d71c28b3fb259 (patch) | |
tree | 034c1a447427260c5ae79b22f735d3a416515bb5 | |
parent | 2d95d8889989594d66045d2bc76d6247a774962f (diff) | |
download | mongo-8c4778e59fb1911ee5fc2df7c88d71c28b3fb259.tar.gz |
beginnings of $explain
-rw-r--r-- | db/db.vcproj | 16 | ||||
-rw-r--r-- | db/pdfile.h | 3 | ||||
-rw-r--r-- | db/query.cpp | 103 | ||||
-rw-r--r-- | db/queryoptimizer.cpp | 44 | ||||
-rw-r--r-- | db/queryoptimizer.h | 49 | ||||
-rw-r--r-- | db/scanandorder.h | 20 |
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 |