summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorKevin Albertson <kevin.albertson@10gen.com>2015-06-11 10:30:46 -0400
committerKevin Albertson <kevin.albertson@10gen.com>2015-07-17 12:07:32 -0400
commitc33f68518b06e6b98596a8133d097e75d8431293 (patch)
tree49757af46288b63155e554f5bcc64b026ef494f4 /src/mongo/db
parent0db35ed67ac5c22969cc674a43bf741f85cd9bdc (diff)
downloadmongo-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.cpp14
-rw-r--r--src/mongo/db/exec/geo_near.h5
-rw-r--r--src/mongo/db/index/2d_access_method.cpp2
-rw-r--r--src/mongo/db/index/SConscript16
-rw-r--r--src/mongo/db/index/expression_keys_private.cpp12
-rw-r--r--src/mongo/db/index/expression_params.cpp177
-rw-r--r--src/mongo/db/index/expression_params.h164
-rw-r--r--src/mongo/db/index/external_key_generator.cpp2
-rw-r--r--src/mongo/db/index/s2_access_method.cpp10
-rw-r--r--src/mongo/db/index/s2_access_method.h2
-rw-r--r--src/mongo/db/index/s2_indexing_params.cpp52
-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/SConscript1
-rw-r--r--src/mongo/db/query/expression_index.cpp12
-rw-r--r--src/mongo/db/query/expression_index.h6
-rw-r--r--src/mongo/db/query/index_bounds_builder.cpp6
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(), &params);
// 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());