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;
}
}
}