diff options
author | J. Rassi <rassi@10gen.com> | 2016-06-22 16:26:09 -0400 |
---|---|---|
committer | J. Rassi <rassi@10gen.com> | 2016-06-27 16:04:32 -0400 |
commit | cd9cef182e0e9046df6233380350c5009278f8aa (patch) | |
tree | 4df7783260743419214afbe34c3b69f6dfe19836 | |
parent | abf863ca0f05b7d9db4f2c92b89dcfdff45572f1 (diff) | |
download | mongo-cd9cef182e0e9046df6233380350c5009278f8aa.tar.gz |
SERVER-24703 Improved DBClientWithCommands index creation method
C++ driver SHAs cherry-picked (with modifications) into this commit:
d67f5b5
-rw-r--r-- | src/mongo/bson/bson_field.h | 2 | ||||
-rw-r--r-- | src/mongo/client/SConscript | 11 | ||||
-rw-r--r-- | src/mongo/client/dbclient.cpp | 49 | ||||
-rw-r--r-- | src/mongo/client/dbclientinterface.h | 38 | ||||
-rw-r--r-- | src/mongo/client/index_spec.cpp | 239 | ||||
-rw-r--r-- | src/mongo/client/index_spec.h | 220 | ||||
-rw-r--r-- | src/mongo/client/index_spec_test.cpp | 91 | ||||
-rw-r--r-- | src/mongo/dbtests/clienttests.cpp | 157 | ||||
-rw-r--r-- | src/mongo/dbtests/documentsourcetests.cpp | 6 | ||||
-rw-r--r-- | src/mongo/shell/bench.cpp | 2 |
10 files changed, 759 insertions, 56 deletions
diff --git a/src/mongo/bson/bson_field.h b/src/mongo/bson/bson_field.h index 16f3cff72ab..7faa144bc86 100644 --- a/src/mongo/bson/bson_field.h +++ b/src/mongo/bson/bson_field.h @@ -65,8 +65,6 @@ namespace mongo { * In a query: * conn->findOne(myColl, BSON(MyCollFields::count.gt(10))) ; * - * In a command: - * conn->ensureIndex(mycoll, BSON(MyCollFields::draining() << 1), true); */ template <typename T> diff --git a/src/mongo/client/SConscript b/src/mongo/client/SConscript index 4134ed14548..a01d60ffe87 100644 --- a/src/mongo/client/SConscript +++ b/src/mongo/client/SConscript @@ -140,6 +140,7 @@ env.Library( 'dbclient.cpp', 'dbclient_rs.cpp', 'dbclientcursor.cpp', + 'index_spec.cpp', 'global_conn_pool.cpp', 'replica_set_monitor.cpp', 'replica_set_monitor_manager.cpp', @@ -237,6 +238,16 @@ env.CppUnitTest('dbclient_rs_test', ) env.CppUnitTest( + target='index_spec_test', + source=[ + 'index_spec_test.cpp', + ], + LIBDEPS=[ + 'clientdriver', + ], +) + +env.CppUnitTest( target='scoped_db_conn_test', source=[ 'scoped_db_conn_test.cpp', diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp index 28b99a1e708..8a76a95de26 100644 --- a/src/mongo/client/dbclient.cpp +++ b/src/mongo/client/dbclient.cpp @@ -1233,42 +1233,23 @@ string DBClientWithCommands::genIndexName(const BSONObj& keys) { return ss.str(); } -void DBClientWithCommands::ensureIndex(const string& ns, - BSONObj keys, - bool unique, - const string& name, - bool background, - int version, - int ttl) { - BSONObjBuilder toSave; - toSave.append("ns", ns); - toSave.append("key", keys); - - string cacheKey(ns); - cacheKey += "--"; - - if (name != "") { - toSave.append("name", name); - cacheKey += name; - } else { - string nn = genIndexName(keys); - toSave.append("name", nn); - cacheKey += nn; +void DBClientWithCommands::createIndex(StringData ns, const IndexSpec& descriptor) { + const BSONObj descriptorObj = descriptor.toBSON(); + + BSONObjBuilder command; + command.append("createIndexes", nsToCollectionSubstring(ns)); + { + BSONArrayBuilder indexes(command.subarrayStart("indexes")); + indexes.append(descriptorObj); } + const BSONObj commandObj = command.done(); - if (version >= 0) - toSave.append("v", version); - - if (unique) - toSave.appendBool("unique", unique); - - if (background) - toSave.appendBool("background", true); - - if (ttl > 0) - toSave.append("expireAfterSeconds", ttl); - - insert(NamespaceString(ns).getSystemIndexesCollection(), toSave.obj()); + BSONObj infoObj; + if (!runCommand(nsToDatabase(ns), commandObj, infoObj)) { + Status runCommandStatus = getStatusFromCommandResult(infoObj); + invariant(!runCommandStatus.isOK()); + uassertStatusOK(runCommandStatus); + } } /* -- DBClientCursor ---------------------------------------------- */ diff --git a/src/mongo/client/dbclientinterface.h b/src/mongo/client/dbclientinterface.h index 6244db6c4ed..cc2be6c600e 100644 --- a/src/mongo/client/dbclientinterface.h +++ b/src/mongo/client/dbclientinterface.h @@ -32,6 +32,7 @@ #include "mongo/base/string_data.h" #include "mongo/client/connection_string.h" +#include "mongo/client/index_spec.h" #include "mongo/client/query.h" #include "mongo/client/read_preference.h" #include "mongo/db/jsobj.h" @@ -704,23 +705,28 @@ public: bool exists(const std::string& ns); - /** Create an index if it does not already exist. - @param ns collection to be indexed - @param keys the "key pattern" for the index. e.g., { name : 1 } - @param unique if true, indicates that key uniqueness should be enforced for this index - @param name if not specified, it will be created from the keys automatically (which is - recommended) - @param background build index in the background (see mongodb docs for details) - @param v index version. leave at default value. (unit tests set this parameter.) - @param ttl. The value of how many seconds before data should be removed from a collection. + /** Create an index on the collection 'ns' as described by the given keys. If you wish + * to specify options, see the more flexible overload of 'createIndex' which takes an + * IndexSpec object. Failure to construct the index is reported by throwing a + * UserException. + * + * @param ns Namespace on which to create the index + * @param keys Document describing keys and index types. You must provide at least one + * field and its direction. + */ + void createIndex(StringData ns, const BSONObj& keys) { + return createIndex(ns, IndexSpec().addKeys(keys)); + } + + /** Create an index on the collection 'ns' as described by the given + * descriptor. Failure to construct the index is reported by throwing a + * UserException. + * + * @param ns Namespace on which to create the index + * @param descriptor Configuration object describing the index to create. The + * descriptor must describe at least one key and index type. */ - virtual void ensureIndex(const std::string& ns, - BSONObj keys, - bool unique = false, - const std::string& name = "", - bool background = false, - int v = -1, - int ttl = 0); + virtual void createIndex(StringData ns, const IndexSpec& descriptor); virtual std::list<BSONObj> getIndexSpecs(const std::string& ns, int options = 0); diff --git a/src/mongo/client/index_spec.cpp b/src/mongo/client/index_spec.cpp new file mode 100644 index 00000000000..bf725088496 --- /dev/null +++ b/src/mongo/client/index_spec.cpp @@ -0,0 +1,239 @@ +/** + * Copyright (C) 2016 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/platform/basic.h" + +#include "mongo/client/index_spec.h" + +#include "mongo/client/dbclientinterface.h" + +namespace mongo { + +const char IndexSpec::kIndexValText[] = "text"; +const char IndexSpec::kIndexValGeo2D[] = "2d"; +const char IndexSpec::kIndexValGeoHaystack[] = "geoHaystack"; +const char IndexSpec::kIndexValGeo2DSphere[] = "2dsphere"; +const char IndexSpec::kIndexValHashed[] = "hashed"; + +namespace { + +const int kIndexTypeNumbers[] = {IndexSpec::kIndexValAscending, IndexSpec::kIndexValDescending}; + +const char* const kIndexTypeStrings[] = {NULL, + NULL, + IndexSpec::kIndexValText, + IndexSpec::kIndexValGeo2D, + IndexSpec::kIndexValGeoHaystack, + IndexSpec::kIndexValGeo2DSphere, + IndexSpec::kIndexValHashed}; + +const char kDuplicateKey[] = "duplicate key added to index descriptor"; +const char kDuplicateOption[] = "duplicate option added to index descriptor"; + +} // namespace + +IndexSpec::IndexSpec() : _dynamicName(true) {} + +IndexSpec& IndexSpec::addKey(const StringData& field, IndexType type) { + uassert(ErrorCodes::InvalidOptions, kDuplicateKey, !_keys.asTempObj().hasField(field)); + if (type <= kIndexTypeDescending) + _keys.append(field, kIndexTypeNumbers[type]); + else + _keys.append(field, kIndexTypeStrings[type]); + _rename(); + return *this; +} + +IndexSpec& IndexSpec::addKey(const BSONElement& fieldAndType) { + uassert(ErrorCodes::InvalidOptions, + kDuplicateKey, + !_keys.asTempObj().hasField(fieldAndType.fieldName())); + _keys.append(fieldAndType); + _rename(); + return *this; +} + +IndexSpec& IndexSpec::addKeys(const KeyVector& keys) { + KeyVector::const_iterator where = keys.begin(); + const KeyVector::const_iterator end = keys.end(); + for (; where != end; ++where) + addKey(where->first, where->second); + return *this; +} + +IndexSpec& IndexSpec::addKeys(const BSONObj& keys) { + BSONObjIterator iter(keys); + while (iter.more()) + addKey(iter.next()); + return *this; +} + +IndexSpec& IndexSpec::background(bool value) { + uassert( + ErrorCodes::InvalidOptions, kDuplicateOption, !_options.asTempObj().hasField("background")); + _options.append("background", value); + return *this; +} + +IndexSpec& IndexSpec::unique(bool value) { + uassert(ErrorCodes::InvalidOptions, kDuplicateOption, !_options.asTempObj().hasField("unique")); + _options.append("unique", value); + return *this; +} + +IndexSpec& IndexSpec::name(const StringData& value) { + _name = value.toString(); + _dynamicName = false; + return *this; +} + +IndexSpec& IndexSpec::dropDuplicates(bool value) { + uassert( + ErrorCodes::InvalidOptions, kDuplicateOption, !_options.asTempObj().hasField("dropDups")); + _options.append("dropDups", value); + return *this; +} + +IndexSpec& IndexSpec::sparse(bool value) { + uassert(ErrorCodes::InvalidOptions, kDuplicateOption, !_options.asTempObj().hasField("sparse")); + _options.append("sparse", value); + return *this; +} + +IndexSpec& IndexSpec::expireAfterSeconds(int value) { + uassert(ErrorCodes::InvalidOptions, + kDuplicateOption, + !_options.asTempObj().hasField("expireAfterSeconds")); + _options.append("expireAfterSeconds", value); + return *this; +} + +IndexSpec& IndexSpec::version(int value) { + uassert(ErrorCodes::InvalidOptions, kDuplicateOption, !_options.asTempObj().hasField("v")); + _options.append("v", value); + return *this; +} + +IndexSpec& IndexSpec::textWeights(const BSONObj& value) { + uassert( + ErrorCodes::InvalidOptions, kDuplicateOption, !_options.asTempObj().hasField("weights")); + _options.append("weights", value); + return *this; +} + +IndexSpec& IndexSpec::textDefaultLanguage(const StringData& value) { + uassert(ErrorCodes::InvalidOptions, + kDuplicateOption, + !_options.asTempObj().hasField("default_language")); + _options.append("default_language", value); + return *this; +} + +IndexSpec& IndexSpec::textLanguageOverride(const StringData& value) { + uassert(ErrorCodes::InvalidOptions, + kDuplicateOption, + !_options.asTempObj().hasField("language_override")); + _options.append("language_override", value); + return *this; +} + +IndexSpec& IndexSpec::textIndexVersion(int value) { + uassert(ErrorCodes::InvalidOptions, + kDuplicateOption, + !_options.asTempObj().hasField("textIndexVersion")); + _options.append("textIndexVersion", value); + return *this; +} + +IndexSpec& IndexSpec::geo2DSphereIndexVersion(int value) { + uassert(ErrorCodes::InvalidOptions, + kDuplicateOption, + !_options.asTempObj().hasField("2dsphereIndexVersion")); + _options.append("2dsphereIndexVersion", value); + return *this; +} + +IndexSpec& IndexSpec::geo2DBits(int value) { + uassert(ErrorCodes::InvalidOptions, kDuplicateOption, !_options.asTempObj().hasField("bits")); + _options.append("bits", value); + return *this; +} + +IndexSpec& IndexSpec::geo2DMin(double value) { + uassert(ErrorCodes::InvalidOptions, kDuplicateOption, !_options.asTempObj().hasField("min")); + _options.append("min", value); + return *this; +} + +IndexSpec& IndexSpec::geo2DMax(double value) { + uassert(ErrorCodes::InvalidOptions, kDuplicateOption, !_options.asTempObj().hasField("max")); + _options.append("max", value); + return *this; +} + +IndexSpec& IndexSpec::geoHaystackBucketSize(double value) { + uassert( + ErrorCodes::InvalidOptions, kDuplicateOption, !_options.asTempObj().hasField("bucketSize")); + _options.append("bucketSize", value); + return *this; +} + +IndexSpec& IndexSpec::addOption(const BSONElement& option) { + uassert(ErrorCodes::InvalidOptions, + kDuplicateOption, + !_options.asTempObj().hasField(option.fieldName())); + _options.append(option); + return *this; +} + +IndexSpec& IndexSpec::addOptions(const BSONObj& options) { + BSONObjIterator iter(options); + while (iter.more()) + addOption(iter.next()); + return *this; +} + +std::string IndexSpec::name() const { + return _name; +} + +BSONObj IndexSpec::toBSON() const { + BSONObjBuilder bob; + bob.append("name", name()); + bob.append("key", _keys.asTempObj()); + bob.appendElements(_options.asTempObj()); + return bob.obj(); +} + +void IndexSpec::_rename() { + if (!_dynamicName) + return; + _name = DBClientWithCommands::genIndexName(_keys.asTempObj()); +} + +} // namespace mongo diff --git a/src/mongo/client/index_spec.h b/src/mongo/client/index_spec.h new file mode 100644 index 00000000000..17154acdfef --- /dev/null +++ b/src/mongo/client/index_spec.h @@ -0,0 +1,220 @@ +/** + * Copyright (C) 2016 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. + */ + +#pragma once + +#include <string> +#include <utility> +#include <vector> + +#include "mongo/db/jsobj.h" + +namespace mongo { + +class StringData; + +class IndexSpec { +public: + // An enumeration of symbolic names for index types. + enum IndexType { + kIndexTypeAscending, + kIndexTypeDescending, + kIndexTypeText, + kIndexTypeGeo2D, + kIndexTypeGeoHaystack, + kIndexTypeGeo2DSphere, + kIndexTypeHashed, + }; + + // The values to be encoded in BSON for each type of index. + static const int kIndexValAscending = 1; + static const int kIndexValDescending = -1; + static const char kIndexValText[]; + static const char kIndexValGeo2D[]; + static const char kIndexValGeoHaystack[]; + static const char kIndexValGeo2DSphere[]; + static const char kIndexValHashed[]; + + /** Create a new IndexSpec. */ + IndexSpec(); + + // + // Methods for adding keys. Methods on this class will prevent you from adding a given + // key multiple times. Constraints on the validity of compound indexes are not enforced + // here. + // + + /** Add a new component, by default ascending, field to index. */ + IndexSpec& addKey(const StringData& field, IndexType type = kIndexTypeAscending); + + /** Add a component to this index. The field name of the element is used as the field + * name to index. The value of the element is the index type. This method exists to + * accomodate building future index types for which the enumeration value has not yet + * been extended. + */ + IndexSpec& addKey(const BSONElement& fieldAndType); + + /** Add all components in the provided key vector to the index descriptor. */ + typedef std::vector<std::pair<std::string, IndexType>> KeyVector; + IndexSpec& addKeys(const KeyVector& keys); + + /** Add all keys from the provided object to the index descriptor. */ + IndexSpec& addKeys(const BSONObj& keys); + + + // + // Methods for adding options. As for keys, duplicated settings are checked and will + // raise an error. + // + + // + // Common index options + // + + /** Controls whether this index should be built in the foreground or background. By + * default indexes are built in the foreground. + */ + IndexSpec& background(bool value = true); + + /** Set whether or not this index should enforce uniqueness. By default, uniqueness is + * not enforced. + */ + IndexSpec& unique(bool value = true); + + + /** Set the name for this index. If not set, a name will be automatically generated. */ + IndexSpec& name(const StringData& name); + + /** Sets whether duplicates detected while indexing should be dropped. By default, + * duplicates are not dropped. + */ + IndexSpec& dropDuplicates(bool value = true); + + /** Sets whether or not this index should be sparse. By default, indexes are not + * sparse. + */ + IndexSpec& sparse(bool value = true); + + /** Enables time-to-live semantics for this index with the specified lifetime in + * seconds. Note that the indexed field must be of type UTC datetime for this option + * to work correctly. + */ + IndexSpec& expireAfterSeconds(int value); + + /** Explicitly request an index of the given version. As of MongoDB 2.6, the only accepted + * values are 0 or 1. Versions 2.0 and later default to '1'. Do not set this option except + * in unusual circumstances. + */ + IndexSpec& version(int value); + + + // + // Text options + // + + /** Sets the 'weights' document for a text index. */ + IndexSpec& textWeights(const BSONObj& value); + + /** Sets the default language for a text index. */ + IndexSpec& textDefaultLanguage(const StringData& value); + + /** Sets the name of the field containing the language override in a text index. */ + IndexSpec& textLanguageOverride(const StringData& value); + + /** Sets the version of the text index to use. MongoDB 2.4 only supports version + * '1'. If not otherwise specified, MongoDB 2.6 defaults to version 2. + */ + IndexSpec& textIndexVersion(int value); + + + // + // 2D Sphere Options + // + + /** Sets the version of the 2D sphere index to use. MongoDB 2.4 only supports version + * '1'. If not otherwise specified, MongoDB 2.6 defaults to version 2. + */ + IndexSpec& geo2DSphereIndexVersion(int value); + + + // + // Geo2D Options + // + + /** Sets the number of bits of precision for geohash. */ + IndexSpec& geo2DBits(int value); + + /** Sets the minimum value for keys in a geo2d index. */ + IndexSpec& geo2DMin(double value); + + /** Sets the maximum value for keys in a geo2d index. */ + IndexSpec& geo2DMax(double value); + + + // + // Geo Haystack Options + // + + /** Sets the bucket size for haystack indexes. */ + IndexSpec& geoHaystackBucketSize(double value); + + + // + // Support for adding generic options. This is here so that if new index options + // become available on the server those options can be set independently of the + // named methods above. + // + + /** Adds another option verbatim. */ + IndexSpec& addOption(const BSONElement& option); + + /** Adds the provided options verbatim. */ + IndexSpec& addOptions(const BSONObj& options); + + /** Get a copy of the current name for this index. If a name was provided to the + * constructor, a copy of this name is returned. Otherwise, the current auto-generated + * name given the set of indexes will be returned. Note that this is a copy: + * subsequent changes to the indexed fields will not affect the value returned here, + * and you must call 'name' again to obtain the updated value. + */ + std::string name() const; + + /** Return a BSONObj that captures the selected index keys and options. */ + BSONObj toBSON() const; + +private: + void _rename(); + + std::string _name; + bool _dynamicName; + + mutable BSONObjBuilder _keys; + mutable BSONObjBuilder _options; +}; + +} // namespace mongo diff --git a/src/mongo/client/index_spec_test.cpp b/src/mongo/client/index_spec_test.cpp new file mode 100644 index 00000000000..65821df91e5 --- /dev/null +++ b/src/mongo/client/index_spec_test.cpp @@ -0,0 +1,91 @@ +/** + * Copyright (C) 2016 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/platform/basic.h" + +#include "mongo/client/index_spec.h" + +#include "mongo/unittest/unittest.h" + +#define ASSERT_UASSERTS(STATEMENT) ASSERT_THROWS(STATEMENT, UserException) + +namespace mongo { + +TEST(Options, RepeatedOptionsFail) { + ASSERT_UASSERTS(IndexSpec().background().background()); + ASSERT_UASSERTS(IndexSpec().unique().unique()); + ASSERT_UASSERTS(IndexSpec().dropDuplicates().dropDuplicates()); + ASSERT_UASSERTS(IndexSpec().sparse().sparse()); + ASSERT_UASSERTS(IndexSpec().expireAfterSeconds(1).expireAfterSeconds(1)); + ASSERT_UASSERTS(IndexSpec().version(0).version(0)); + ASSERT_UASSERTS(IndexSpec().textWeights(BSONObj()).textWeights(BSONObj())); + ASSERT_UASSERTS(IndexSpec().textDefaultLanguage("foo").textDefaultLanguage("foo")); + ASSERT_UASSERTS(IndexSpec().textLanguageOverride("foo").textLanguageOverride("foo")); + ASSERT_UASSERTS(IndexSpec().textIndexVersion(0).textIndexVersion(0)); + ASSERT_UASSERTS(IndexSpec().geo2DSphereIndexVersion(0).geo2DSphereIndexVersion(0)); + ASSERT_UASSERTS(IndexSpec().geo2DBits(0).geo2DBits(0)); + ASSERT_UASSERTS(IndexSpec().geo2DMin(2.00).geo2DMin(2.00)); + ASSERT_UASSERTS(IndexSpec().geo2DMax(2.00).geo2DMax(2.00)); + ASSERT_UASSERTS(IndexSpec().geoHaystackBucketSize(2.0).geoHaystackBucketSize(2.0)); + ASSERT_UASSERTS(IndexSpec().addOptions(BSON("foo" << 1 << "foo" << 1))); + ASSERT_UASSERTS(IndexSpec().sparse(0).addOptions(BSON("sparse" << 1))); +} + +TEST(Options, RepeatedKeysFail) { + + IndexSpec spec; + spec.addKey("aField"); + + ASSERT_UASSERTS(spec.addKey("aField")); + + const BSONObj fields = BSON("someField" << 1 << "aField" << 1 << "anotherField" << 1); + ASSERT_UASSERTS(spec.addKey(fields.getField("aField"))); + ASSERT_UASSERTS(spec.addKeys(fields)); +} + +TEST(Options, NameIsHonored) { + IndexSpec spec; + spec.addKey("aField"); + + // Should get an auto generated name + ASSERT_FALSE(spec.name().empty()); + + // That is not the name we are about to set. + ASSERT_NE("someName", spec.name()); + + spec.name("someName"); + + // Should get the name we specified. + ASSERT_EQ("someName", spec.name()); + + // Name can be changed as many times as we want + spec.name("yetAnotherName"); + ASSERT_EQ("yetAnotherName", spec.name()); +} + +} // namespace mongo diff --git a/src/mongo/dbtests/clienttests.cpp b/src/mongo/dbtests/clienttests.cpp index 7764f5697c1..9cb2d0b70c1 100644 --- a/src/mongo/dbtests/clienttests.cpp +++ b/src/mongo/dbtests/clienttests.cpp @@ -233,6 +233,153 @@ public: } }; +class CreateSimpleV1Index : public Base { +public: + CreateSimpleV1Index() : Base("CreateSimpleV1Index") {} + void run() { + const ServiceContext::UniqueOperationContext txnPtr = cc().makeOperationContext(); + OperationContext& txn = *txnPtr; + DBDirectClient db(&txn); + + db.createIndex(ns(), IndexSpec().addKey("aField").version(1)); + } +}; + +class CreateSimpleNamedV1Index : public Base { +public: + CreateSimpleNamedV1Index() : Base("CreateSimpleNamedV1Index") {} + void run() { + const ServiceContext::UniqueOperationContext txnPtr = cc().makeOperationContext(); + OperationContext& txn = *txnPtr; + DBDirectClient db(&txn); + + db.createIndex(ns(), IndexSpec().addKey("aField").version(1).name("aFieldV1Index")); + } +}; + +class CreateCompoundNamedV1Index : public Base { +public: + CreateCompoundNamedV1Index() : Base("CreateCompoundNamedV1Index") {} + void run() { + const ServiceContext::UniqueOperationContext txnPtr = cc().makeOperationContext(); + OperationContext& txn = *txnPtr; + DBDirectClient db(&txn); + + db.createIndex(ns(), + IndexSpec() + .addKey("aField") + .addKey("bField", IndexSpec::kIndexTypeDescending) + .version(1) + .name("aFieldbFieldV1Index")); + } +}; + +class CreateUniqueSparseDropDupsIndexInBackground : public Base { +public: + CreateUniqueSparseDropDupsIndexInBackground() + : Base("CreateUniqueSparseDropDupsIndexInBackground") {} + void run() { + const ServiceContext::UniqueOperationContext txnPtr = cc().makeOperationContext(); + OperationContext& txn = *txnPtr; + DBDirectClient db(&txn); + + db.createIndex( + ns(), IndexSpec().addKey("aField").background().unique().sparse().dropDuplicates()); + } +}; + +class CreateComplexTextIndex : public Base { +public: + CreateComplexTextIndex() : Base("CreateComplexTextIndex") {} + void run() { + const ServiceContext::UniqueOperationContext txnPtr = cc().makeOperationContext(); + OperationContext& txn = *txnPtr; + DBDirectClient db(&txn); + + db.createIndex(ns(), + IndexSpec() + .addKey("aField", IndexSpec::kIndexTypeText) + .addKey("bField", IndexSpec::kIndexTypeText) + .textWeights(BSON("aField" << 100)) + .textDefaultLanguage("spanish") + .textLanguageOverride("lang") + .textIndexVersion(2)); + } +}; + +class Create2DIndex : public Base { +public: + Create2DIndex() : Base("Create2DIndex") {} + void run() { + const ServiceContext::UniqueOperationContext txnPtr = cc().makeOperationContext(); + OperationContext& txn = *txnPtr; + DBDirectClient db(&txn); + + db.createIndex(ns(), + IndexSpec() + .addKey("aField", IndexSpec::kIndexTypeGeo2D) + .geo2DBits(20) + .geo2DMin(-120.0) + .geo2DMax(120.0)); + } +}; + +class CreateHaystackIndex : public Base { +public: + CreateHaystackIndex() : Base("CreateHaystackIndex") {} + void run() { + const ServiceContext::UniqueOperationContext txnPtr = cc().makeOperationContext(); + OperationContext& txn = *txnPtr; + DBDirectClient db(&txn); + + db.createIndex(ns(), + IndexSpec() + .addKey("aField", IndexSpec::kIndexTypeGeoHaystack) + .addKey("otherField", IndexSpec::kIndexTypeDescending) + .geoHaystackBucketSize(1.0)); + } +}; + +class Create2DSphereIndex : public Base { +public: + Create2DSphereIndex() : Base("Create2DSphereIndex") {} + void run() { + const ServiceContext::UniqueOperationContext txnPtr = cc().makeOperationContext(); + OperationContext& txn = *txnPtr; + DBDirectClient db(&txn); + + db.createIndex(ns(), + IndexSpec() + .addKey("aField", IndexSpec::kIndexTypeGeo2DSphere) + .geo2DSphereIndexVersion(2)); + } +}; + +class CreateHashedIndex : public Base { +public: + CreateHashedIndex() : Base("CreateHashedIndex") {} + void run() { + const ServiceContext::UniqueOperationContext txnPtr = cc().makeOperationContext(); + OperationContext& txn = *txnPtr; + DBDirectClient db(&txn); + + db.createIndex(ns(), IndexSpec().addKey("aField", IndexSpec::kIndexTypeHashed)); + } +}; + +class CreateIndexFailure : public Base { +public: + CreateIndexFailure() : Base("CreateIndexFailure") {} + void run() { + const ServiceContext::UniqueOperationContext txnPtr = cc().makeOperationContext(); + OperationContext& txn = *txnPtr; + DBDirectClient db(&txn); + + db.createIndex(ns(), IndexSpec().addKey("aField")); + ASSERT_THROWS(db.createIndex(ns(), IndexSpec().addKey("aField").unique()), UserException); + } +}; + class All : public Suite { public: All() : Suite("client") {} @@ -244,6 +391,16 @@ public: add<PushBack>(); add<Create>(); add<ConnectionStringTests>(); + add<CreateSimpleV1Index>(); + add<CreateSimpleNamedV1Index>(); + add<CreateCompoundNamedV1Index>(); + add<CreateUniqueSparseDropDupsIndexInBackground>(); + add<CreateComplexTextIndex>(); + add<Create2DIndex>(); + add<CreateHaystackIndex>(); + add<Create2DSphereIndex>(); + add<CreateHashedIndex>(); + add<CreateIndexFailure>(); } }; diff --git a/src/mongo/dbtests/documentsourcetests.cpp b/src/mongo/dbtests/documentsourcetests.cpp index 4118fa78c07..973629cdabe 100644 --- a/src/mongo/dbtests/documentsourcetests.cpp +++ b/src/mongo/dbtests/documentsourcetests.cpp @@ -278,7 +278,7 @@ public: class IndexScanProvidesSortOnKeys : public Base { public: void run() { - client.ensureIndex(nss.ns(), BSON("a" << 1)); + client.createIndex(nss.ns(), BSON("a" << 1)); createSource(BSON("a" << 1)); ASSERT_EQ(source()->getOutputSorts().size(), 1U); @@ -289,7 +289,7 @@ public: class ReverseIndexScanProvidesSort : public Base { public: void run() { - client.ensureIndex(nss.ns(), BSON("a" << -1)); + client.createIndex(nss.ns(), BSON("a" << -1)); createSource(BSON("a" << -1)); ASSERT_EQ(source()->getOutputSorts().size(), 1U); @@ -300,7 +300,7 @@ public: class CompoundIndexScanProvidesMultipleSorts : public Base { public: void run() { - client.ensureIndex(nss.ns(), BSON("a" << 1 << "b" << -1)); + client.createIndex(nss.ns(), BSON("a" << 1 << "b" << -1)); createSource(BSON("a" << 1 << "b" << -1)); ASSERT_EQ(source()->getOutputSorts().size(), 2U); diff --git a/src/mongo/shell/bench.cpp b/src/mongo/shell/bench.cpp index ceac9b90619..7a22953c7dd 100644 --- a/src/mongo/shell/bench.cpp +++ b/src/mongo/shell/bench.cpp @@ -1042,7 +1042,7 @@ void BenchRunWorker::generateLoadOnConnection(DBClientBase* conn) { } } break; case OpType::CREATEINDEX: - conn->ensureIndex(op.ns, op.key, false, "", false); + conn->createIndex(op.ns, op.key); break; case OpType::DROPINDEX: conn->dropIndex(op.ns, op.key); |