summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDianna Hohensee <dianna.hohensee@mongodb.com>2021-03-05 14:23:08 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-03-05 20:58:46 +0000
commit69027dee0ae53ef10a0adcc324d06ef12d0f634f (patch)
tree60ab77125f33d6efc4921576250fa488d725393b
parent7739da6997795c20924580087a0e09db0a8cf929 (diff)
downloadmongo-69027dee0ae53ef10a0adcc324d06ef12d0f634f.tar.gz
SERVER-54929 Helper functions to convert underlying buckets collection index specs to time-series
collection index specs
-rw-r--r--src/mongo/db/commands/dbcommands.cpp95
-rw-r--r--src/mongo/db/commands/list_indexes.cpp132
-rw-r--r--src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.cpp54
-rw-r--r--src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.h24
4 files changed, 111 insertions, 194 deletions
diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp
index 34ba441a488..b6c1790794f 100644
--- a/src/mongo/db/commands/dbcommands.cpp
+++ b/src/mongo/db/commands/dbcommands.cpp
@@ -116,87 +116,6 @@ using std::unique_ptr;
namespace {
/**
- * Returns time-series options if 'ns' refers to a time-series collection.
- */
-boost::optional<TimeseriesOptions> getTimeseriesOptions(OperationContext* opCtx,
- const NamespaceString& ns) {
- auto viewCatalog = DatabaseHolder::get(opCtx)->getViewCatalog(opCtx, ns.db());
- if (!viewCatalog) {
- return {};
- }
-
- auto view = viewCatalog->lookupWithoutValidatingDurableViews(opCtx, ns.ns());
- if (!view) {
- return {};
- }
-
- // Return a copy of the time-series options so that we don't refer to the internal state of
- // 'viewCatalog' once it goes out of scope.
- return view->timeseries();
-}
-
-/**
- * Returns an index key with field names mapped to the bucket collection schema.
- */
-BSONObj makeTimeseriesIndexSpecKey(const TimeseriesOptions& timeseriesOptions,
- const CollMod& origCmd,
- const BSONObj& origKey) {
- auto timeField = timeseriesOptions.getTimeField();
- auto metaField = timeseriesOptions.getMetaField();
-
- BSONObjBuilder builder;
- for (const auto& elem : origKey) {
- // Determine if the index requested on the time field is ascending or descending.
- // The final index spec will be subjected to a more complete validation in
- // index_key_validate::validateKeyPattern().
- if (elem.fieldNameStringData() == timeField) {
- uassert(
- ErrorCodes::IndexNotFound,
- str::stream() << "Invalid index spec for time-series collection: "
- << redact(origCmd.toBSON({})) // 'origKey' is included in 'origCmd'
- << ". Indexes on the time field must be ascending or descending "
- "(numbers only): "
- << elem,
- elem.isNumber());
- if (elem.number() >= 0) {
- builder.appendAs(elem, str::stream() << "control.min." << timeField);
- builder.appendAs(elem, str::stream() << "control.max." << timeField);
- } else {
- builder.appendAs(elem, str::stream() << "control.max." << timeField);
- builder.appendAs(elem, str::stream() << "control.min." << timeField);
- }
- continue;
- }
-
- uassert(ErrorCodes::IndexNotFound,
- str::stream() << "Invalid index spec for time-series collection: "
- << redact(origCmd.toBSON({})) // 'origKey' is included in 'origCmd'
- << ". Index must be on the '" << timeField << "' field: " << elem,
- metaField);
-
- if (elem.fieldNameStringData() == *metaField) {
- builder.appendAs(elem, BucketUnpacker::kBucketMetaFieldName);
- continue;
- }
-
- if (elem.fieldNameStringData().startsWith(*metaField + ".")) {
- builder.appendAs(elem,
- str::stream()
- << BucketUnpacker::kBucketMetaFieldName << "."
- << elem.fieldNameStringData().substr(metaField->size() + 1));
- continue;
- }
-
- uasserted(ErrorCodes::IndexNotFound,
- str::stream() << "Invalid index spec for time-series collection: "
- << redact(origCmd.toBSON({})) // 'origKey' is included in 'origCmd'
- << ". Index must be either on the '" << *metaField << "' or '"
- << timeField << "' fields: " << elem);
- }
- return builder.obj();
-}
-
-/**
* Returns a CollMod on the underlying buckets collection of the time-series collection.
* Returns null if 'origCmd' is not for a time-series collection.
*/
@@ -204,18 +123,24 @@ std::unique_ptr<CollMod> makeTimeseriesCollModCommand(OperationContext* opCtx,
const CollMod& origCmd) {
const auto& origNs = origCmd.getNamespace();
- auto timeseriesOptions = getTimeseriesOptions(opCtx, origNs);
+ auto timeseriesOptions = timeseries::getTimeseriesOptions(opCtx, origNs);
// Return early with null if we are not working with a time-series collection.
if (!timeseriesOptions) {
return {};
}
- // TODO(SERVER-54639): Map index specs to bucket collection using helper function.
auto index = origCmd.getIndex();
if (index && index->getKeyPattern()) {
- index->setKeyPattern(
- makeTimeseriesIndexSpecKey(*timeseriesOptions, origCmd, *index->getKeyPattern()));
+ auto bucketsIndexSpecWithStatus = timeseries::convertTimeseriesIndexSpecToBucketsIndexSpec(
+ *timeseriesOptions, *index->getKeyPattern());
+
+ uassert(ErrorCodes::IndexNotFound,
+ str::stream() << bucketsIndexSpecWithStatus.getStatus().toString()
+ << " Command request: " << redact(origCmd.toBSON({})),
+ bucketsIndexSpecWithStatus.isOK());
+
+ index->setKeyPattern(std::move(bucketsIndexSpecWithStatus.getValue()));
}
auto ns = origNs.makeTimeseriesBucketsNamespace();
diff --git a/src/mongo/db/commands/list_indexes.cpp b/src/mongo/db/commands/list_indexes.cpp
index 699efe6555c..5d207392b6a 100644
--- a/src/mongo/db/commands/list_indexes.cpp
+++ b/src/mongo/db/commands/list_indexes.cpp
@@ -36,7 +36,6 @@
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/auth/authorization_session.h"
-#include "mongo/db/catalog/database_holder.h"
#include "mongo/db/catalog/list_indexes.h"
#include "mongo/db/clientcursor.h"
#include "mongo/db/commands.h"
@@ -49,7 +48,6 @@
#include "mongo/db/exec/working_set.h"
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/list_indexes_gen.h"
-#include "mongo/db/pipeline/document_source_internal_unpack_bucket.h"
#include "mongo/db/query/cursor_request.h"
#include "mongo/db/query/cursor_response.h"
#include "mongo/db/query/find_common.h"
@@ -57,7 +55,7 @@
#include "mongo/db/service_context.h"
#include "mongo/db/storage/durable_catalog.h"
#include "mongo/db/storage/storage_engine.h"
-#include "mongo/db/views/view_catalog.h"
+#include "mongo/db/timeseries/timeseries_index_schema_conversion_functions.h"
#include "mongo/logv2/log.h"
#include "mongo/util/uuid.h"
@@ -65,105 +63,20 @@ namespace mongo {
namespace {
/**
- * Returns time-series options if 'ns' refers to a time-series collection.
- */
-boost::optional<TimeseriesOptions> getTimeseriesOptions(
- OperationContext* opCtx, const boost::optional<NamespaceString>& ns) {
- if (!ns) {
- return {};
- }
-
- auto viewCatalog = DatabaseHolder::get(opCtx)->getViewCatalog(opCtx, ns->db());
- if (!viewCatalog) {
- return {};
- }
-
- auto view = viewCatalog->lookupWithoutValidatingDurableViews(opCtx, ns->ns());
- if (!view) {
- return {};
- }
-
- // Return a copy of the time-series options so that we don't refer to the internal state of
- // 'viewCatalog' once it goes out of scope.
- return view->timeseries();
-}
-
-/**
- * Returns an index key with field names mapped from the bucket collection schema.
- * Returns an empty BSONObj if the index key cannot be converted.
- */
-BSONObj makeTimeseriesIndexSpecKey(const TimeseriesOptions& timeseriesOptions,
- const BSONObj& origKey) {
- auto timeField = timeseriesOptions.getTimeField();
- auto metaField = timeseriesOptions.getMetaField();
-
- std::string controlMinTimeField = str::stream() << "control.min." << timeField;
- std::string controlMaxTimeField = str::stream() << "control.max." << timeField;
-
- BSONObjBuilder builder;
- for (const auto& elem : origKey) {
- // Determine if the index requested on the time field is ascending or descending.
- // The final index spec will be subjected to a more complete validation in
- // index_key_validate::validateKeyPattern().
- if (elem.fieldNameStringData() == controlMinTimeField) {
- if (!elem.isNumber()) {
- return {};
- }
- builder.appendAs(elem, timeField);
- continue;
- } else if (elem.fieldNameStringData() == controlMaxTimeField) {
- // Skip control.max.<timeField> since the control.min.<timeField> field is enough to
- // figure out the direction of the index (ascending/descending).
- continue;
- }
-
- if (!metaField) {
- return {};
- }
-
- if (elem.fieldNameStringData() == BucketUnpacker::kBucketMetaFieldName) {
- builder.appendAs(elem, *metaField);
- continue;
- }
-
- if (elem.fieldNameStringData().startsWith(BucketUnpacker::kBucketMetaFieldName + ".")) {
- builder.appendAs(elem,
- str::stream() << *metaField << "."
- << elem.fieldNameStringData().substr(
- BucketUnpacker::kBucketMetaFieldName.size() + 1));
- continue;
- }
-
- return {};
- }
- return builder.obj();
-}
-
-/**
+ * Converts buckets collection index specs to the time-series collection schema.
* Returns a list of index specs mapped from the bucket collection schema.
*/
std::list<BSONObj> makeTimeseriesIndexSpecs(const TimeseriesOptions& timeseriesOptions,
- const std::list<BSONObj>& bucketIndexSpecs) {
+ const std::list<BSONObj>& bucketsIndexSpecs) {
std::list<BSONObj> indexSpecs;
- for (const auto& bucketIndexSpec : bucketIndexSpecs) {
+ for (const auto& bucketsIndexSpec : bucketsIndexSpecs) {
// TODO(SERVER-54639): Map index specs from bucket collection using helper function.
BSONObjBuilder builder;
bool skip = false;
- for (const auto& elem : bucketIndexSpec) {
+ for (const auto& elem : bucketsIndexSpec) {
if (elem.fieldNameStringData() == ListIndexesReplyItem::kKeyFieldName) {
- // On a time-series collection with 'tm' time field and 'mm' metadata field,
- // we may see a compound index on the underlying bucket collection mapped from:
- // {
- // 'meta.tag1': 1,
- // 'control.min.tm': 1,
- // 'control.max.tm': 1
- // }
- // to an index on the time-series collection:
- // {
- // 'mm.tag1': 1,
- // tm: 1
- // }
- auto key = makeTimeseriesIndexSpecKey(timeseriesOptions, elem.Obj());
+ auto key = timeseries::convertBucketsIndexSpecToTimeseriesIndexSpec(
+ timeseriesOptions, elem.Obj());
if (key.isEmpty()) {
// Skip index spec due to failed conversion.
skip = true;
@@ -204,21 +117,22 @@ IndexSpecsWithNamespaceString getIndexSpecsWithNamespaceString(OperationContext*
// Since time-series collections don't have UUIDs, we skip the time-series lookup
// if the target collection is specified as a UUID.
- if (const auto& origNss = origNssOrUUID.nss();
- auto timeseriesOptions = getTimeseriesOptions(opCtx, origNss)) {
- auto bucketsNss = origNss->makeTimeseriesBucketsNamespace();
- AutoGetCollectionForReadCommandMaybeLockFree autoColl(opCtx, bucketsNss);
-
- const CollectionPtr& coll = autoColl.getCollection();
- uassert(ErrorCodes::NamespaceNotFound,
- str::stream() << "ns does not exist: " << bucketsNss,
- coll);
-
- return std::make_pair(
- makeTimeseriesIndexSpecs(
- *timeseriesOptions,
- listIndexesInLock(opCtx, coll, bucketsNss, cmd.getIncludeBuildUUIDs())),
- *origNss);
+ if (const auto& origNss = origNssOrUUID.nss()) {
+ if (auto timeseriesOptions = timeseries::getTimeseriesOptions(opCtx, *origNss)) {
+ auto bucketsNss = origNss->makeTimeseriesBucketsNamespace();
+ AutoGetCollectionForReadCommandMaybeLockFree autoColl(opCtx, bucketsNss);
+
+ const CollectionPtr& coll = autoColl.getCollection();
+ uassert(ErrorCodes::NamespaceNotFound,
+ str::stream() << "ns does not exist: " << bucketsNss,
+ coll);
+
+ return std::make_pair(
+ makeTimeseriesIndexSpecs(
+ *timeseriesOptions,
+ listIndexesInLock(opCtx, coll, bucketsNss, cmd.getIncludeBuildUUIDs())),
+ *origNss);
+ }
}
AutoGetCollectionForReadCommandMaybeLockFree autoColl(opCtx, origNssOrUUID);
diff --git a/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.cpp b/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.cpp
index e9212d3b212..57841a7b83f 100644
--- a/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.cpp
+++ b/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.cpp
@@ -123,5 +123,59 @@ StatusWith<BSONObj> convertTimeseriesIndexSpecToBucketsIndexSpec(
return builder.obj();
}
+BSONObj convertBucketsIndexSpecToTimeseriesIndexSpec(const TimeseriesOptions& timeseriesOptions,
+ const BSONObj& bucketsIndexSpecBSON) {
+ auto timeField = timeseriesOptions.getTimeField();
+ auto metaField = timeseriesOptions.getMetaField();
+
+ const std::string controlMinTimeField = str::stream() << "control.min." << timeField;
+ const std::string controlMaxTimeField = str::stream() << "control.max." << timeField;
+
+ BSONObjBuilder builder;
+ for (const auto& elem : bucketsIndexSpecBSON) {
+ // The index specification on the time field is ascending or descending.
+ if (elem.fieldNameStringData() == controlMinTimeField) {
+ if (!elem.isNumber()) {
+ // This index spec on the underlying buckets collection is not valid for
+ // time-series. Therefore, we will not convert the index spec..
+ return {};
+ }
+
+ builder.appendAs(elem, timeField);
+ continue;
+ } else if (elem.fieldNameStringData() == controlMaxTimeField) {
+ // Skip 'control.max.<timeField>' since the 'control.min.<timeField>' field is
+ // sufficient to determine whether the index is ascending or descending.
+ continue;
+ }
+
+ if (!metaField) {
+ // 'elem' is an invalid index spec field for this time-series collection. It does not
+ // match the time field and there is no metaField set. Therefore, we will not convert
+ // the index spec.
+ return {};
+ }
+
+ if (elem.fieldNameStringData() == BucketUnpacker::kBucketMetaFieldName) {
+ builder.appendAs(elem, *metaField);
+ continue;
+ }
+
+ if (elem.fieldNameStringData().startsWith(BucketUnpacker::kBucketMetaFieldName + ".")) {
+ builder.appendAs(elem,
+ str::stream() << *metaField << "."
+ << elem.fieldNameStringData().substr(
+ BucketUnpacker::kBucketMetaFieldName.size() + 1));
+ continue;
+ }
+
+ // 'elem' is an invalid index spec field for this time-series collection. It matches neither
+ // the time field nor the metaField field. Therefore, we will not convert the index spec.
+ return {};
+ }
+
+ return builder.obj();
+}
+
} // namespace timeseries
} // namespace mongo
diff --git a/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.h b/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.h
index 151837bdce9..aec7cda44c7 100644
--- a/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.h
+++ b/src/mongo/db/timeseries/timeseries_index_schema_conversion_functions.h
@@ -58,5 +58,29 @@ boost::optional<TimeseriesOptions> getTimeseriesOptions(OperationContext* opCtx,
StatusWith<BSONObj> convertTimeseriesIndexSpecToBucketsIndexSpec(
const TimeseriesOptions& timeseriesOptions, const BSONObj& timeseriesIndexSpecBSON);
+/**
+ * Maps the buckets collection index spec 'bucketsIndexSpecBSON' to the index schema of the
+ * time-series collection using the information provided in 'timeseriesOptions'.
+ *
+ * If 'bucketsIndexSpecBSON' does not match a valid time-series index format, then an empty BSONObj
+ * is returned.
+ *
+ * Conversion Example:
+ * On a time-series collection with 'tm' time field and 'mm' metadata field,
+ * we may see a compound index on the underlying bucket collection mapped from:
+ * {
+ * 'meta.tag1': 1,
+ * 'control.min.tm': 1,
+ * 'control.max.tm': 1
+ * }
+ * to an index on the time-series collection:
+ * {
+ * 'mm.tag1': 1,
+ * 'tm': 1
+ * }
+ */
+BSONObj convertBucketsIndexSpecToTimeseriesIndexSpec(const TimeseriesOptions& timeseriesOptions,
+ const BSONObj& bucketsIndexSpecBSON);
+
} // namespace timeseries
} // namespace mongo