summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorAlison Lu <alison.lu@mongodb.com>2021-08-04 15:41:53 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-08-05 15:01:30 +0000
commit89c22bc43c3678861b80b8eef59edc7d0667cb06 (patch)
tree0099e7b1ebcb5497f30b57c9460556b5d87875d1 /src/mongo/db
parent788a4d88a4ce8b6b972c67cd33d874b6ba1d149e (diff)
downloadmongo-89c22bc43c3678861b80b8eef59edc7d0667cb06.tar.gz
SERVER-58396 Translate multiple unordered metaField-only time-series updates into updates on the underlying buckets collection
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/commands/write_commands.cpp14
-rw-r--r--src/mongo/db/timeseries/SConscript1
-rw-r--r--src/mongo/db/timeseries/timeseries_update_delete_util.cpp176
-rw-r--r--src/mongo/db/timeseries/timeseries_update_delete_util.h7
4 files changed, 54 insertions, 144 deletions
diff --git a/src/mongo/db/commands/write_commands.cpp b/src/mongo/db/commands/write_commands.cpp
index e336ed6eb02..a69ccf9f885 100644
--- a/src/mongo/db/commands/write_commands.cpp
+++ b/src/mongo/db/commands/write_commands.cpp
@@ -1136,8 +1136,8 @@ public:
write_ops::UpdateCommandReply typedRun(OperationContext* opCtx) final try {
transactionChecks(opCtx, ns());
-
write_ops::UpdateCommandReply updateReply;
+ OperationSource source = OperationSource::kStandard;
if (isTimeseries(opCtx, ns())) {
uassert(ErrorCodes::InvalidOptions,
@@ -1149,15 +1149,7 @@ public:
"time-series collection: "
<< ns(),
!opCtx->inMultiDocumentTransaction());
-
- auto reply = write_ops_exec::performUpdates(
- opCtx, request(), OperationSource::kTimeseriesUpdate);
- populateReply(opCtx,
- !request().getWriteCommandRequestBase().getOrdered(),
- request().getUpdates().size(),
- std::move(reply),
- &updateReply);
- return updateReply;
+ source = OperationSource::kTimeseriesUpdate;
}
long long nModified = 0;
@@ -1166,7 +1158,7 @@ public:
// 'postProcessHandler' and should not be accessed afterwards.
std::vector<write_ops::Upserted> upsertedInfoVec;
- auto reply = write_ops_exec::performUpdates(opCtx, request());
+ auto reply = write_ops_exec::performUpdates(opCtx, request(), source);
// Handler to process each 'SingleWriteResult'.
auto singleWriteHandler = [&](const SingleWriteResult& opResult, int index) {
diff --git a/src/mongo/db/timeseries/SConscript b/src/mongo/db/timeseries/SConscript
index d6e541baed0..1f82e872c55 100644
--- a/src/mongo/db/timeseries/SConscript
+++ b/src/mongo/db/timeseries/SConscript
@@ -75,7 +75,6 @@ env.Library(
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/bson/mutable/mutable_bson',
- '$BUILD_DIR/mongo/db/ops/write_ops_parsers',
'$BUILD_DIR/mongo/db/pipeline/pipeline',
],
)
diff --git a/src/mongo/db/timeseries/timeseries_update_delete_util.cpp b/src/mongo/db/timeseries/timeseries_update_delete_util.cpp
index 065784ffe80..919ecd9dab4 100644
--- a/src/mongo/db/timeseries/timeseries_update_delete_util.cpp
+++ b/src/mongo/db/timeseries/timeseries_update_delete_util.cpp
@@ -127,63 +127,29 @@ bool updateOnlyModifiesMetaField(OperationContext* opCtx,
const NamespaceString& ns,
const mongo::write_ops::UpdateModification& updateMod,
StringData metaField) {
- switch (updateMod.type()) {
- case write_ops::UpdateModification::Type::kClassic: {
- const auto& document = updateMod.getUpdateClassic();
- if (isDocReplacement(document))
- return false;
- // else document is an update document.
- for (auto&& updatePair : document) {
- // updatePair = <updateOperator> : {<field1> : <value1>, ... }
- for (auto&& fieldValuePair : updatePair.embeddedObject()) {
- // updatePair.embeddedObject() = {<field1> : <value1>, ... }
- if (!isMetaFieldFirstElementOfDottedPathField(
- fieldValuePair.fieldNameStringData(), metaField))
- return false;
- }
- }
- break;
- }
- case write_ops::UpdateModification::Type::kPipeline: {
- const auto& updatePipeline = updateMod.getUpdatePipeline();
- for (const auto& stage : updatePipeline) {
- auto aggOp = stage.firstElementFieldNameStringData();
- auto operation = stage.firstElement();
- if (aggOp == "$set" || aggOp == "$addFields") {
- // stage = {$set: {<newField> : <newExpression>, ...}}
- // operation = $set: {<newField> : <newExpression>, ...}
- for (auto&& updatePair : operation.embeddedObject()) {
- // operation.embeddedObject() = {<newField> : <newExpression>, ...}
- if (!isMetaFieldFirstElementOfDottedPathField(
- updatePair.fieldNameStringData(), metaField)) {
- return false;
- }
- }
- } else if (aggOp == "$unset" || aggOp == "$project") {
- if (operation.type() == BSONType::Array) {
- // stage = {$unset: ["field1", "field2", ...]}
- for (auto elt : operation.Array()) {
- if (!isMetaFieldFirstElementOfDottedPathField(elt.valueStringDataSafe(),
- metaField))
- return false;
- }
- } else {
- // stage = {$unset: "singleField"}
- if (!isMetaFieldFirstElementOfDottedPathField(
- operation.valueStringDataSafe(), metaField))
- return false;
- }
- } else { // aggOp == "$replaceWith" || aggOp == "$replaceRoot"
- return false;
- }
- }
- break;
- }
- case write_ops::UpdateModification::Type::kDelta:
- // It is not possible for the client to run a delta update.
- MONGO_UNREACHABLE;
- }
- return true;
+ invariant(updateMod.type() != write_ops::UpdateModification::Type::kDelta);
+ uassert(ErrorCodes::InvalidOptions,
+ str::stream() << "Pipeline upddates are not supported for time-series collections: "
+ << ns,
+ updateMod.type() != write_ops::UpdateModification::Type::kPipeline);
+
+ const auto& document = updateMod.getUpdateClassic();
+ uassert(ErrorCodes::InvalidOptions,
+ str::stream()
+ << "Replacement document updates are not supported for time-series collections: "
+ << ns,
+ !isDocReplacement(document));
+
+ return std::all_of(document.begin(), document.end(), [metaField](const auto& updatePair) {
+ // updatePair = <updateOperator> : {<field1> : <value1>, ... }
+ // updatePair.embeddedObject() = {<field1> : <value1>, ... }
+ return std::all_of(updatePair.embeddedObject().begin(),
+ updatePair.embeddedObject().end(),
+ [metaField](const auto& fieldValuePair) {
+ return isMetaFieldFirstElementOfDottedPathField(
+ fieldValuePair.fieldNameStringData(), metaField);
+ });
+ });
}
BSONObj translateQuery(const BSONObj& query, StringData metaField) {
@@ -196,82 +162,34 @@ BSONObj translateQuery(const BSONObj& query, StringData metaField) {
write_ops::UpdateModification translateUpdate(const write_ops::UpdateModification& updateMod,
StringData metaField) {
invariant(!metaField.empty());
- // Make a mutable copy of the updates to apply where we can replace all occurrences
- // of the metaField with "meta". The update is either a document or a pipeline.
- switch (updateMod.type()) {
- case write_ops::UpdateModification::Type::kClassic: {
- const auto& document = updateMod.getUpdateClassic();
- invariant(!isDocReplacement(document));
+ invariant(updateMod.type() == write_ops::UpdateModification::Type::kClassic);
- // Make a mutable copy of the update document.
- auto updateDoc = mutablebson::Document(document);
- // updateDoc = { <updateOperator> : { <field1>: <value1>, ... },
- // <updateOperator> : { <field1>: <value1>, ... },
- // ... }
+ const auto& document = updateMod.getUpdateClassic();
+ invariant(!isDocReplacement(document));
- // updateDoc.root() = <updateOperator> : { <field1>: <value1>, ... },
- // <updateOperator> : { <field1>: <value1>, ... },
- // ...
- for (size_t i = 0; i < updateDoc.root().countChildren(); ++i) {
- auto updatePair = updateDoc.root().findNthChild(i);
- // updatePair = <updateOperator> : { <field1>: <value1>, ... }
- // Check each field that is being modified by the update operator
- // and replace it if it is the metaField.
- for (size_t j = 0; j < updatePair.countChildren(); j++) {
- auto fieldValuePair = updatePair.findNthChild(j);
- replaceQueryMetaFieldName(
- fieldValuePair, fieldValuePair.getFieldName(), metaField);
- }
- }
+ // Make a mutable copy of the update document in order to replace all occurrences of the
+ // metaField with "meta".
+ auto updateDoc = mutablebson::Document(document);
+ // updateDoc = { <updateOperator> : { <field1>: <value1>, ... },
+ // <updateOperator> : { <field1>: <value1>, ... },
+ // ... }
- return write_ops::UpdateModification::parseFromClassicUpdate(updateDoc.getObject());
- }
- case write_ops::UpdateModification::Type::kPipeline: {
- std::vector<BSONObj> translatedPipeline;
- for (const auto& stage : updateMod.getUpdatePipeline()) {
- // stage: { <$operator> : <argument(s)> }
- mutablebson::Document stageDoc(stage);
- auto root = stageDoc.root();
- for (size_t i = 0; i < root.countChildren(); ++i) {
- auto updatePair = root.findNthChild(i);
- auto aggOp = updatePair.getFieldName();
- if (aggOp == "$set" || aggOp == "$addFields") {
- // updatePair = $set: {<newField> : <newExpression>, ...}
- for (size_t j = 0; j < updatePair.countChildren(); j++) {
- auto fieldValuePair = updatePair.findNthChild(j);
- replaceQueryMetaFieldName(
- fieldValuePair, fieldValuePair.getFieldName(), metaField);
- }
- } else if (aggOp == "$unset" || aggOp == "$project") {
- if (updatePair.getType() == BSONType::Array) {
- // updatePair = $unset: ["field1", "field2", ...]
- for (size_t j = 0; j < updatePair.countChildren(); j++) {
- replaceQueryMetaFieldName(
- updatePair,
- updatePair.findNthChild(j).getValueString(),
- metaField);
- }
- } else { // updatePair.getType() == BSONType::String
- // updatePair = $unset: "singleField"
- auto singleField = StringData(updatePair.getValueString());
- if (isMetaFieldFirstElementOfDottedPathField(singleField, metaField)) {
- // Replace updatePair with a new pair where singleField is renamed.
- auto newPair = stageDoc.makeElementString(
- aggOp, getRenamedField(singleField, "meta"));
- updatePair.remove().ignore();
- root.pushBack(newPair).ignore();
- }
- } // else aggOp == "$replaceWith" || aggOp == "$replaceRoot"
- }
- }
- // Add the translated pipeline.
- translatedPipeline.push_back(stageDoc.getObject());
- }
- return write_ops::UpdateModification(translatedPipeline);
+ // updateDoc.root() = <updateOperator> : { <field1>: <value1>, ... },
+ // <updateOperator> : { <field1>: <value1>, ... },
+ // ...
+ for (size_t i = 0; i < updateDoc.root().countChildren(); ++i) {
+ // TODO: SERVER-59104 Remove usages of findNthChild().
+ auto updatePair = updateDoc.root().findNthChild(i);
+ // updatePair = <updateOperator> : { <field1>: <value1>, ... }
+ // Check each field that is being modified by the update operator
+ // and replace it if it is the metaField.
+ for (size_t j = 0; j < updatePair.countChildren(); j++) {
+ // TODO: SERVER-59104 Remove usages of findNthChild().
+ auto fieldValuePair = updatePair.findNthChild(j);
+ replaceQueryMetaFieldName(fieldValuePair, fieldValuePair.getFieldName(), metaField);
}
- case write_ops::UpdateModification::Type::kDelta:
- MONGO_UNREACHABLE;
}
- MONGO_UNREACHABLE;
+
+ return write_ops::UpdateModification::parseFromClassicUpdate(updateDoc.getObject());
}
} // namespace mongo::timeseries
diff --git a/src/mongo/db/timeseries/timeseries_update_delete_util.h b/src/mongo/db/timeseries/timeseries_update_delete_util.h
index 63327c69af2..6e4523902ff 100644
--- a/src/mongo/db/timeseries/timeseries_update_delete_util.h
+++ b/src/mongo/db/timeseries/timeseries_update_delete_util.h
@@ -32,7 +32,6 @@
#include "mongo/bson/bsonobj.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/ops/write_ops_gen.h"
-#include "mongo/db/ops/write_ops_parsers.h"
namespace mongo::timeseries {
@@ -51,7 +50,9 @@ bool queryOnlyDependsOnMetaField(
/**
* Returns true if the given update modification only modifies the time-series collection's given
- * metaField, false otherwise. Returns false on any document replacement.
+ * metaField, false otherwise. Requires that the update is not a delta update, and throws an
+ * exception if the update is not an update document (e.g. is a pipeline update or a replacement
+ * document).
*/
bool updateOnlyModifiesMetaField(OperationContext* opCtx,
const NamespaceString& ns,
@@ -71,7 +72,7 @@ BSONObj translateQuery(const BSONObj& query, StringData metaField);
* Translates the given update on the time-series collection to an update on the time-series
* collection's underlying buckets collection. Creates and returns a translated UpdateModification
* where all occurrences of metaField in updateMod are replaced with the literal "meta". Requires
- * that updateMod is not a replacement document and that the given metaField is not empty.
+ * that updateMod is an update document and that the given metaField is not empty.
*/
write_ops::UpdateModification translateUpdate(const write_ops::UpdateModification& updateMod,
StringData metaField);