diff options
author | Alison Lu <alison.lu@mongodb.com> | 2021-08-04 15:41:53 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-08-05 15:01:30 +0000 |
commit | 89c22bc43c3678861b80b8eef59edc7d0667cb06 (patch) | |
tree | 0099e7b1ebcb5497f30b57c9460556b5d87875d1 /src/mongo/db | |
parent | 788a4d88a4ce8b6b972c67cd33d874b6ba1d149e (diff) | |
download | mongo-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.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/timeseries/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/timeseries/timeseries_update_delete_util.cpp | 176 | ||||
-rw-r--r-- | src/mongo/db/timeseries/timeseries_update_delete_util.h | 7 |
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); |