Rakesh Vidyadharan Help

Update

Update is the most complex scenario. The service supports the two main update modes supported by Mongo:

  • update - The data specified in the document sub-document is merged into the existing document(s).

  • replace - The data specified in the document is used to replace an existing document.

Updates are possible either by an explicit _id field in the input document, or via a filter sub-document that expresses the query used to identify the candidate document(s) to update.

The returned BSON document depends on whether a single-document or multi-document update request was made:

  • Single-Document - For single document updates the full updated stored document (document) and basic information about the associated version history document (history) are returned.

  • Multi-Document - For multi-document updates, an array of BSON object ids for successful updates (success), failed updates (failure), and the basic information about the version history documents (history).

Data models that represents the payload to be submitted to the service for updating document(s). Provides structures for updating a single document by merging, for replacing the stored document, as well as a structure for updating multiple documents. Variants of the structures for owned vs referenced documents are also provided.

// // MergeForIdd by Rakesh on 13/12/2024. // #pragma once #include "action.hpp" #include "../../options/update.hpp" #if defined __has_include #if __has_include("../../../common/util/json.hpp") #include "../../../common/util/json.hpp" #else #include <mongo-service/common/util/json.hpp> #endif #endif #include <bsoncxx/builder/stream/document.hpp> namespace spt::mongoservice::api::model::request { template <util::Visitable Document, util::Visitable Metadata> requires std::is_same_v<decltype(Document::id), bsoncxx::oid> && std::constructible_from<Document, bsoncxx::document::view> struct MergeForId { MergeForId() = default; ~MergeForId() = default; MergeForId(MergeForId&&) = default; MergeForId& operator=(MergeForId&&) = default; MergeForId(const MergeForId&) = delete; MergeForId& operator=(const MergeForId&) = delete; BEGIN_VISITABLES(MergeForId); Document document; VISITABLE(std::optional<Metadata>, metadata); VISITABLE(std::optional<options::Update>, options); std::string database; std::string collection; std::string application; std::string correlationId; Action action{Action::update}; bool skipVersion{false}; bool skipMetric{false}; END_VISITABLES; }; template <util::Visitable Document, util::Visitable Metadata> requires std::is_same_v<decltype(Document::id), bsoncxx::oid> && std::constructible_from<Document, bsoncxx::document::view> struct MergeForIdWithReference { explicit MergeForIdWithReference( const Document& doc ) : document{ std::cref( doc ) } {} MergeForIdWithReference( const Document& doc, const Metadata& md ) : document{ std::cref( doc ) }, metadata{ std::cref( md ) } {} ~MergeForIdWithReference() = default; MergeForIdWithReference(MergeForIdWithReference&&) = default; MergeForIdWithReference& operator=(MergeForIdWithReference&&) = default; MergeForIdWithReference(const MergeForIdWithReference&) = delete; MergeForIdWithReference& operator=(const MergeForIdWithReference&) = delete; BEGIN_VISITABLES(MergeForIdWithReference); std::reference_wrapper<const Document> document; VISITABLE(std::optional<std::reference_wrapper<const Metadata>>, metadata); VISITABLE(std::optional<options::Update>, options); std::string database; std::string collection; std::string application; std::string correlationId; Action action{Action::update}; bool skipVersion{false}; bool skipMetric{false}; END_VISITABLES; }; struct IdFilter { explicit IdFilter( bsoncxx::document::view bson ) { util::unmarshall( *this, bson ); } explicit IdFilter( bsoncxx::oid id ) : id( id ) {} IdFilter() = default; ~IdFilter() = default; IdFilter(IdFilter&&) = default; IdFilter& operator=(IdFilter&&) = default; bool operator==(const IdFilter&) const = default; IdFilter(const IdFilter&) = delete; IdFilter& operator=(const IdFilter&) = delete; BEGIN_VISITABLES(IdFilter); VISITABLE(bsoncxx::oid, id); END_VISITABLES; }; template <util::Visitable Document, util::Visitable Metadata, util::Visitable Filter> requires std::is_same_v<decltype(Document::id), bsoncxx::oid> struct Replace { struct Payload { explicit Payload( bsoncxx::document::view bson ) { util::unmarshall( *this, bson ); } Payload() = default; ~Payload() = default; Payload(Payload&&) = default; Payload& operator=(Payload&&) = default; Payload(const Payload&) = delete; Payload& operator=(const Payload&) = delete; bool operator==(const Payload&) const = default; BEGIN_VISITABLES(Payload); VISITABLE(std::optional<Filter>, filter); VISITABLE(std::optional<Document>, replace); END_VISITABLES; }; Replace() = default; ~Replace() = default; Replace(Replace&&) = default; Replace& operator=(Replace&&) = default; Replace(const Replace&) = delete; Replace& operator=(const Replace&) = delete; BEGIN_VISITABLES(Replace); Payload document; VISITABLE(std::optional<Metadata>, metadata); VISITABLE(std::optional<options::Update>, options); std::string database; std::string collection; std::string application; std::string correlationId; Action action{Action::update}; bool skipVersion{false}; bool skipMetric{false}; END_VISITABLES; }; template <util::Visitable Document, util::Visitable Metadata, util::Visitable Filter> requires std::is_same_v<decltype(Document::id), bsoncxx::oid> struct ReplaceWithReference { struct Payload { Payload( const Filter& filter, const Document& document ) : filter{ std::cref( filter ) }, replace{ std::cref( document ) } {} ~Payload() = default; Payload(Payload&&) = default; Payload& operator=(Payload&&) = default; Payload(const Payload&) = delete; Payload& operator=(const Payload&) = delete; bool operator==(const Payload&) const = default; BEGIN_VISITABLES(Payload); VISITABLE(std::reference_wrapper<const Filter>, filter); VISITABLE(std::reference_wrapper<const Document>, replace); END_VISITABLES; }; ReplaceWithReference( const Filter& filter, const Document& document ) : document{ filter, document } {} ReplaceWithReference( const Filter& filter, const Document& document, const Metadata& metadata ) : document{ filter, document }, metadata{ std::cref( metadata ) } {} ~ReplaceWithReference() = default; ReplaceWithReference(ReplaceWithReference&&) = default; ReplaceWithReference& operator=(ReplaceWithReference&&) = default; ReplaceWithReference(const ReplaceWithReference&) = delete; ReplaceWithReference& operator=(const ReplaceWithReference&) = delete; BEGIN_VISITABLES(ReplaceWithReference); Payload document; VISITABLE(std::optional<std::reference_wrapper<const Metadata>>, metadata); VISITABLE(std::optional<options::Update>, options); std::string database; std::string collection; std::string application; std::string correlationId; Action action{Action::update}; bool skipVersion{false}; bool skipMetric{false}; END_VISITABLES; }; template <util::Visitable Document, util::Visitable Metadata, util::Visitable Filter> struct Update { struct Payload { explicit Payload( bsoncxx::document::view bson ) { util::unmarshall( *this, bson ); } Payload() = default; ~Payload() = default; Payload(Payload&&) = default; Payload& operator=(Payload&&) = default; Payload(const Payload&) = delete; Payload& operator=(const Payload&) = delete; bool operator==(const Payload&) const = default; BEGIN_VISITABLES(Payload); VISITABLE(std::optional<Filter>, filter); VISITABLE(std::optional<Document>, update); END_VISITABLES; }; Update() = default; ~Update() = default; Update(Update&&) = default; Update& operator=(Update&&) = default; Update(const Update&) = delete; Update& operator=(const Update&) = delete; BEGIN_VISITABLES(Update); Payload document; VISITABLE(std::optional<Metadata>, metadata); VISITABLE(std::optional<options::Update>, options); std::string database; std::string collection; std::string application; std::string correlationId; Action action{Action::update}; bool skipVersion{false}; bool skipMetric{false}; END_VISITABLES; }; template <util::Visitable Document, util::Visitable Metadata, util::Visitable Filter> struct UpdateWithReference { struct Payload { Payload( const Filter& filter, const Document& document ): filter{ std::cref( filter ) }, update{ std::cref( document ) } {} ~Payload() = default; Payload(Payload&&) = default; Payload& operator=(Payload&&) = default; Payload(const Payload&) = delete; Payload& operator=(const Payload&) = delete; bool operator==(const Payload&) const = default; BEGIN_VISITABLES(Payload); VISITABLE(std::reference_wrapper<const Filter>, filter); VISITABLE(std::reference_wrapper<const Document>, update); END_VISITABLES; }; UpdateWithReference( const Filter& filter, const Document& document ) : document{ filter, document } {} UpdateWithReference( const Filter& filter, const Document& document, const Metadata& metadata ) : document{ filter, document }, metadata{ std::cref( metadata ) } {} ~UpdateWithReference() = default; UpdateWithReference(UpdateWithReference&&) = default; UpdateWithReference& operator=(UpdateWithReference&&) = default; UpdateWithReference(const UpdateWithReference&) = delete; UpdateWithReference& operator=(const UpdateWithReference&) = delete; BEGIN_VISITABLES(UpdateWithReference); Payload document; VISITABLE(std::optional<std::reference_wrapper<const Metadata>>, metadata); VISITABLE(std::optional<options::Update>, options); std::string database; std::string collection; std::string application; std::string correlationId; Action action{Action::update}; bool skipVersion{false}; bool skipMetric{false}; END_VISITABLES; }; }

Data models that represents the payloads the service responds with when updating document(s).

// // Created by Rakesh on 17/12/2024. // #pragma once #include "history.hpp" namespace spt::mongoservice::api::model::response { template <util::Visitable Document> requires std::is_same_v<decltype(Document::id), bsoncxx::oid> struct Update { explicit Update( bsoncxx::document::view document ) { util::unmarshall( *this, document ); } Update() = default; ~Update() = default; Update(Update&&) = default; Update& operator=(Update&&) = default; Update(const Update&) = delete; Update& operator=(const Update&) = delete; BEGIN_VISITABLES(Update); VISITABLE(std::optional<Document>, document); VISITABLE(std::optional<History>, history); std::optional<bool> skipVersion; END_VISITABLES; }; struct UpdateMany { explicit UpdateMany( bsoncxx::document::view document ) { util::unmarshall( *this, document ); } UpdateMany() = default; ~UpdateMany() = default; UpdateMany(UpdateMany&&) = default; UpdateMany& operator=(UpdateMany&&) = default; UpdateMany(const UpdateMany&) = delete; UpdateMany& operator=(const UpdateMany&) = delete; BEGIN_VISITABLES(UpdateMany); VISITABLE(std::vector<bsoncxx::oid>, success); VISITABLE(std::vector<bsoncxx::oid>, failure); VISITABLE(std::vector<History>, history); END_VISITABLES; }; }

Sample code illustrating the update actions.

#include <mongo-service/api/repository/repository.hpp> #include <log/NanoLog.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; }; struct Query { bool operator==(const Query&) const = default; BEGIN_VISITABLES(Query); VISITABLE(std::string, str); END_VISITABLES; }; } int main() { using namespace spt::mongoservice::api; // merge by id { auto update = model::request::MergeForId<example::Document, example::Metadata>{}; update.database = "test"; update.collection = "test"; update.document.id = bsoncxx::oid{}; update.document.str = "value modified"; update.metadata.emplace(); update.metadata->project = "serialisation"; update.metadata->product = "mongo-service"; update.options.emplace(); update.options->bypassValidation = true; update.options->upsert = true; auto result = repository::update( update ); if ( !result.has_value() ) { LOG_WARN << "Error updating document " << update.document.id << ". " << magic_enum::enum_name( result.error().cause ) << ". " << result.error().message; } } // replace document { auto update = model::request::Replace<example::Document, example::Metadata, model::request::IdFilter>{}; update.database = "test"; update.collection = "test"; update.document.filter.emplace(); update.document.filter->id = oid; update.document.replace = example::Document{}; update.document.replace->id = oid; update.document.replace->str = "value replaced"; update.metadata.emplace(); update.metadata->project = "serialisation"; update.metadata->product = "mongo-service"; update.options.emplace(); update.options->bypassValidation = true; update.options->upsert = true; auto result = repository::update( update ); if ( !result.has_value() ) { LOG_WARN << "Error updating document " << update.document.filter->id << ". " << magic_enum::enum_name( result.error().cause ) << ". " << result.error().message; } } // updating document by id filter { auto update = model::request::Update<example::Document, example::Metadata, model::request::IdFilter>{}; update.database = "test"; update.collection = "test"; update.document.filter.emplace(); update.document.filter->id = oid; update.document.update.emplace(); update.document.update->str = "value modified"; update.options.emplace(); update.options->collation.emplace(); update.options->collation->locale = "en"; update.options->collation->strength = 1; auto result = repository::update( update ); if ( !result.has_value() ) { LOG_WARN << "Error updating document " << update.document.filter->id << ". " << magic_enum::enum_name( result.error().cause ) << ". " << result.error().message; } } // updating documents by property { auto update = model::request::Update<example::Document, example::Metadata, example::Query>{}; update.database = "test"; update.collection = "test"; update.document.filter = example::Query{ .str = "value modified" }; update.document.update = example::Document{}; update.document.update->str = "value modified update"; update.options = options::Update{}; update.options->collation = options::Collation{}; update.options->collation->locale = "en"; update.options->collation->strength = 1; auto result = repository::updateMany( update ); if ( !result.has_value() ) { LOG_WARN << "Error updating document " << update.document.filter->id << ". " << magic_enum::enum_name( result.error().cause ) << ". " << result.error().message; } } }
Last modified: 18 February 2025