Rakesh Vidyadharan Help

Transaction

Execute a sequence of actions in a transaction. Nest the individual actions that are to be performed in the transaction within the document sub-document.

The document in the payload must include an items array of documents. Each document in the array represents the full specification for the action in the transaction. The document specification is the same as the document specification for using the service.

The specification for the action document in the items array is:

  • action (string) - The type of action to perform. Should be one of create|update|delete.

  • database (string) - The database in which the step is to be performed.

  • collection (string) - The collection in which the step is to be performed.

  • document (document) - The BSON specification for executing the action.

  • skipVersion (bool) - Do not create version history document for this action.

Data models that represents the payload to be submitted to the service for creating/deleting documents.

// // Created by Rakesh on 22/12/2024. // #pragma once #include "create.hpp" #include "update.hpp" #include "delete.hpp" namespace spt::mongoservice::api::model::request { struct TransactionBuilder { TransactionBuilder( std::string_view database, std::string_view collection ) : database{ database }, collection{ collection } {} ~TransactionBuilder() = default; TransactionBuilder( const TransactionBuilder& ) = delete; TransactionBuilder& operator=( const TransactionBuilder& ) = delete; TransactionBuilder( TransactionBuilder&& ) = delete; TransactionBuilder& operator=( TransactionBuilder&& ) = delete; template <util::Visitable Document, util::Visitable Metadata> requires std::is_same_v<decltype(Document::id), bsoncxx::oid> void addCreate( const Create<Document, Metadata>& request ) { items << util::marshall( request ); } template <util::Visitable Document, util::Visitable Metadata> requires std::is_same_v<decltype(Document::id), bsoncxx::oid> void addUpdate( const MergeForId<Document, Metadata>& request ) { items << util::marshall( request ); } template <util::Visitable Document, util::Visitable Metadata, util::Visitable Filter, template<typename, typename, typename> typename Model> requires std::is_same_v<Model<Document, Metadata, Filter>, Replace<Document, Metadata, Filter>> || std::is_same_v<Model<Document, Metadata, Filter>, Update<Document, Metadata, Filter>> void addUpdate( const Model<Document, Metadata, Filter>& request ) { items << util::marshall( request ); } template <util::Visitable Document, util::Visitable Metadata> requires std::constructible_from<Metadata, bsoncxx::document::view> void addRemove( const Delete<Document, Metadata>& request ) { items << util::marshall( request ); } [[nodiscard]] bsoncxx::document::value build(); private: std::string database; std::string collection; bsoncxx::builder::stream::array items; }; }

Data models that represents the payloads the service responds with when creating/deleting document(s).

// // Created by Rakesh on 19/12/2024. // #pragma once #if defined __has_include #if __has_include("../../../common/visit_struct/visit_struct_intrusive.hpp") #include "../../../common/visit_struct/visit_struct_intrusive.hpp" #include "../../../common/util/serialise.hpp" #else #include <mongo-service/common/visit_struct/visit_struct_intrusive.hpp> #include <mongo-service/common/util/serialise.hpp> #endif #endif #include <cstdint> #include <string> #include <vector> #include <bsoncxx/oid.hpp> namespace spt::mongoservice::api::model::response { struct Transaction { struct History { History() = default; ~History() = default; History(History&&) = default; History& operator=(History&&) = default; bool operator==(const History& rhs) const = default; History(const History&) = delete; History& operator=(const History&) = delete; BEGIN_VISITABLES(History); VISITABLE(std::vector<bsoncxx::oid>, created); VISITABLE(std::vector<bsoncxx::oid>, updated); VISITABLE(std::vector<bsoncxx::oid>, deleted); VISITABLE(std::string, database); VISITABLE(std::string, collection); END_VISITABLES; }; explicit Transaction( bsoncxx::document::view document ) { util::unmarshall( *this, document ); } Transaction() = default; ~Transaction() = default; Transaction(Transaction&&) = default; Transaction& operator=(Transaction&&) = default; Transaction(const Transaction&) = delete; Transaction& operator=(const Transaction&) = delete; BEGIN_VISITABLES(Transaction); VISITABLE(History, history); VISITABLE(int32_t, created); VISITABLE(int32_t, updated); VISITABLE(int32_t, deleted); END_VISITABLES; }; }

Sample code illustrating the transaction action.

#include <mongo-service/api/repository/repository.hpp> namespace example { struct Document { explicit Document( bsoncxx::document::view bson ) { spt::util::unmarshall( *this, bson ); } Document() = default; ~Document() = default; Document(Document&&) = default; Document& operator=(Document&&) = default; bool operator==(const Document&) const = default; Document(const Document&) = delete; Document& operator=(const Document&) = delete; BEGIN_VISITABLES(Document); VISITABLE(bsoncxx::oid, id); VISITABLE(std::string, str); VISITABLE_DIRECT_INIT(spt::util::DateTimeMs, created, {std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now().time_since_epoch() )}); VISITABLE_DIRECT_INIT(int64_t, integer, {5}); VISITABLE_DIRECT_INIT(double, floating, {10.345}); VISITABLE_DIRECT_INIT(bool, boolean, {true}); END_VISITABLES; }; struct Metadata { explicit Metadata( bsoncxx::document::view bson ) { spt::util::unmarshall( *this, bson ); } Metadata() = default; ~Metadata() = default; Metadata(Metadata&&) = default; Metadata& operator=(Metadata&&) = default; bool operator==(const Metadata&) const = default; Metadata(const Metadata&) = delete; Metadata& operator=(const Metadata&) = delete; BEGIN_VISITABLES(Metadata); VISITABLE(std::string, project); VISITABLE(std::string, product); END_VISITABLES; }; } int main() { using namespace spt::mongoservice::api; using bsoncxx::builder::stream::open_array; using bsoncxx::builder::stream::close_array; using bsoncxx::builder::stream::open_document; using bsoncxx::builder::stream::close_document; auto builder = model::request::TransactionBuilder{ "unit", "test" }; auto insert = model::request::Create<pmodel::Document, pmodel::Metadata>{}; insert.database = "test"; insert.collection = "test"; insert.application = "unitTest"; insert.document.str = "value"; insert.metadata = pmodel::Metadata{}; insert.metadata->project = "serialisation"; insert.metadata->product = "mongo-service"; insert.options = options::Insert{}; insert.options->writeConcern = options::WriteConcern{}; insert.options->writeConcern->tag = "test"; insert.options->writeConcern->majority = std::chrono::milliseconds{ 100 }; insert.options->writeConcern->timeout = std::chrono::milliseconds{ 250 }; insert.options->writeConcern->nodes = 2; insert.options->writeConcern->acknowledgeLevel = options::WriteConcern::Level::Majority; insert.options->writeConcern->journal = true; insert.options->bypassValidation = true; insert.options->ordered = true; builder.addCreate( insert ); auto update = model::request::Update<bsoncxx::document::value, pmodel::Metadata, model::request::IdFilter>{}; update.document.filter = model::request::IdFilter{}; update.document.filter->id = bsoncxx::oid{}; update.document.update = document{} << "$unset" << open_document << "obsoleteProperty" << 1 << close_document << "$set" << open_document << "modified" << bsoncxx::types::b_date{ std::chrono::system_clock::now() } << "user._id" << bsoncxx::oid{} << close_document << finalize; update.database = "unit"; update.collection = "test"; update.application = "unitTest"; update.options = options::Update{}; update.options->collation = options::Collation{}; update.options->collation->locale = "en"; update.options->collation->strength = 1; update.options->writeConcern = options::WriteConcern{}; update.options->writeConcern->tag = "test"; update.options->writeConcern->majority = std::chrono::milliseconds{ 100 }; update.options->writeConcern->timeout = std::chrono::milliseconds{ 250 }; update.options->writeConcern->nodes = 2; update.options->writeConcern->acknowledgeLevel = options::WriteConcern::Level::Majority; update.options->writeConcern->journal = true; update.options->bypassValidation = false; update.options->upsert = false; builder.addUpdate( update ); auto remove = model::request::Delete<bsoncxx::document::value, pmodel::Metadata>{ document{} << finalize }; remove.database = "unit"; remove.collection = "test"; remove.application = "unitTest"; remove.options = options::Delete{}; remove.options->collation = options::Collation{}; remove.options->collation->locale = "en"; remove.options->collation->strength = 1; remove.options->hint = document{} << "name" << 1 << finalize; remove.options->writeConcern = options::WriteConcern{}; remove.options->writeConcern->tag = "test"; remove.options->writeConcern->majority = std::chrono::milliseconds{ 100 }; remove.options->writeConcern->timeout = std::chrono::milliseconds{ 250 }; remove.options->writeConcern->nodes = 2; remove.options->writeConcern->acknowledgeLevel = options::WriteConcern::Level::Majority; remove.options->writeConcern->journal = true; remove.options->let = document{} << "vars" << open_document << "total" << open_document << "$add" << open_array << "$price" << "$tax" << close_array << close_document << "discounted" << open_document << "$cond" << open_document << "if" << "$applyDiscount" << "then" << 0.9 << "else" << 1 << close_document << close_document << close_document << "in" << open_document << "$multiply" << open_array << "$$total" << "$$discounted" << close_array << close_document << finalize; builder.addRemove( remove ); const auto bson = builder.build(); auto result = repository::transaction( bson ); if ( !result.has_value() ) { LOG_WARN << "Error executing transaction. " << magic_enum::enum_name( result.error().cause ) << ". " << result.error().message; } }
Last modified: 18 February 2025