diff options
author | Kevin Albertson <kevin.albertson@10gen.com> | 2015-06-11 10:30:46 -0400 |
---|---|---|
committer | Kevin Albertson <kevin.albertson@10gen.com> | 2015-07-17 12:07:32 -0400 |
commit | c33f68518b06e6b98596a8133d097e75d8431293 (patch) | |
tree | 49757af46288b63155e554f5bcc64b026ef494f4 /src/mongo/db | |
parent | 0db35ed67ac5c22969cc674a43bf741f85cd9bdc (diff) | |
download | mongo-c33f68518b06e6b98596a8133d097e75d8431293.tar.gz |
SERVER-19097 Add version 3 to 2dsphere index
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/exec/geo_near.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/exec/geo_near.h | 5 | ||||
-rw-r--r-- | src/mongo/db/index/2d_access_method.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/index/SConscript | 16 | ||||
-rw-r--r-- | src/mongo/db/index/expression_keys_private.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/index/expression_params.cpp | 177 | ||||
-rw-r--r-- | src/mongo/db/index/expression_params.h | 164 | ||||
-rw-r--r-- | src/mongo/db/index/external_key_generator.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/index/s2_access_method.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/index/s2_access_method.h | 2 | ||||
-rw-r--r-- | src/mongo/db/index/s2_indexing_params.cpp | 52 | ||||
-rw-r--r-- | src/mongo/db/index/s2_indexing_params.h (renamed from src/mongo/db/index/s2_common.h) | 44 | ||||
-rw-r--r-- | src/mongo/db/query/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/query/expression_index.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/query/expression_index.h | 6 | ||||
-rw-r--r-- | src/mongo/db/query/index_bounds_builder.cpp | 6 |
16 files changed, 315 insertions, 210 deletions
diff --git a/src/mongo/db/exec/geo_near.cpp b/src/mongo/db/exec/geo_near.cpp index db54e778da7..cf1de9618e9 100644 --- a/src/mongo/db/exec/geo_near.cpp +++ b/src/mongo/db/exec/geo_near.cpp @@ -810,6 +810,7 @@ GeoNear2DSphereStage::GeoNear2DSphereStage(const GeoNearParams& nearParams, _boundsIncrement(0.0) { _specificStats.keyPattern = s2Index->keyPattern(); _specificStats.indexName = s2Index->indexName(); + ExpressionParams::parse2dsphereParams(s2Index->infoObj(), &_indexParams); } GeoNear2DSphereStage::~GeoNear2DSphereStage() {} @@ -897,15 +898,14 @@ class GeoNear2DSphereStage::DensityEstimator { public: DensityEstimator(PlanStage::Children* children, const IndexDescriptor* s2Index, - const GeoNearParams* nearParams) + const GeoNearParams* nearParams, + const S2IndexingParams& indexParams) : _children(children), _s2Index(s2Index), _nearParams(nearParams), _currentLevel(0) { - S2IndexingParams params; - ExpressionParams::parse2dsphereParams(_s2Index->infoObj(), ¶ms); // Since cellId.AppendVertexNeighbors(level, output) requires level < cellId.level(), // we have to start to find documents at most S2::kMaxCellLevel - 1. Thus the finest // search area is 16 * finest cell area at S2::kMaxCellLevel, which is less than // (1.4 inch X 1.4 inch) on the earth. - _currentLevel = std::max(0, params.finestIndexedLevel - 1); + _currentLevel = std::max(0, indexParams.finestIndexedLevel - 1); } // Search for a document in neighbors at current level. @@ -1023,7 +1023,8 @@ PlanStage::StageState GeoNear2DSphereStage::initialize(OperationContext* txn, Collection* collection, WorkingSetID* out) { if (!_densityEstimator) { - _densityEstimator.reset(new DensityEstimator(&_children, _s2Index, &_nearParams)); + _densityEstimator.reset( + new DensityEstimator(&_children, _s2Index, &_nearParams, _indexParams)); } double estimatedDistance; @@ -1105,8 +1106,7 @@ StatusWith<NearStage::CoveredInterval*> // TwoDSphereKeyInRegionExpression* keyMatcher = new TwoDSphereKeyInRegionExpression(_currBounds, s2Field); - ExpressionMapping::cover2dsphere( - keyMatcher->getRegion(), _s2Index->infoObj(), coveredIntervals); + ExpressionMapping::cover2dsphere(keyMatcher->getRegion(), _indexParams, coveredIntervals); // IndexScan owns the hash matcher IndexScan* scan = new IndexScanWithMatch(txn, scanParams, workingSet, keyMatcher); diff --git a/src/mongo/db/exec/geo_near.h b/src/mongo/db/exec/geo_near.h index 06b25ad27b7..b898ee51280 100644 --- a/src/mongo/db/exec/geo_near.h +++ b/src/mongo/db/exec/geo_near.h @@ -32,8 +32,9 @@ #include "mongo/db/exec/near.h" #include "mongo/db/exec/working_set.h" #include "mongo/db/exec/plan_stats.h" -#include "mongo/db/index/index_descriptor.h" #include "mongo/db/geo/geometry_container.h" +#include "mongo/db/index/index_descriptor.h" +#include "mongo/db/index/s2_indexing_params.h" #include "mongo/db/matcher/expression.h" #include "mongo/db/matcher/expression_geo.h" #include "mongo/db/query/index_bounds.h" @@ -133,6 +134,8 @@ private: // Not owned here IndexDescriptor* const _s2Index; + S2IndexingParams _indexParams; + // The total search annulus const R2Annulus _fullBounds; diff --git a/src/mongo/db/index/2d_access_method.cpp b/src/mongo/db/index/2d_access_method.cpp index 531df3369b1..bc5bad9283b 100644 --- a/src/mongo/db/index/2d_access_method.cpp +++ b/src/mongo/db/index/2d_access_method.cpp @@ -52,7 +52,7 @@ void TwoDAccessMethod::getKeys(const BSONObj& obj, BSONObjSet* keys) const { } /** Finds all locations in a geo-indexed object */ -void TwoDAccessMethod::getKeys(const BSONObj& obj, vector<BSONObj>& locs) const { +void TwoDAccessMethod::getKeys(const BSONObj& obj, std::vector<BSONObj>& locs) const { ExpressionKeysPrivate::get2DKeys(obj, _params, NULL, &locs); } diff --git a/src/mongo/db/index/SConscript b/src/mongo/db/index/SConscript index 188a070b067..ecafab77727 100644 --- a/src/mongo/db/index/SConscript +++ b/src/mongo/db/index/SConscript @@ -18,9 +18,9 @@ env.Library( 'expression_keys_private.cpp', ], LIBDEPS=[ + 'expression_params', '$BUILD_DIR/mongo/bson/bson', '$BUILD_DIR/mongo/db/fts/base', - '$BUILD_DIR/mongo/db/geo/geometry', '$BUILD_DIR/mongo/db/geo/geoparser', '$BUILD_DIR/mongo/db/index_names', '$BUILD_DIR/mongo/db/mongohasher', @@ -39,6 +39,20 @@ env.Library( ], ) +env.Library( + target='expression_params', + source=[ + 'expression_params.cpp', + 's2_indexing_params.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/bson/bson', + '$BUILD_DIR/mongo/bson/util/bson_extract', + '$BUILD_DIR/mongo/db/mongohasher', + '$BUILD_DIR/third_party/s2/s2', + ] +) + env.CppUnitTest( target='btree_key_generator_test', source=[ diff --git a/src/mongo/db/index/expression_keys_private.cpp b/src/mongo/db/index/expression_keys_private.cpp index 559175444bf..7e549a8e2bc 100644 --- a/src/mongo/db/index/expression_keys_private.cpp +++ b/src/mongo/db/index/expression_keys_private.cpp @@ -36,10 +36,10 @@ #include "mongo/db/geo/geoconstants.h" #include "mongo/db/geo/geometry_container.h" #include "mongo/db/geo/geoparser.h" -#include "mongo/db/index/s2_common.h" #include "mongo/db/geo/s2.h" #include "mongo/db/index_names.h" #include "mongo/db/index/2d_common.h" +#include "mongo/db/index/s2_indexing_params.h" #include "mongo/util/assert_util.h" #include "mongo/util/log.h" #include "mongo/util/mongoutils/str.h" @@ -445,8 +445,8 @@ void ExpressionKeysPrivate::getS2Keys(const BSONObj& obj, BSONObjSet keysForThisField; if (IndexNames::GEO_2DSPHERE == e.valuestr()) { - if (S2_INDEX_VERSION_2 == params.indexVersion) { - // For V2, + if (params.indexVersion >= S2_INDEX_VERSION_2) { + // For >= V2, // geo: null, // geo: undefined // geo: [] @@ -466,7 +466,7 @@ void ExpressionKeysPrivate::getS2Keys(const BSONObj& obj, } } - // V2 2dsphere indices require that at least one geo field to be present in a + // >= V2 2dsphere indices require that at least one geo field to be present in a // document in order to index it. if (fieldElements.size() > 0) { haveGeoField = true; @@ -504,8 +504,8 @@ void ExpressionKeysPrivate::getS2Keys(const BSONObj& obj, keysToAdd = updatedKeysToAdd; } - // Make sure that if we're V2 there's at least one geo field present in the doc. - if (S2_INDEX_VERSION_2 == params.indexVersion) { + // Make sure that if we're >= V2 there's at least one geo field present in the doc. + if (params.indexVersion >= S2_INDEX_VERSION_2) { if (!haveGeoField) { return; } diff --git a/src/mongo/db/index/expression_params.cpp b/src/mongo/db/index/expression_params.cpp new file mode 100644 index 00000000000..778f5c03010 --- /dev/null +++ b/src/mongo/db/index/expression_params.cpp @@ -0,0 +1,177 @@ +/** + * Copyright (C) 2015 MongoDB 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/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/db/index/expression_params.h" + +#include "mongo/bson/util/bson_extract.h" +#include "mongo/db/geo/geoconstants.h" +#include "mongo/db/hasher.h" +#include "mongo/db/index_names.h" +#include "mongo/db/index/2d_common.h" +#include "mongo/db/index/s2_indexing_params.h" +#include "mongo/util/mongoutils/str.h" + +namespace mongo { + +using mongoutils::str::stream; + +void ExpressionParams::parseTwoDParams(const BSONObj& infoObj, TwoDIndexingParams* out) { + BSONObjIterator i(infoObj.getObjectField("key")); + + while (i.more()) { + BSONElement e = i.next(); + if (e.type() == String && IndexNames::GEO_2D == e.valuestr()) { + uassert(16800, "can't have 2 geo fields", out->geo.size() == 0); + uassert(16801, "2d has to be first in index", out->other.size() == 0); + out->geo = e.fieldName(); + } else { + int order = 1; + if (e.isNumber()) { + order = static_cast<int>(e.Number()); + } + out->other.push_back(std::make_pair(e.fieldName(), order)); + } + } + + uassert(16802, "no geo field specified", out->geo.size()); + + GeoHashConverter::Parameters hashParams; + Status paramStatus = GeoHashConverter::parseParameters(infoObj, &hashParams); + uassertStatusOK(paramStatus); + + out->geoHashConverter.reset(new GeoHashConverter(hashParams)); +} + +void ExpressionParams::parseHashParams(const BSONObj& infoObj, + HashSeed* seedOut, + int* versionOut, + std::string* fieldOut) { + // Default _seed to DEFAULT_HASH_SEED if "seed" is not included in the index spec + // or if the value of "seed" is not a number + + // *** WARNING *** + // Choosing non-default seeds will invalidate hashed sharding + // Changing the seed default will break existing indexes and sharded collections + if (infoObj["seed"].eoo()) { + *seedOut = BSONElementHasher::DEFAULT_HASH_SEED; + } else { + *seedOut = infoObj["seed"].numberInt(); + } + + // In case we have hashed indexes based on other hash functions in the future, we store + // a hashVersion number. If hashVersion changes, "makeSingleHashKey" will need to change + // accordingly. Defaults to 0 if "hashVersion" is not included in the index spec or if + // the value of "hashversion" is not a number + *versionOut = infoObj["hashVersion"].numberInt(); + + // Get the hashfield name + BSONElement firstElt = infoObj.getObjectField("key").firstElement(); + massert(16765, "error: no hashed index field", firstElt.str().compare(IndexNames::HASHED) == 0); + *fieldOut = firstElt.fieldName(); +} + +void ExpressionParams::parseHaystackParams(const BSONObj& infoObj, + std::string* geoFieldOut, + std::vector<std::string>* otherFieldsOut, + double* bucketSizeOut) { + BSONElement e = infoObj["bucketSize"]; + uassert(16777, "need bucketSize", e.isNumber()); + *bucketSizeOut = e.numberDouble(); + uassert(16769, "bucketSize cannot be zero", *bucketSizeOut != 0.0); + + // Example: + // db.foo.ensureIndex({ pos : "geoHaystack", type : 1 }, { bucketSize : 1 }) + BSONObjIterator i(infoObj.getObjectField("key")); + while (i.more()) { + BSONElement e = i.next(); + if (e.type() == String && IndexNames::GEO_HAYSTACK == e.valuestr()) { + uassert(16770, "can't have more than one geo field", geoFieldOut->size() == 0); + uassert(16771, "the geo field has to be first in index", otherFieldsOut->size() == 0); + *geoFieldOut = e.fieldName(); + } else { + uassert(16772, + "geoSearch can only have 1 non-geo field for now", + otherFieldsOut->size() == 0); + otherFieldsOut->push_back(e.fieldName()); + } + } +} + +void ExpressionParams::parse2dsphereParams(const BSONObj& infoObj, S2IndexingParams* out) { + // Set up basic params. + out->maxKeysPerInsert = 200; + + // This is advisory. + out->maxCellsInCovering = 50; + + // Near distances are specified in meters...sometimes. + out->radius = kRadiusOfEarthInMeters; + + + static const std::string kIndexVersionFieldName("2dsphereIndexVersion"); + static const std::string kFinestIndexedLevel("finestIndexedLevel"); + static const std::string kCoarsestIndexedLevel("coarsestIndexedLevel"); + + long long finestIndexedLevel, coarsestIndexedLevel, indexVersion; + + bsonExtractIntegerFieldWithDefault(infoObj, + kFinestIndexedLevel, + S2::kAvgEdge.GetClosestLevel(500.0 / out->radius), + &finestIndexedLevel); + bsonExtractIntegerFieldWithDefault(infoObj, + kCoarsestIndexedLevel, + S2::kAvgEdge.GetClosestLevel(100 * 1000.0 / out->radius), + &coarsestIndexedLevel); + + // These are not advisory. + out->finestIndexedLevel = finestIndexedLevel; + out->coarsestIndexedLevel = coarsestIndexedLevel; + + // Determine which version of this index we're using. If none was set in the descriptor, + // assume S2_INDEX_VERSION_1 (alas, the first version predates the existence of the version + // field). + bsonExtractIntegerFieldWithDefault( + infoObj, kIndexVersionFieldName, S2_INDEX_VERSION_1, &indexVersion); + + out->indexVersion = static_cast<S2IndexVersion>(indexVersion); + + uassert(16747, "coarsestIndexedLevel must be >= 0", out->coarsestIndexedLevel >= 0); + uassert(16748, "finestIndexedLevel must be <= 30", out->finestIndexedLevel <= 30); + uassert(16749, + "finestIndexedLevel must be >= coarsestIndexedLevel", + out->finestIndexedLevel >= out->coarsestIndexedLevel); + + + massert(17395, + stream() << "unsupported geo index version { " << kIndexVersionFieldName << " : " + << out->indexVersion << " }, only support versions: [" << S2_INDEX_VERSION_1 + << "," << S2_INDEX_VERSION_2 << "," << S2_INDEX_VERSION_3 << "]", + out->indexVersion == S2_INDEX_VERSION_3 || out->indexVersion == S2_INDEX_VERSION_2 || + out->indexVersion == S2_INDEX_VERSION_1); +} +} // namespace mongo diff --git a/src/mongo/db/index/expression_params.h b/src/mongo/db/index/expression_params.h index a7613b027d3..598ffd388f5 100644 --- a/src/mongo/db/index/expression_params.h +++ b/src/mongo/db/index/expression_params.h @@ -26,159 +26,35 @@ * it in the license file. */ -#include "mongo/db/index/s2_common.h" -#include "mongo/db/index_names.h" -#include "mongo/db/index/2d_common.h" -#include "mongo/db/jsobj.h" -#include "mongo/util/mongoutils/str.h" - -namespace mongo { - -class ExpressionParams { -public: - static void parseTwoDParams(const BSONObj& infoObj, TwoDIndexingParams* out) { - BSONObjIterator i(infoObj.getObjectField("key")); - - while (i.more()) { - BSONElement e = i.next(); - if (e.type() == String && IndexNames::GEO_2D == e.valuestr()) { - uassert(16800, "can't have 2 geo fields", out->geo.size() == 0); - uassert(16801, "2d has to be first in index", out->other.size() == 0); - out->geo = e.fieldName(); - } else { - int order = 1; - if (e.isNumber()) { - order = static_cast<int>(e.Number()); - } - out->other.push_back(std::make_pair(e.fieldName(), order)); - } - } - - uassert(16802, "no geo field specified", out->geo.size()); - - GeoHashConverter::Parameters hashParams; - Status paramStatus = GeoHashConverter::parseParameters(infoObj, &hashParams); - uassertStatusOK(paramStatus); - - out->geoHashConverter.reset(new GeoHashConverter(hashParams)); - } - - static void parseHashParams(const BSONObj& infoObj, - HashSeed* seedOut, - int* versionOut, - std::string* fieldOut) { - // Default _seed to DEFAULT_HASH_SEED if "seed" is not included in the index spec - // or if the value of "seed" is not a number +#pragma once - // *** WARNING *** - // Choosing non-default seeds will invalidate hashed sharding - // Changing the seed default will break existing indexes and sharded collections - if (infoObj["seed"].eoo()) { - *seedOut = BSONElementHasher::DEFAULT_HASH_SEED; - } else { - *seedOut = infoObj["seed"].numberInt(); - } +#include <string> +#include <vector> - // In case we have hashed indexes based on other hash functions in the future, we store - // a hashVersion number. If hashVersion changes, "makeSingleHashKey" will need to change - // accordingly. Defaults to 0 if "hashVersion" is not included in the index spec or if - // the value of "hashversion" is not a number - *versionOut = infoObj["hashVersion"].numberInt(); - - // Get the hashfield name - BSONElement firstElt = infoObj.getObjectField("key").firstElement(); - massert( - 16765, "error: no hashed index field", firstElt.str().compare(IndexNames::HASHED) == 0); - *fieldOut = firstElt.fieldName(); - } - - static void parseHaystackParams(const BSONObj& infoObj, - std::string* geoFieldOut, - std::vector<std::string>* otherFieldsOut, - double* bucketSizeOut) { - BSONElement e = infoObj["bucketSize"]; - uassert(16777, "need bucketSize", e.isNumber()); - *bucketSizeOut = e.numberDouble(); - uassert(16769, "bucketSize cannot be zero", *bucketSizeOut != 0.0); - - // Example: - // db.foo.ensureIndex({ pos : "geoHaystack", type : 1 }, { bucketSize : 1 }) - BSONObjIterator i(infoObj.getObjectField("key")); - while (i.more()) { - BSONElement e = i.next(); - if (e.type() == String && IndexNames::GEO_HAYSTACK == e.valuestr()) { - uassert(16770, "can't have more than one geo field", geoFieldOut->size() == 0); - uassert( - 16771, "the geo field has to be first in index", otherFieldsOut->size() == 0); - *geoFieldOut = e.fieldName(); - } else { - uassert(16772, - "geoSearch can only have 1 non-geo field for now", - otherFieldsOut->size() == 0); - otherFieldsOut->push_back(e.fieldName()); - } - } - } - - static void parse2dsphereParams(const BSONObj& infoObj, S2IndexingParams* out) { - // Set up basic params. - out->maxKeysPerInsert = 200; - - // This is advisory. - out->maxCellsInCovering = 50; - - // Near distances are specified in meters...sometimes. - out->radius = kRadiusOfEarthInMeters; +#include "mongo/db/jsobj.h" +#include "mongo/db/hasher.h" - // These are not advisory. - out->finestIndexedLevel = configValueWithDefaultInt( - infoObj, "finestIndexedLevel", S2::kAvgEdge.GetClosestLevel(500.0 / out->radius)); +namespace mongo { - out->coarsestIndexedLevel = - configValueWithDefaultInt(infoObj, - "coarsestIndexedLevel", - S2::kAvgEdge.GetClosestLevel(100 * 1000.0 / out->radius)); +struct TwoDIndexingParams; +struct S2IndexingParams; - static const std::string kIndexVersionFieldName("2dsphereIndexVersion"); +namespace ExpressionParams { - // Determine which version of this index we're using. If none was set in the descriptor, - // assume S2_INDEX_VERSION_1 (alas, the first version predates the existence of the version - // field). - out->indexVersion = static_cast<S2IndexVersion>( - configValueWithDefaultInt(infoObj, kIndexVersionFieldName, S2_INDEX_VERSION_1)); +void parseTwoDParams(const BSONObj& infoObj, TwoDIndexingParams* out); - uassert(16747, "coarsestIndexedLevel must be >= 0", out->coarsestIndexedLevel >= 0); - uassert(16748, "finestIndexedLevel must be <= 30", out->finestIndexedLevel <= 30); - uassert(16749, - "finestIndexedLevel must be >= coarsestIndexedLevel", - out->finestIndexedLevel >= out->coarsestIndexedLevel); +void parseHashParams(const BSONObj& infoObj, + HashSeed* seedOut, + int* versionOut, + std::string* fieldOut); - massert(17395, - mongoutils::str::stream() << "unsupported geo index version { " - << kIndexVersionFieldName << " : " << out->indexVersion - << " }, only support versions: [" << S2_INDEX_VERSION_1 - << "," << S2_INDEX_VERSION_2 << "]", - out->indexVersion == S2_INDEX_VERSION_2 || out->indexVersion == S2_INDEX_VERSION_1); - } +void parseHaystackParams(const BSONObj& infoObj, + std::string* geoFieldOut, + std::vector<std::string>* otherFieldsOut, + double* bucketSizeOut); -private: - static double configValueWithDefaultDouble(const BSONObj& infoObj, - const std::string& name, - double def) { - BSONElement e = infoObj[name]; - if (e.isNumber()) { - return e.numberDouble(); - } - return def; - } +void parse2dsphereParams(const BSONObj& infoObj, S2IndexingParams* out); - static int configValueWithDefaultInt(const BSONObj& infoObj, const std::string& name, int def) { - BSONElement e = infoObj[name]; - if (e.isNumber()) { - return e.numberInt(); - } - return def; - } -}; +} // namespace ExpressionParams } // namespace mongo diff --git a/src/mongo/db/index/external_key_generator.cpp b/src/mongo/db/index/external_key_generator.cpp index cf2ba7d9a4b..e35c5d7f328 100644 --- a/src/mongo/db/index/external_key_generator.cpp +++ b/src/mongo/db/index/external_key_generator.cpp @@ -32,12 +32,12 @@ #include <string> #include "mongo/db/fts/fts_spec.h" -#include "mongo/db/index/s2_common.h" #include "mongo/db/index_names.h" #include "mongo/db/index/2d_common.h" #include "mongo/db/index/btree_key_generator.h" #include "mongo/db/index/expression_keys_private.h" #include "mongo/db/index/expression_params.h" +#include "mongo/db/index/s2_indexing_params.h" #include "mongo/db/jsobj.h" namespace mongo { diff --git a/src/mongo/db/index/s2_access_method.cpp b/src/mongo/db/index/s2_access_method.cpp index 3fcf4d013a1..15079d17620 100644 --- a/src/mongo/db/index/s2_access_method.cpp +++ b/src/mongo/db/index/s2_access_method.cpp @@ -35,7 +35,6 @@ #include "mongo/base/status.h" #include "mongo/db/geo/geoparser.h" #include "mongo/db/geo/geoconstants.h" -#include "mongo/db/index/s2_common.h" #include "mongo/db/index_names.h" #include "mongo/db/index/expression_keys_private.h" #include "mongo/db/index/expression_params.h" @@ -82,13 +81,13 @@ S2AccessMethod::S2AccessMethod(IndexCatalogEntry* btreeState, SortedDataInterfac // static BSONObj S2AccessMethod::fixSpec(const BSONObj& specObj) { // If the spec object has the field "2dsphereIndexVersion", validate it. If it doesn't, add - // {2dsphereIndexVersion: 2}, which is the default for newly-built indexes. + // {2dsphereIndexVersion: 3}, which is the default for newly-built indexes. BSONElement indexVersionElt = specObj[kIndexVersionFieldName]; if (indexVersionElt.eoo()) { BSONObjBuilder bob; bob.appendElements(specObj); - bob.append(kIndexVersionFieldName, S2_INDEX_VERSION_2); + bob.append(kIndexVersionFieldName, S2_INDEX_VERSION_3); return bob.obj(); } @@ -96,9 +95,10 @@ BSONObj S2AccessMethod::fixSpec(const BSONObj& specObj) { uassert(17394, str::stream() << "unsupported geo index version { " << kIndexVersionFieldName << " : " << indexVersionElt << " }, only support versions: [" << S2_INDEX_VERSION_1 - << "," << S2_INDEX_VERSION_2 << "]", + << "," << S2_INDEX_VERSION_2 << "," << S2_INDEX_VERSION_3 << "]", indexVersionElt.isNumber() && - (indexVersion == S2_INDEX_VERSION_2 || indexVersion == S2_INDEX_VERSION_1)); + (indexVersion == S2_INDEX_VERSION_3 || indexVersion == S2_INDEX_VERSION_2 || + indexVersion == S2_INDEX_VERSION_1)); return specObj; } diff --git a/src/mongo/db/index/s2_access_method.h b/src/mongo/db/index/s2_access_method.h index 15e0c3a773e..febb7c19b16 100644 --- a/src/mongo/db/index/s2_access_method.h +++ b/src/mongo/db/index/s2_access_method.h @@ -29,9 +29,9 @@ #pragma once #include "mongo/base/status.h" -#include "mongo/db/index/s2_common.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/index/index_descriptor.h" +#include "mongo/db/index/s2_indexing_params.h" #include "mongo/db/jsobj.h" namespace mongo { diff --git a/src/mongo/db/index/s2_indexing_params.cpp b/src/mongo/db/index/s2_indexing_params.cpp new file mode 100644 index 00000000000..8217f16fa7e --- /dev/null +++ b/src/mongo/db/index/s2_indexing_params.cpp @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2015 MongoDB 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/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/db/index/s2_indexing_params.h" + +#include "third_party/s2/s2regioncoverer.h" + +namespace mongo { + +std::string S2IndexingParams::toString() const { + std::stringstream ss; + ss << "maxKeysPerInsert: " << maxKeysPerInsert << std::endl; + ss << "maxCellsInCovering: " << maxCellsInCovering << std::endl; + ss << "finestIndexedLevel: " << finestIndexedLevel << std::endl; + ss << "coarsestIndexedLevel: " << coarsestIndexedLevel << std::endl; + ss << "indexVersion: " << indexVersion << std::endl; + return ss.str(); +} + +void S2IndexingParams::configureCoverer(S2RegionCoverer* coverer) const { + coverer->set_min_level(coarsestIndexedLevel); + coverer->set_max_level(finestIndexedLevel); + + // This is advisory; the two above are strict. + coverer->set_max_cells(maxCellsInCovering); +} +} // namespace mongo diff --git a/src/mongo/db/index/s2_common.h b/src/mongo/db/index/s2_indexing_params.h index fad4087bb89..dc5123c1423 100644 --- a/src/mongo/db/index/s2_common.h +++ b/src/mongo/db/index/s2_indexing_params.h @@ -26,15 +26,11 @@ * it in the license file. */ -#include "mongo/db/jsobj.h" -#include "mongo/db/geo/geoconstants.h" +#pragma once + #include "mongo/db/geo/s2.h" -#include "third_party/s2/s2cell.h" -#include "third_party/s2/s2polyline.h" -#include "third_party/s2/s2polygon.h" -#include "third_party/s2/s2regioncoverer.h" -#pragma once +class S2RegionCoverer; namespace mongo { @@ -44,10 +40,14 @@ enum S2IndexVersion { // 2.4.0 and later. Supports the following GeoJSON objects: Point, LineString, Polygon. S2_INDEX_VERSION_1 = 1, - // The current version of the S2 index, introduced in MongoDB 2.6.0. Compatible with + // The second version of the S2 index, introduced in MongoDB 2.6.0. Compatible with // MongoDB 2.6.0 and later. Introduced support for the following GeoJSON objects: // MultiPoint, MultiLineString, MultiPolygon, GeometryCollection. - S2_INDEX_VERSION_2 = 2 + S2_INDEX_VERSION_2 = 2, + + // The third version of the S2 index, introduced in MongoDB 3.2.0. Introduced + // performance improvements and changed the key type from string to numeric + S2_INDEX_VERSION_3 = 3 }; struct S2IndexingParams { @@ -57,33 +57,19 @@ struct S2IndexingParams { // This is really an advisory parameter that we pass to the cover generator. The // finest/coarsest index level determine the required # of cells. int maxCellsInCovering; - // What's the finest grained level that we'll index? When we query for a point - // we start at that -- we index nothing finer than this. + // What's the finest grained level that we'll index? int finestIndexedLevel; - // And, what's the coarsest? When we search in larger coverings we know we - // can stop here -- we index nothing coarser than this. + // And, what's the coarsest? When we search in larger coverings + // we know we can stop here -- we index nothing coarser than this. int coarsestIndexedLevel; // Version of this index (specific to the index type). S2IndexVersion indexVersion; - + // Radius of the earth in meters double radius; - std::string toString() const { - std::stringstream ss; - ss << "maxKeysPerInsert: " << maxKeysPerInsert << std::endl; - ss << "maxCellsInCovering: " << maxCellsInCovering << std::endl; - ss << "finestIndexedLevel: " << finestIndexedLevel << std::endl; - ss << "coarsestIndexedLevel: " << coarsestIndexedLevel << std::endl; - ss << "indexVersion: " << indexVersion << std::endl; - return ss.str(); - } + std::string toString() const; - void configureCoverer(S2RegionCoverer* coverer) const { - coverer->set_min_level(coarsestIndexedLevel); - coverer->set_max_level(finestIndexedLevel); - // This is advisory; the two above are strict. - coverer->set_max_cells(maxCellsInCovering); - } + void configureCoverer(S2RegionCoverer* coverer) const; }; } // namespace mongo diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript index d6a11866243..4d5d65bee31 100644 --- a/src/mongo/db/query/SConscript +++ b/src/mongo/db/query/SConscript @@ -83,6 +83,7 @@ env.Library( "$BUILD_DIR/mongo/bson/bson", "$BUILD_DIR/mongo/db/matcher/expressions_geo", "$BUILD_DIR/mongo/db/index_names", + "$BUILD_DIR/mongo/db/index/expression_params", "$BUILD_DIR/mongo/db/mongohasher", "$BUILD_DIR/mongo/db/server_parameters", ], diff --git a/src/mongo/db/query/expression_index.cpp b/src/mongo/db/query/expression_index.cpp index 67f06314266..7f86c374a90 100644 --- a/src/mongo/db/query/expression_index.cpp +++ b/src/mongo/db/query/expression_index.cpp @@ -92,18 +92,10 @@ void ExpressionMapping::cover2d(const R2Region& region, } } -// TODO: what should we really pass in for indexInfoObj? void ExpressionMapping::cover2dsphere(const S2Region& region, - const BSONObj& indexInfoObj, + const S2IndexingParams& indexingParams, OrderedIntervalList* oilOut) { - int coarsestIndexedLevel; - BSONElement ce = indexInfoObj["coarsestIndexedLevel"]; - if (ce.isNumber()) { - coarsestIndexedLevel = ce.numberInt(); - } else { - coarsestIndexedLevel = S2::kAvgEdge.GetClosestLevel(100 * 1000.0 / kRadiusOfEarthInMeters); - } - + int coarsestIndexedLevel = indexingParams.coarsestIndexedLevel; // The min level of our covering is the level whose cells are the closest match to the // *area* of the region (or the max indexed level, whichever is smaller) The max level // is 4 sizes larger. diff --git a/src/mongo/db/query/expression_index.h b/src/mongo/db/query/expression_index.h index 910433924ff..c65d57c1322 100644 --- a/src/mongo/db/query/expression_index.h +++ b/src/mongo/db/query/expression_index.h @@ -30,8 +30,9 @@ #include "third_party/s2/s2region.h" -#include "mongo/db/jsobj.h" #include "mongo/db/geo/shapes.h" +#include "mongo/db/index/s2_indexing_params.h" +#include "mongo/db/jsobj.h" #include "mongo/db/query/index_bounds_builder.h" // For OrderedIntervalList namespace mongo { @@ -50,9 +51,8 @@ public: int maxCoveringCells, OrderedIntervalList* oil); - // TODO: what should we really pass in for indexInfoObj? static void cover2dsphere(const S2Region& region, - const BSONObj& indexInfoObj, + const S2IndexingParams& indexParams, OrderedIntervalList* oilOut); }; diff --git a/src/mongo/db/query/index_bounds_builder.cpp b/src/mongo/db/query/index_bounds_builder.cpp index e32c19eec4d..d515dfd3012 100644 --- a/src/mongo/db/query/index_bounds_builder.cpp +++ b/src/mongo/db/query/index_bounds_builder.cpp @@ -35,6 +35,8 @@ #include "mongo/base/string_data.h" #include "mongo/db/geo/geoconstants.h" +#include "mongo/db/index/expression_params.h" +#include "mongo/db/index/s2_indexing_params.h" #include "mongo/db/matcher/expression_geo.h" #include "mongo/db/query/expression_index.h" #include "mongo/db/query/expression_index_knobs.h" @@ -557,7 +559,9 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr, if (mongoutils::str::equals("2dsphere", elt.valuestrsafe())) { verify(gme->getGeoExpression().getGeometry().hasS2Region()); const S2Region& region = gme->getGeoExpression().getGeometry().getS2Region(); - ExpressionMapping::cover2dsphere(region, index.infoObj, oilOut); + S2IndexingParams indexParams; + ExpressionParams::parse2dsphereParams(index.infoObj, &indexParams); + ExpressionMapping::cover2dsphere(region, indexParams, oilOut); *tightnessOut = IndexBoundsBuilder::INEXACT_FETCH; } else if (mongoutils::str::equals("2d", elt.valuestrsafe())) { verify(gme->getGeoExpression().getGeometry().hasR2Region()); |