diff options
27 files changed, 393 insertions, 312 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript index c6e4f35e8ee..fac782eadc4 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -238,6 +238,7 @@ env.StaticLibrary("coredb", [ "db/pipeline/pipeline.cpp", "db/dbcommands_generic.cpp", "db/dbwebserver.cpp", + "db/index_names.cpp", "db/keypattern.cpp", "db/matcher.cpp", "db/pipeline/accumulator.cpp", @@ -287,6 +288,7 @@ env.StaticLibrary("coredb", [ coreServerFiles = [ "db/client_basic.cpp", "util/net/miniwebserver.cpp", "db/indexkey.cpp", + "db/index_selection.cpp", "db/stats/counters.cpp", "db/stats/service_stats.cpp", ] diff --git a/src/mongo/db/fts/fts_index.cpp b/src/mongo/db/fts/fts_index.cpp index 2e667d0d1f7..0f6c3995fce 100644 --- a/src/mongo/db/fts/fts_index.cpp +++ b/src/mongo/db/fts/fts_index.cpp @@ -48,14 +48,6 @@ namespace mongo { FTSIndexFormat::getKeys( _ftsSpec, obj, &keys ); } - shared_ptr<Cursor> FTSIndex::newCursor( const BSONObj& query, - const BSONObj& order, - int numWanted ) const { - shared_ptr<Cursor> c; - verify(0); - return c; - } - FTSIndexPlugin::FTSIndexPlugin() : IndexPlugin( INDEX_NAME ) {} diff --git a/src/mongo/db/fts/fts_index.h b/src/mongo/db/fts/fts_index.h index 2eff9207ab1..a6a1378672f 100644 --- a/src/mongo/db/fts/fts_index.h +++ b/src/mongo/db/fts/fts_index.h @@ -40,11 +40,6 @@ namespace mongo { void getKeys( const BSONObj& obj, BSONObjSet& keys) const; - /* newCursor is pure Virtual in IndexType so it has to be redefined in FTSIndex */ - shared_ptr<Cursor> newCursor( const BSONObj& query, - const BSONObj& order, - int numWanted ) const; - const FTSSpec& getFtsSpec() const { return _ftsSpec; } private: diff --git a/src/mongo/db/geo/2d.cpp b/src/mongo/db/geo/2d.cpp index 487df2c846b..21ecfac92ae 100644 --- a/src/mongo/db/geo/2d.cpp +++ b/src/mongo/db/geo/2d.cpp @@ -214,55 +214,6 @@ namespace mongo { const IndexDetails* getDetails() const { return _spec->getDetails(); } - virtual shared_ptr<Cursor> newCursor(const BSONObj& query, const BSONObj& order, - int numWanted) const { - shared_ptr<Cursor> c; - verify(0); - return c; - } - - virtual IndexSuitability suitability( const FieldRangeSet& queryConstraints , - const BSONObj& order ) const { - BSONObj query = queryConstraints.originalQuery(); - - BSONElement e = query.getFieldDotted(_geo.c_str()); - switch (e.type()) { - case Object: { - BSONObj sub = e.embeddedObject(); - switch (sub.firstElement().getGtLtOp()) { - case BSONObj::opNEAR: - return OPTIMAL; - case BSONObj::opWITHIN: { - // Don't return optimal if it's $within: {$geometry: ... } - // because we will error out in that case, but the matcher - // or 2dsphere index may handle it. - BSONElement elt = sub.firstElement(); - if (Object == elt.type()) { - BSONObjIterator it(elt.embeddedObject()); - while (it.more()) { - BSONElement elt = it.next(); - if (mongoutils::str::equals("$geometry", elt.fieldName())) { - return USELESS; - } - } - } - return OPTIMAL; - } - default: - // We can try to match if there's no other indexing defined, - // this is assumed a point - return HELPFUL; - } - } - case Array: - // We can try to match if there's no other indexing defined, - // this is assumed a point - return HELPFUL; - default: - return USELESS; - } - } - const GeoHashConverter& getConverter() const { return *_geoHashConverter; } // XXX: make private with a getter diff --git a/src/mongo/db/geo/geonear.cpp b/src/mongo/db/geo/geonear.cpp index 83d2600c753..c262912c4c8 100644 --- a/src/mongo/db/geo/geonear.cpp +++ b/src/mongo/db/geo/geonear.cpp @@ -23,6 +23,7 @@ #include "mongo/db/auth/privilege.h" #include "mongo/db/commands.h" #include "mongo/db/curop.h" +#include "mongo/db/index_names.h" #include "mongo/db/index/2d_index_cursor.h" #include "mongo/db/index/catalog_hack.h" #include "mongo/db/index/index_descriptor.h" @@ -106,7 +107,7 @@ namespace mongo { vector<int> idxs; - d->findIndexByType("2d", idxs); + d->findIndexByType(IndexNames::GEO_2D, idxs); if (idxs.size() > 1) { errmsg = "more than one 2d index, not sure which to run geoNear on"; return false; @@ -114,10 +115,14 @@ namespace mongo { if (1 == idxs.size()) { result.append("ns", ns); - return twod_internal::TwoDGeoNearRunner::run2DGeoNear(d, idxs[0], cmdObj, commonArgs, errmsg, result); + twod_internal::TwoDGeoNearRunner::run2DGeoNear(d, idxs[0], cmdObj, commonArgs, errmsg, result); + BSONObjBuilder stats(result.subobjStart("stats")); + stats.append("time", cc().curop()->elapsedMillis()); + stats.done(); + return true; } - d->findIndexByType("2dsphere", idxs); + d->findIndexByType(IndexNames::GEO_2DSPHERE, idxs); if (idxs.size() > 1) { errmsg = "more than one 2dsphere index, not sure which to run geoNear on"; return false; @@ -125,7 +130,11 @@ namespace mongo { if (1 == idxs.size()) { result.append("ns", ns); - return run2DSphereGeoNear(d, idxs[0], cmdObj, commonArgs, errmsg, result); + run2DSphereGeoNear(d, idxs[0], cmdObj, commonArgs, errmsg, result); + BSONObjBuilder stats(result.subobjStart("stats")); + stats.append("time", cc().curop()->elapsedMillis()); + stats.done(); + return true; } errmsg = "no geo indices for geoNear"; @@ -146,7 +155,7 @@ namespace mongo { BSONObjIterator i(descriptor->keyPattern()); while (i.more()) { BSONElement e = i.next(); - if (e.type() == String && S2IndexingParams::SPHERE_2D_NAME == e.valuestr()) { + if (e.type() == String && IndexNames::GEO_2DSPHERE == e.valuestr()) { geoFieldNames.push_back(e.fieldName()); } } @@ -213,7 +222,6 @@ namespace mongo { resultBuilder.done(); BSONObjBuilder stats(result.subobjStart("stats")); - stats.append("time", cc().curop()->elapsedMillis()); stats.appendNumber("nscanned", nic->nscanned()); stats.append("avgDistance", totalDistance / results); stats.append("maxDistance", farthestDist); diff --git a/src/mongo/db/geo/haystack.cpp b/src/mongo/db/geo/haystack.cpp index 0d77876a68a..6088d7c8f1e 100644 --- a/src/mongo/db/geo/haystack.cpp +++ b/src/mongo/db/geo/haystack.cpp @@ -176,14 +176,6 @@ namespace mongo { } } - // XXX: Who could call this and how do they know not to actually do so? - shared_ptr<Cursor> newCursor(const BSONObj& query, const BSONObj& order, - int numWanted) const { - shared_ptr<Cursor> c; - verify(0); - return c; - } - void searchCommand(NamespaceDetails* nsd, const BSONObj& n /*near*/, double maxDistance, const BSONObj& search, BSONObjBuilder& result, unsigned limit) { diff --git a/src/mongo/db/geo/s2index.cpp b/src/mongo/db/geo/s2index.cpp index 5b024104a22..568a8db6145 100644 --- a/src/mongo/db/geo/s2index.cpp +++ b/src/mongo/db/geo/s2index.cpp @@ -138,50 +138,6 @@ namespace mongo { } } - // Entry point for a search. - virtual shared_ptr<Cursor> newCursor(const BSONObj& query, const BSONObj& order, - int numWanted) const { - shared_ptr<Cursor> c; - verify(0); - return c; - } - - virtual IndexSuitability suitability(const FieldRangeSet& queryConstraints, - const BSONObj& order) const { - BSONObj query = queryConstraints.originalQuery(); - - for (size_t i = 0; i < _fields.size(); ++i) { - const IndexedField &field = _fields[i]; - if (IndexedField::GEO != field.type) { continue; } - - BSONElement e = query.getFieldDotted(field.name); - // Some locations are given to us as arrays. Sigh. - if (Array == e.type()) { return HELPFUL; } - if (Object != e.type()) { continue; } - // getGtLtOp is horribly misnamed and really means get the operation. - switch (e.embeddedObject().firstElement().getGtLtOp()) { - case BSONObj::opNEAR: - return OPTIMAL; - case BSONObj::opWITHIN: { - BSONElement elt = e.embeddedObject().firstElement(); - if (Object != elt.type()) { continue; } - const char* fname = elt.embeddedObject().firstElement().fieldName(); - if (mongoutils::str::equals("$geometry", fname) - || mongoutils::str::equals("$centerSphere", fname)) { - return OPTIMAL; - } else { - return USELESS; - } - } - case BSONObj::opGEO_INTERSECTS: - return OPTIMAL; - default: - return USELESS; - } - } - return USELESS; - } - const IndexDetails* getDetails() const { return _spec->getDetails(); } // These are used by the geoNear command. geoNear constructs its own cursor. diff --git a/src/mongo/db/hashindex.cpp b/src/mongo/db/hashindex.cpp index 61c8f394af5..a658ba1467f 100644 --- a/src/mongo/db/hashindex.cpp +++ b/src/mongo/db/hashindex.cpp @@ -69,13 +69,6 @@ namespace mongo { HashedIndexType::~HashedIndexType() { } - IndexSuitability HashedIndexType::suitability( const FieldRangeSet& queryConstraints , - const BSONObj& order ) const { - if ( queryConstraints.isPointIntervalSet( _hashedField ) ) - return HELPFUL; - return USELESS; - } - void HashedIndexType::getKeys( const BSONObj &obj, BSONObjSet &keys ) const { string hashedFieldCopy = string( _hashedField ); const char* hashedFieldCopyPtr = hashedFieldCopy.c_str(); @@ -92,13 +85,6 @@ namespace mongo { } } - shared_ptr<Cursor> HashedIndexType::newCursor(const BSONObj& query, const BSONObj& order, - int numWanted) const { - shared_ptr<Cursor> c; - verify(0); - return c; - } - /* This class registers HASHED_INDEX_NAME in a global map of special index types * Using this pattern, any index with the pattern, {fieldname : HASHED_INDEX_NAME} * will be recognized as a HashedIndexType and the associated methods will be used. diff --git a/src/mongo/db/hashindex.h b/src/mongo/db/hashindex.h index 7451ba307f1..f7bfa4b4552 100644 --- a/src/mongo/db/hashindex.h +++ b/src/mongo/db/hashindex.h @@ -64,21 +64,6 @@ namespace mongo { HashedIndexType( const IndexPlugin* plugin , const IndexSpec* spec ); virtual ~HashedIndexType(); - /* This index is only considered "HELPFUL" for a query - * if it's the union of at least one equality constraint on the - * hashed field. Otherwise it's considered USELESS. - * Example queries (supposing the indexKey is {a : "hashed"}): - * {a : 3} HELPFUL - * {a : 3 , b : 3} HELPFUL - * {a : {$in : [3,4]}} HELPFUL - * {a : {$gte : 3, $lte : 3}} HELPFUL - * {} USELESS - * {b : 3} USELESS - * {a : {$gt : 3}} USELESS - */ - IndexSuitability suitability( const FieldRangeSet& queryConstraints , - const BSONObj& order ) const; - /* The input is "obj" which should have a field corresponding to the hashedfield. * The output is a BSONObj with a single BSONElement whose value is the hash * Eg if this is an index on "a" we have @@ -93,13 +78,6 @@ namespace mongo { */ BSONElement missingField() const { return _missingKey.firstElement(); } - /* The newCursor method works for suitable queries by generating a BtreeCursor - * using the hash of point-intervals parsed by FieldRangeSet. - * For unsuitable queries it just instantiates a btree cursor over the whole tree - */ - shared_ptr<Cursor> newCursor( const BSONObj& query , - const BSONObj& order , int numWanted ) const; - /* Takes a BSONElement, seed and hashVersion, and outputs the * 64-bit hash used for this index * E.g. if the element is {a : 3} this outputs v1-hash(3) diff --git a/src/mongo/db/index/2d_access_method.cpp b/src/mongo/db/index/2d_access_method.cpp index 33e7c8aede8..1d69796833f 100644 --- a/src/mongo/db/index/2d_access_method.cpp +++ b/src/mongo/db/index/2d_access_method.cpp @@ -20,6 +20,7 @@ #include <vector> #include "mongo/db/geo/core.h" +#include "mongo/db/index_names.h" #include "mongo/db/index/2d_common.h" #include "mongo/db/index/2d_index_cursor.h" #include "mongo/db/jsobj.h" @@ -27,8 +28,6 @@ namespace mongo { - const string TwoDIndexingParams::TWOD_NAME = "2d"; - static double configValueWithDefault(IndexDescriptor *desc, const string& name, double def) { BSONElement e = desc->getInfoElement(name); if (e.isNumber()) { return e.numberDouble(); } @@ -41,7 +40,7 @@ namespace mongo { BSONObjIterator i(descriptor->keyPattern()); while (i.more()) { BSONElement e = i.next(); - if (e.type() == String && TwoDIndexingParams::TWOD_NAME == e.valuestr()) { + if (e.type() == String && IndexNames::GEO_2D == e.valuestr()) { uassert(16800, "can't have 2 geo fields", _params.geo.size() == 0); uassert(16801, "2d has to be first in index", _params.other.size() == 0); _params.geo = e.fieldName(); diff --git a/src/mongo/db/index/2d_common.h b/src/mongo/db/index/2d_common.h index 630d53afcd2..67426ce209b 100644 --- a/src/mongo/db/index/2d_common.h +++ b/src/mongo/db/index/2d_common.h @@ -24,7 +24,6 @@ namespace mongo { struct TwoDIndexingParams { - static const string TWOD_NAME; string geo; vector<pair<string, int> > other; shared_ptr<GeoHashConverter> geoHashConverter; diff --git a/src/mongo/db/index/2d_index_cursor.cpp b/src/mongo/db/index/2d_index_cursor.cpp index 3c203520748..ab8d9944a89 100644 --- a/src/mongo/db/index/2d_index_cursor.cpp +++ b/src/mongo/db/index/2d_index_cursor.cpp @@ -17,7 +17,6 @@ #include "mongo/db/index/2d_index_cursor.h" #include "mongo/db/btreecursor.h" -#include "mongo/db/curop.h" #include "mongo/db/index/2d_access_method.h" #include "mongo/db/index/btree_interface.h" #include "mongo/db/index/catalog_hack.h" @@ -1821,7 +1820,6 @@ namespace mongo { arr.done(); BSONObjBuilder stats(result.subobjStart("stats")); - stats.append("time", cc().curop()->elapsedMillis()); stats.appendNumber("btreelocs", gs._nscanned); stats.appendNumber("nscanned", gs._lookedAt); stats.appendNumber("objectsLoaded", gs._objectsLoaded); diff --git a/src/mongo/db/index/catalog_hack.h b/src/mongo/db/index/catalog_hack.h index 34bf8171075..17eaacf78e5 100644 --- a/src/mongo/db/index/catalog_hack.h +++ b/src/mongo/db/index/catalog_hack.h @@ -25,6 +25,7 @@ #include "mongo/db/index/index_access_method.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/index/s2_access_method.h" +#include "mongo/db/index_names.h" #include "mongo/db/keypattern.h" namespace mongo { @@ -41,17 +42,17 @@ namespace mongo { static BtreeBasedAccessMethod* getBtreeBasedIndex(IndexDescriptor* desc) { string type = KeyPattern::findPluginName(desc->keyPattern()); - if ("hashed" == type) { + if (IndexNames::HASHED == type) { return new HashAccessMethod(desc); - } else if ("2dsphere" == type) { + } else if (IndexNames::GEO_2DSPHERE == type) { return new S2AccessMethod(desc); - } else if ("text" == type || "_fts" == type) { + } else if (IndexNames::TEXT == type || IndexNames::TEXT_INTERNAL == type) { return new FTSAccessMethod(desc); - } else if ("geoHaystack" == type) { + } else if (IndexNames::GEO_HAYSTACK == type) { return new HaystackAccessMethod(desc); } else if ("" == type) { return new BtreeAccessMethod(desc); - } else if ("2d" == type) { + } else if (IndexNames::GEO_2D == type) { return new TwoDAccessMethod(desc); } else { cout << "Can't find index for keypattern " << desc->keyPattern() << endl; @@ -62,17 +63,17 @@ namespace mongo { static IndexAccessMethod* getIndex(IndexDescriptor* desc) { string type = KeyPattern::findPluginName(desc->keyPattern()); - if ("hashed" == type) { + if (IndexNames::HASHED == type) { return new HashAccessMethod(desc); - } else if ("2dsphere" == type) { + } else if (IndexNames::GEO_2DSPHERE == type) { return new S2AccessMethod(desc); - } else if ("text" == type || "_fts" == type) { + } else if (IndexNames::TEXT == type || IndexNames::TEXT_INTERNAL == type) { return new FTSAccessMethod(desc); - } else if ("geoHaystack" == type) { + } else if (IndexNames::GEO_HAYSTACK == type) { return new HaystackAccessMethod(desc); } else if ("" == type) { return new BtreeAccessMethod(desc); - } else if ("2d" == type) { + } else if (IndexNames::GEO_2D == type) { return new TwoDAccessMethod(desc); } else { cout << "Can't find index for keypattern " << desc->keyPattern() << endl; diff --git a/src/mongo/db/index/emulated_cursor.h b/src/mongo/db/index/emulated_cursor.h index f283768b4d6..e4f41631e98 100644 --- a/src/mongo/db/index/emulated_cursor.h +++ b/src/mongo/db/index/emulated_cursor.h @@ -20,6 +20,7 @@ #include <set> #include "mongo/db/cursor.h" +#include "mongo/db/index_names.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/index/index_cursor.h" @@ -28,10 +29,6 @@ namespace mongo { - static const string SPHERE_2D_NAME = "2dsphere"; - static const string HASH_NAME = "hashed"; - static const string GEO_2D_NAME = "2d"; - /** * This class is a crutch to help migrate from the old everything-is-a-Cursor world to the new * index API world. It wraps a new IndexCursor in the old Cursor. We only use this for special @@ -43,6 +40,14 @@ namespace mongo { * Create a new EmulatedCursor. * Takes ownership of the provided indexAccessMethod indexCursor. * Takes ownership of the IndexDescriptor inside the indexAccessMethod. + * + * Full semantics of numWanted: + * numWanted == 0 : Return any number of results, but try to return in batches of 101. + * numWanted == 1 : Return exactly one result. + * numWanted > 1 : Return any number of results, but try to return in batches of numWanted. + * + * In practice, your cursor can ignore numWanted, as enforcement of limits is done + * by the caller. */ static EmulatedCursor* make(IndexDescriptor* descriptor, IndexAccessMethod* indexAccessMethod, @@ -147,13 +152,13 @@ namespace mongo { options.numWanted = numWanted; cursor->setOptions(options); - if (HASH_NAME == _pluginName) { + if (IndexNames::HASHED == _pluginName) { _supportYields = true; _supportGetMore = true; _modifiedKeys = true; _shouldGetSetDup = false; _autoDedup = true; - } else if (SPHERE_2D_NAME == _pluginName) { + } else if (IndexNames::GEO_2DSPHERE == _pluginName) { _supportYields = true; _supportGetMore = true; _modifiedKeys = true; @@ -161,7 +166,7 @@ namespace mongo { // to not de-dup themselves. _shouldGetSetDup = true; _autoDedup = false; - } else if (GEO_2D_NAME == _pluginName) { + } else if (IndexNames::GEO_2D == _pluginName) { _supportYields = false; _supportGetMore = true; _modifiedKeys = true; @@ -190,7 +195,7 @@ namespace mongo { _nscanned = 0; } - if (HASH_NAME == _pluginName) { + if (IndexNames::HASHED == _pluginName) { // Quoted from hashindex.cpp: // Force a match of the query against the actual document by giving // the cursor a matcher with an empty indexKeyPattern. This ensures the @@ -198,7 +203,7 @@ namespace mongo { // NOTE: this forcing is necessary due to potential hash collisions _matcher = shared_ptr<CoveredIndexMatcher>( new CoveredIndexMatcher(query, BSONObj())); - } else if (SPHERE_2D_NAME == _pluginName) { + } else if (IndexNames::GEO_2DSPHERE == _pluginName) { // Technically, the non-geo indexed fields are in the key, though perhaps not in the // exact format the matcher expects (arrays). So, we match against all non-geo // fields. This could possibly be relaxed in some fashion in the future? Requires @@ -208,7 +213,7 @@ namespace mongo { while (keyIt.more()) { BSONElement e = keyIt.next(); - if (e.type() == String && SPHERE_2D_NAME == e.valuestr()) { + if (e.type() == String && IndexNames::GEO_2DSPHERE == e.valuestr()) { fieldsToNuke.append(e.fieldName(), ""); } } @@ -217,7 +222,7 @@ namespace mongo { _matcher = shared_ptr<CoveredIndexMatcher>( new CoveredIndexMatcher(filteredQuery, _keyPattern)); - } else if (GEO_2D_NAME == _pluginName) { + } else if (IndexNames::GEO_2D == _pluginName) { // No-op matcher. _matcher = shared_ptr<CoveredIndexMatcher>( new CoveredIndexMatcher(BSONObj(), BSONObj())); @@ -225,7 +230,7 @@ namespace mongo { } void checkMultiKeyProperties() { - if (GEO_2D_NAME != _pluginName) { + if (IndexNames::GEO_2D != _pluginName) { _isMultiKey = _shouldGetSetDup = _descriptor->isMultikey(); } } diff --git a/src/mongo/db/index/s2_access_method.cpp b/src/mongo/db/index/s2_access_method.cpp index b4f7cd3f0fb..619775566c5 100644 --- a/src/mongo/db/index/s2_access_method.cpp +++ b/src/mongo/db/index/s2_access_method.cpp @@ -20,6 +20,7 @@ #include "mongo/base/status.h" #include "mongo/db/geo/geoparser.h" +#include "mongo/db/index_names.h" #include "mongo/db/index/s2_index_cursor.h" #include "mongo/db/jsobj.h" #include "third_party/s2/s2.h" @@ -43,8 +44,6 @@ namespace { namespace mongo { - const string S2IndexingParams::SPHERE_2D_NAME = "2dsphere"; - // Thanks, Wikipedia. const double S2IndexingParams::kRadiusOfEarthInMeters = (6378.1 * 1000); @@ -78,7 +77,7 @@ namespace mongo { BSONObjIterator i(descriptor->keyPattern()); while (i.more()) { BSONElement e = i.next(); - if (e.type() == String && S2IndexingParams::SPHERE_2D_NAME == e.valuestr()) { + if (e.type() == String && IndexNames::GEO_2DSPHERE == e.valuestr()) { ++geoFields; } } @@ -105,7 +104,7 @@ namespace mongo { obj.getFieldsDotted(e.fieldName(), fieldElements, false); BSONObjSet keysForThisField; - if (S2IndexingParams::SPHERE_2D_NAME == e.valuestr()) { + if (IndexNames::GEO_2DSPHERE == e.valuestr()) { getGeoKeys(fieldElements, &keysForThisField); } else { getLiteralKeys(fieldElements, &keysForThisField); diff --git a/src/mongo/db/index/s2_common.h b/src/mongo/db/index/s2_common.h index d9de45b9891..6f10f9fd760 100644 --- a/src/mongo/db/index/s2_common.h +++ b/src/mongo/db/index/s2_common.h @@ -36,8 +36,6 @@ namespace mongo { }; struct S2IndexingParams { - static const string SPHERE_2D_NAME; - static const double kRadiusOfEarthInMeters; // Since we take the cartesian product when we generate keys for an insert, diff --git a/src/mongo/db/index/s2_index_cursor.cpp b/src/mongo/db/index/s2_index_cursor.cpp index 0601fb9b5b7..01e38b49d8f 100644 --- a/src/mongo/db/index/s2_index_cursor.cpp +++ b/src/mongo/db/index/s2_index_cursor.cpp @@ -17,6 +17,7 @@ #include "mongo/db/index/s2_index_cursor.h" #include "mongo/db/btreecursor.h" +#include "mongo/db/index_names.h" #include "mongo/db/index/index_cursor.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/index/s2_near_cursor.h" @@ -42,7 +43,7 @@ namespace mongo { while (keyIt.more()) { BSONElement keyElt = keyIt.next(); - if (keyElt.type() != String || S2IndexingParams::SPHERE_2D_NAME != keyElt.valuestr()) { + if (keyElt.type() != String || IndexNames::GEO_2DSPHERE != keyElt.valuestr()) { continue; } diff --git a/src/mongo/db/index_names.cpp b/src/mongo/db/index_names.cpp new file mode 100644 index 00000000000..6a9aac30bc3 --- /dev/null +++ b/src/mongo/db/index_names.cpp @@ -0,0 +1,26 @@ +/** +* Copyright (C) 2013 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 "mongo/db/index_names.h" + +namespace mongo { + const string IndexNames::GEO_2D = "2d"; + const string IndexNames::GEO_HAYSTACK = "geoHaystack"; + const string IndexNames::GEO_2DSPHERE = "2dsphere"; + const string IndexNames::TEXT = "text"; + const string IndexNames::TEXT_INTERNAL = "_fts"; + const string IndexNames::HASHED = "hashed"; +} // namespace mongo diff --git a/src/mongo/db/index_names.h b/src/mongo/db/index_names.h new file mode 100644 index 00000000000..db16ab0067a --- /dev/null +++ b/src/mongo/db/index_names.h @@ -0,0 +1,39 @@ +/** +* Copyright (C) 2013 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 + +#include <string> + +namespace mongo { + + using std::string; + + /** + * We use the string representation of index names all over the place, so we declare them all + * once here. + */ + class IndexNames { + public: + static const string GEO_2D; + static const string GEO_HAYSTACK; + static const string GEO_2DSPHERE; + static const string TEXT; + static const string TEXT_INTERNAL; + static const string HASHED; + }; + +} // namespace mongo diff --git a/src/mongo/db/index_selection.cpp b/src/mongo/db/index_selection.cpp new file mode 100644 index 00000000000..deaa130d255 --- /dev/null +++ b/src/mongo/db/index_selection.cpp @@ -0,0 +1,170 @@ +/** +* Copyright (C) 2013 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 "mongo/db/index_selection.h" + +#include "mongo/db/index_names.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/keypattern.h" +#include "mongo/db/queryutil.h" + +namespace mongo { + + IndexSuitability IndexSelection::isSuitableFor(const BSONObj& keyPattern, + const FieldRangeSet& queryConstraints, + const BSONObj& order) { + + string type = KeyPattern::findPluginName(keyPattern); + BSONObj query = queryConstraints.originalQuery(); + + if (IndexNames::HASHED == type) { + /* This index is only considered "HELPFUL" for a query + * if it's the union of at least one equality constraint on the + * hashed field. Otherwise it's considered USELESS. + * Example queries (supposing the indexKey is {a : "hashed"}): + * {a : 3} HELPFUL + * {a : 3 , b : 3} HELPFUL + * {a : {$in : [3,4]}} HELPFUL + * {a : {$gte : 3, $lte : 3}} HELPFUL + * {} USELESS + * {b : 3} USELESS + * {a : {$gt : 3}} USELESS + */ + BSONElement firstElt = keyPattern.firstElement(); + if (queryConstraints.isPointIntervalSet(firstElt.fieldName())) { + return HELPFUL; + } else { + return USELESS; + } + } else if (IndexNames::GEO_2DSPHERE == type) { + BSONObjIterator i(keyPattern); + while (i.more()) { + BSONElement ie = i.next(); + + if (ie.type() != String || IndexNames::GEO_2DSPHERE != ie.valuestr()) { + continue; + } + + BSONElement e = query.getFieldDotted(ie.fieldName()); + // Some locations are given to us as arrays. Sigh. + if (Array == e.type()) { return HELPFUL; } + if (Object != e.type()) { continue; } + // getGtLtOp is horribly misnamed and really means get the operation. + switch (e.embeddedObject().firstElement().getGtLtOp()) { + case BSONObj::opNEAR: + return OPTIMAL; + case BSONObj::opWITHIN: { + BSONElement elt = e.embeddedObject().firstElement(); + if (Object != elt.type()) { continue; } + const char* fname = elt.embeddedObject().firstElement().fieldName(); + if (mongoutils::str::equals("$geometry", fname) + || mongoutils::str::equals("$centerSphere", fname)) { + return OPTIMAL; + } else { + return USELESS; + } + } + case BSONObj::opGEO_INTERSECTS: + return OPTIMAL; + default: + return USELESS; + } + } + return USELESS; + } else if (IndexNames::TEXT == type || IndexNames::TEXT_INTERNAL == type) { + return USELESS; + } else if (IndexNames::GEO_HAYSTACK == type) { + return USELESS; + } else if ("" == type) { + // This is a quick first pass to determine the suitability of the index. It produces + // some false positives (returns HELPFUL for some indexes which are not particularly). + // When we return HELPFUL a more precise determination of utility is done by the query + // optimizer. + + // check whether any field in the index is constrained at all by the query + BSONForEach( elt, keyPattern ){ + const FieldRange& frange = queryConstraints.range( elt.fieldName() ); + if( ! frange.universal() ) + return HELPFUL; + } + + // or whether any field in the desired sort order is in the index + set<string> orderFields; + order.getFieldNames( orderFields ); + BSONForEach( k, keyPattern ) { + if ( orderFields.find( k.fieldName() ) != orderFields.end() ) + return HELPFUL; + } + return USELESS; + } else if (IndexNames::GEO_2D == type) { + string fieldName; + + BSONObjIterator i(keyPattern); + while (i.more()) { + BSONElement ie = i.next(); + + if (ie.type() == String && IndexNames::GEO_2D == ie.valuestr()) { + fieldName = ie.fieldName(); + break; + } + } + + verify("" != fieldName); + + BSONElement e = query.getFieldDotted(fieldName); + switch (e.type()) { + case Object: { + BSONObj sub = e.embeddedObject(); + switch (sub.firstElement().getGtLtOp()) { + case BSONObj::opNEAR: + return OPTIMAL; + case BSONObj::opWITHIN: { + // Don't return optimal if it's $within: {$geometry: ... } + // because we will error out in that case, but the matcher + // or 2dsphere index may handle it. + BSONElement elt = sub.firstElement(); + if (Object == elt.type()) { + BSONObjIterator it(elt.embeddedObject()); + while (it.more()) { + BSONElement elt = it.next(); + if (mongoutils::str::equals("$geometry", elt.fieldName())) { + return USELESS; + } + } + } + return OPTIMAL; + } + default: + // We can try to match if there's no other indexing defined, + // this is assumed a point + return HELPFUL; + } + } + case Array: + // We can try to match if there's no other indexing defined, + // this is assumed a point + return HELPFUL; + default: + return USELESS; + } + } else { + cout << "Can't find index for keypattern " << keyPattern << endl; + verify(0); + return USELESS; + } + } + +} // namespace mongo diff --git a/src/mongo/db/index_selection.h b/src/mongo/db/index_selection.h new file mode 100644 index 00000000000..e64978f638e --- /dev/null +++ b/src/mongo/db/index_selection.h @@ -0,0 +1,42 @@ +/** +* Copyright (C) 2013 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 + +#include "mongo/db/indexkey.h" // for IndexSuitability + +namespace mongo { + + class BSONObj; + class FieldRangeSet; + + /** + * This class is part of query optimization. For a given index (as uniquely described by + * keyPattern) and a given query (queryConstraints) and given sort order (order), return how + * "suitable" the index is for the query + sort. + * + * USELESS indices are never used. + * HELPFUL indices are explored and compared to other HELPFUL indices. + * OPTIMAL indices short-circuit the search process and are always used. + */ + class IndexSelection { + public: + static IndexSuitability isSuitableFor(const BSONObj& keyPattern, + const FieldRangeSet& queryConstraints, + const BSONObj& order); + }; + +} // namespace mongo diff --git a/src/mongo/db/indexkey.cpp b/src/mongo/db/indexkey.cpp index 616dbd9a499..b0203713ab4 100644 --- a/src/mongo/db/indexkey.cpp +++ b/src/mongo/db/indexkey.cpp @@ -469,41 +469,6 @@ namespace mongo { } } - - IndexSuitability IndexSpec::suitability( const FieldRangeSet& queryConstraints , - const BSONObj& order ) const { - if ( _indexType.get() ) - return _indexType->suitability( queryConstraints , order ); - return _suitability( queryConstraints , order ); - } - - IndexSuitability IndexSpec::_suitability( const FieldRangeSet& queryConstraints , - const BSONObj& order ) const { - // This is a quick first pass to determine the suitability of the index. It produces some - // false positives (returns HELPFUL for some indexes which are not particularly). When we - // return HELPFUL a more precise determination of utility is done by the query optimizer. - - // check whether any field in the index is constrained at all by the query - BSONForEach( elt, keyPattern ){ - const FieldRange& frange = queryConstraints.range( elt.fieldName() ); - if( ! frange.universal() ) - return HELPFUL; - } - // or whether any field in the desired sort order is in the index - set<string> orderFields; - order.getFieldNames( orderFields ); - BSONForEach( k, keyPattern ) { - if ( orderFields.find( k.fieldName() ) != orderFields.end() ) - return HELPFUL; - } - return USELESS; - } - - IndexSuitability IndexType::suitability( const FieldRangeSet& queryConstraints , - const BSONObj& order ) const { - return _spec->_suitability( queryConstraints , order ); - } - int IndexSpec::indexVersion() const { if ( !info.hasField( "v" ) ) { return DefaultIndexVersionNumber; diff --git a/src/mongo/db/indexkey.h b/src/mongo/db/indexkey.h index 187f2fe48bc..2acd38e998d 100644 --- a/src/mongo/db/indexkey.h +++ b/src/mongo/db/indexkey.h @@ -18,11 +18,12 @@ #pragma once -#include "mongo/pch.h" -#include "diskloc.h" -#include "jsobj.h" #include <map> +#include "mongo/db/diskloc.h" +#include "mongo/db/index_names.h" +#include "mongo/db/jsobj.h" + namespace mongo { extern const int DefaultIndexVersionNumber; @@ -57,16 +58,6 @@ namespace mongo { */ virtual BSONElement missingField() const; - /* Full semantics of numWanted: - * numWanted == 0 : Return any number of results, but try to return in batches of 101. - * numWanted == 1 : Return exactly one result. - * numWanted > 1 : Return any number of results, but try to return in batches of numWanted. - * - * In practice, your cursor can ignore numWanted, as enforcement of limits is done - * by the caller. - */ - virtual shared_ptr<Cursor> newCursor( const BSONObj& query , const BSONObj& order , int numWanted ) const = 0; - /** optional op : changes query to match what's in the index */ virtual BSONObj fixKey( const BSONObj& in ) { return in; } @@ -78,16 +69,6 @@ namespace mongo { const BSONObj& keyPattern() const; - /* Determines the suitability level of this index for answering a given query. The query is - * represented as a set of constraints given by a FieldRangeSet, and a desired ordering of - * the output. - * - * Note: it is the responsibility of the caller to pass in the correct FieldRangeSet, which - * may depend upon whether this is a single or multi-key index at the time of calling. - */ - virtual IndexSuitability suitability( const FieldRangeSet& queryConstraints , - const BSONObj& order ) const; - virtual bool scanAndOrderRequired( const BSONObj& query , const BSONObj& order ) const ; protected: @@ -149,9 +130,9 @@ namespace mongo { */ static bool existedBefore24(const string& name) { return name.empty() - || name == "2d" - || name == "geoHaystack" - || name == "hashed" + || name == IndexNames::GEO_2D + || name == IndexNames::GEO_HAYSTACK + || name == IndexNames::HASHED ; } @@ -224,9 +205,6 @@ namespace mongo { return _details; } - IndexSuitability suitability( const FieldRangeSet& queryConstraints , - const BSONObj& order ) const ; - bool isSparse() const { return _sparse; } string toString() const; @@ -235,9 +213,6 @@ namespace mongo { int indexVersion() const; - IndexSuitability _suitability( const FieldRangeSet& queryConstraints , - const BSONObj& order ) const ; - BSONSizeTracker _sizeTracker; vector<const char*> _fieldNames; vector<BSONElement> _fixed; diff --git a/src/mongo/db/query_optimizer_internal.cpp b/src/mongo/db/query_optimizer_internal.cpp index e182289d750..0bed4fbdaf8 100644 --- a/src/mongo/db/query_optimizer_internal.cpp +++ b/src/mongo/db/query_optimizer_internal.cpp @@ -20,6 +20,7 @@ #include "mongo/client/dbclientinterface.h" #include "mongo/db/db.h" +#include "mongo/db/index_selection.h" #include "mongo/db/pagefault.h" #include "mongo/db/parsed_query.h" #include "mongo/db/query_plan_selection_policy.h" @@ -544,7 +545,8 @@ namespace mongo { IndexDetails& ii = i.next(); const IndexSpec& spec = ii.getSpec(); if (special.has(spec.getTypeName()) && - spec.suitability( _qps.frsp().frsForIndex(d, j), _qps.order() ) != USELESS ) { + (USELESS != IndexSelection::isSuitableFor(spec.keyPattern, + _qps.frsp().frsForIndex(d, j), _qps.order()))) { uassert( 16330, "'special' query operator not allowed", _allowSpecial ); _qps.setSinglePlan( newPlan( d, j, BSONObj(), BSONObj(), spec.getTypeName())); return true; @@ -1554,9 +1556,8 @@ namespace mongo { // No matches are possible in the index so the index may be useful. return true; } - - return d->idx( idxNo ).getSpec().suitability( frsp.frsForIndex( d , idxNo ) , order ) - != USELESS; + return USELESS != IndexSelection::isSuitableFor(keyPattern, frsp.frsForIndex( d , idxNo ) , + order ); } void QueryUtilIndexed::clearIndexesForPatterns( const FieldRangeSetPair& frsp, diff --git a/src/mongo/db/query_plan.cpp b/src/mongo/db/query_plan.cpp index de8a6b1b342..a055a55df3d 100644 --- a/src/mongo/db/query_plan.cpp +++ b/src/mongo/db/query_plan.cpp @@ -18,6 +18,7 @@ #include "mongo/db/btreecursor.h" #include "mongo/db/cmdline.h" +#include "mongo/db/index_selection.h" #include "mongo/db/index/catalog_hack.h" #include "mongo/db/index/emulated_cursor.h" #include "mongo/db/index/index_descriptor.h" @@ -115,8 +116,8 @@ namespace mongo { // If the parsing or index indicates this is a special query, don't continue the processing if (!_special.empty() || - ( _index->getSpec().getType() && - _index->getSpec().getType()->suitability( _frs, _order ) != USELESS ) ) { + ( _index->getSpec().getType() && (USELESS != + IndexSelection::isSuitableFor(_index->getSpec().keyPattern, _frs, _order)))) { _type = _index->getSpec().getType(); if (_special.empty()) _special = _index->getSpec().getType()->getPlugin()->getName(); diff --git a/src/mongo/db/queryutil.cpp b/src/mongo/db/queryutil.cpp index a191be8e448..dffb52c34bd 100644 --- a/src/mongo/db/queryutil.cpp +++ b/src/mongo/db/queryutil.cpp @@ -16,6 +16,7 @@ #include "pch.h" #include "mongo/db/queryutil.h" +#include "mongo/db/index_names.h" #include "pdfile.h" #include "../util/startup_test.h" @@ -438,15 +439,15 @@ namespace mongo { break; } case BSONObj::opWITHIN: - _special.add("2d", SpecialIndices::NO_INDEX_REQUIRED); - _special.add("2dsphere", SpecialIndices::NO_INDEX_REQUIRED); + _special.add(IndexNames::GEO_2D, SpecialIndices::NO_INDEX_REQUIRED); + _special.add(IndexNames::GEO_2DSPHERE, SpecialIndices::NO_INDEX_REQUIRED); break; case BSONObj::opNEAR: - _special.add("2d", SpecialIndices::INDEX_REQUIRED); - _special.add("2dsphere", SpecialIndices::INDEX_REQUIRED); + _special.add(IndexNames::GEO_2D, SpecialIndices::INDEX_REQUIRED); + _special.add(IndexNames::GEO_2DSPHERE, SpecialIndices::INDEX_REQUIRED); break; case BSONObj::opGEO_INTERSECTS: - _special.add("2dsphere", SpecialIndices::NO_INDEX_REQUIRED); + _special.add(IndexNames::GEO_2DSPHERE, SpecialIndices::NO_INDEX_REQUIRED); break; case BSONObj::opEXISTS: { if ( !existsSpec ) { diff --git a/src/mongo/dbtests/namespacetests.cpp b/src/mongo/dbtests/namespacetests.cpp index 58ebcce1392..e13a25202f3 100644 --- a/src/mongo/dbtests/namespacetests.cpp +++ b/src/mongo/dbtests/namespacetests.cpp @@ -24,6 +24,7 @@ #include "../db/db.h" #include "../db/json.h" #include "mongo/db/hashindex.h" +#include "mongo/db/index_selection.h" #include "mongo/db/index/btree_key_generator.h" #include "mongo/db/queryutil.h" @@ -958,7 +959,7 @@ namespace NamespaceTests { FieldRangeSet frs( "n/a", BSON( "a" << 2 ), true , true ); // Checking a return value of HELPFUL instead of OPTIMAL is descriptive rather than // normative. See SERVER-4485. - ASSERT_EQUALS( HELPFUL, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -968,7 +969,7 @@ namespace NamespaceTests { void run() { IndexSpec spec( BSON( "a" << 1 ), BSONObj() ); FieldRangeSet frs( "n/a", BSON( "b" << 2 ), true , true ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -978,7 +979,7 @@ namespace NamespaceTests { void run() { IndexSpec spec( BSON( "a" << 1 ), BSONObj() ); FieldRangeSet frs( "n/a", BSON( "a.b" << 2 ), true , true ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -988,7 +989,7 @@ namespace NamespaceTests { void run() { IndexSpec spec( BSON( "a.b" << 1 ), BSONObj() ); FieldRangeSet frs( "n/a", BSON( "a" << 2 ), true , true ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1001,7 +1002,7 @@ namespace NamespaceTests { void run() { IndexSpec spec( BSON( "a.b" << 1 ), BSONObj() ); FieldRangeSet frs( "n/a", BSON( "a" << BSON( "b" << 2 ) ), true , true ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1011,7 +1012,7 @@ namespace NamespaceTests { void run() { IndexSpec spec( BSON( "a" << 1 ), BSONObj() ); FieldRangeSet frs( "n/a", BSONObj(), true , true ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frs, BSON( "a" << 1 ) ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSON( "a" << 1 ) ) ); } }; @@ -1021,7 +1022,7 @@ namespace NamespaceTests { void run() { IndexSpec spec( BSON( "a" << -1 ), BSONObj() ); FieldRangeSet frs( "n/a", BSONObj(), true , true ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frs, BSON( "a" << 1 ) ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSON( "a" << 1 ) ) ); } }; @@ -1034,7 +1035,7 @@ namespace NamespaceTests { void run() { IndexSpec spec( BSON( "a" << 1 ), BSONObj() ); FieldRangeSet frs( "n/a", BSONObj(), true , true ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frs, BSON( "b" << 1 << "a" << 1 ) ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSON( "b" << 1 << "a" << 1 ) ) ); } }; @@ -1044,7 +1045,7 @@ namespace NamespaceTests { void run() { IndexSpec spec( BSON( "a" << 1 ), BSONObj() ); FieldRangeSet frs( "n/a", BSONObj(), true , true ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSON( "b" << 1 ) ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSON( "b" << 1 ) ) ); } }; @@ -1054,7 +1055,7 @@ namespace NamespaceTests { void run() { IndexSpec spec( BSON( "a" << 1 ), BSONObj() ); FieldRangeSet frs( "n/a", BSONObj(), true , true ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSON( "a.b" << 1 ) ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSON( "a.b" << 1 ) ) ); } }; @@ -1064,7 +1065,7 @@ namespace NamespaceTests { void run() { IndexSpec spec( BSON( "a.b" << 1 ), BSONObj() ); FieldRangeSet frs( "n/a", BSONObj(), true , true ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSON( "a" << 1 ) ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSON( "a" << 1 ) ) ); } }; @@ -1074,11 +1075,11 @@ namespace NamespaceTests { void run() { IndexSpec spec( BSON( "1" << 1 ), BSONObj() ); FieldRangeSet frs1( "n/a", BSON( "1" << 2 ), true , true ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frs1, BSONObj() ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs1, BSONObj() ) ); FieldRangeSet frs2( "n/a", BSON( "01" << 3), true , true ); - ASSERT_EQUALS( USELESS, spec.suitability( frs2, BSON( "01" << 1 ) ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs2, BSON( "01" << 1 ) ) ); FieldRangeSet frs3( "n/a", BSONObj() , true , true ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frs3, BSON( "1" << 1 ) ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs3, BSON( "1" << 1 ) ) ); } }; @@ -1091,7 +1092,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{$within:{$box:[[100,0],[120,100]]}}}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( OPTIMAL, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( OPTIMAL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1102,7 +1103,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{$within:{$box:[[100,0],[120,100]]}}}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "b" << "2d" ) ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1113,7 +1114,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{$near:[100,0]}}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( OPTIMAL, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( OPTIMAL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1126,7 +1127,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{lat:4,lon:5}}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1140,7 +1141,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{$gt:4,$lt:5}}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1151,7 +1152,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:[1,1]}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1162,7 +1163,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:1}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1173,7 +1174,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1187,7 +1188,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{$and:[{a:{$near:[100,0]}}]}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2d" ) ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1203,7 +1204,7 @@ namespace NamespaceTests { scoped_ptr<FieldRangeSetPair> frsp( org.topFrsp() ); IndexSpec spec( BSON( "a" << "2d" ) ); ASSERT_EQUALS( OPTIMAL, - spec.suitability( frsp->getSingleKeyFRS(), BSONObj() ) ); + IndexSelection::isSuitableFor(spec.keyPattern, frsp->getSingleKeyFRS(), BSONObj() ) ); } }; @@ -1219,7 +1220,7 @@ namespace NamespaceTests { "coordinates:[40,5]}}}}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( OPTIMAL, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( OPTIMAL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1231,7 +1232,7 @@ namespace NamespaceTests { "coordinates:[40,5]}}}}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "b" << "2dsphere" ) ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1242,7 +1243,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{$near:[100,0]}}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( OPTIMAL, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( OPTIMAL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1256,7 +1257,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{lat:4,lon:5}}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1267,7 +1268,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{$gt:4,$lt:5}}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1278,7 +1279,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:[1,1]}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1291,7 +1292,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:1}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1302,7 +1303,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1316,7 +1317,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{$and:[{a:{$near:[100,0]}}]}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "2dsphere" ) ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1332,7 +1333,7 @@ namespace NamespaceTests { scoped_ptr<FieldRangeSetPair> frsp( org.topFrsp() ); IndexSpec spec( BSON( "a" << "2dsphere" ) ); ASSERT_EQUALS( OPTIMAL, - spec.suitability( frsp->getSingleKeyFRS(), BSONObj() ) ); + IndexSelection::isSuitableFor(spec.keyPattern, frsp->getSingleKeyFRS(), BSONObj() ) ); } }; @@ -1347,7 +1348,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:5}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1358,7 +1359,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{$gt:4}}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( USELESS, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( USELESS, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1369,7 +1370,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{a:{$in:[1,2]}}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1383,7 +1384,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{$and:[{a:5}]}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1397,7 +1398,7 @@ namespace NamespaceTests { BSONObj query = fromjson( "{$and:[{a:5},{b:5}]}" ); FieldRangeSet frs( "n/a", query, true, true ); IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frs, BSONObj() ) ); + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frs, BSONObj() ) ); } }; @@ -1412,7 +1413,7 @@ namespace NamespaceTests { OrRangeGenerator org( "n/a", query, true ); scoped_ptr<FieldRangeSetPair> frsp( org.topFrsp() ); IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frsp->getSingleKeyFRS(), + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frsp->getSingleKeyFRS(), BSONObj() ) ); } }; @@ -1428,7 +1429,7 @@ namespace NamespaceTests { OrRangeGenerator org( "n/a", query, true ); scoped_ptr<FieldRangeSetPair> frsp( org.topFrsp() ); IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frsp->getSingleKeyFRS(), + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frsp->getSingleKeyFRS(), BSONObj() ) ); } }; @@ -1444,7 +1445,7 @@ namespace NamespaceTests { OrRangeGenerator org( "n/a", query, true ); scoped_ptr<FieldRangeSetPair> frsp( org.topFrsp() ); IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frsp->getSingleKeyFRS(), + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frsp->getSingleKeyFRS(), BSONObj() ) ); } }; @@ -1460,7 +1461,7 @@ namespace NamespaceTests { OrRangeGenerator org( "n/a", query, true ); scoped_ptr<FieldRangeSetPair> frsp( org.topFrsp() ); IndexSpec spec( BSON( "a" << "hashed" ) ); - ASSERT_EQUALS( HELPFUL, spec.suitability( frsp->getSingleKeyFRS(), + ASSERT_EQUALS( HELPFUL, IndexSelection::isSuitableFor(spec.keyPattern, frsp->getSingleKeyFRS(), BSONObj() ) ); } }; |