10#include <unordered_map>
11#include <unordered_set>
22namespace morph::backend::detail {
30inline constexpr std::size_t kCoalesceKeyLimit = 256;
48class ChangeCoalescer {
52 void add(::morph::effects::Changes
const& changes) {
53 for (
auto const& change : changes.
records) {
56 for (
auto const& change : changes.
instances) {
57 addInstance(change.modelType, change.primary, change.instance);
63 void add(::morph::wire::ChangesBody
const& body) {
64 for (
auto const& change : body.
records) {
67 for (
auto const& topic : body.
instances) {
68 addInstance(topic.modelType, topic.primary, topic.instance);
78 [[nodiscard]] std::size_t keyCount()
const {
return _keyCount; }
82 [[nodiscard]]
bool empty()
const {
return _records.empty() && _instances.empty(); }
87 [[nodiscard]] ::morph::wire::ChangesBody take() {
88 ::morph::wire::ChangesBody body;
89 body.
records.reserve(_records.size());
90 for (
auto& entry : _records) {
91 ::morph::effects::RecordChange change{.record = std::move(entry.record), .keys = std::nullopt};
93 change.
keys = std::move(entry.keys);
95 body.
records.push_back(std::move(change));
101 _seenInstances.clear();
111 std::vector<glz::raw_json> keys;
112 std::unordered_set<std::string> seen;
115 void addRecord(::morph::effects::RecordChange
const& change) {
116 auto [found, inserted] = _recordIndex.try_emplace(change.
record, _records.size());
118 _records.push_back(Entry{.record = change.
record, .whole =
false, .keys = {}, .seen = {}});
120 Entry& entry = _records.at(found->second);
128 for (
auto const& key : *change.
keys) {
129 if (entry.seen.insert(key.str).second) {
130 entry.keys.push_back(key);
134 if (entry.keys.size() > ::morph::effects::kMaxChangeKeys) {
139 void makeWhole(Entry& entry) {
140 _keyCount -= entry.keys.size();
147 void addInstance(std::string_view modelType, std::string_view primary, std::uint64_t instance) {
148 if (_seenInstances.emplace(std::string{modelType}, std::string{primary}, instance).second) {
149 _instances.push_back(::morph::wire::InstanceTopic{
150 .modelType = std::string{modelType}, .primary = std::string{primary}, .instance = instance});
155 std::vector<Entry> _records;
156 std::unordered_map<std::string, std::size_t> _recordIndex;
157 std::vector<::morph::wire::InstanceTopic> _instances;
158 std::set<std::tuple<std::string, std::string, std::uint64_t>> _seenInstances;
159 std::size_t _keyCount{0};
std::vector< RecordChange > records
Record topics, one entry per record type.
std::vector< InstanceChange > instances
Instance topics.
std::string record
The record's wire name.
std::optional< std::vector< glz::raw_json > > keys
The keys of the rows that changed, each as raw JSON; absent when the whole type changed.
std::vector< effects::RecordChange > records
Record changes; a record with no keys changed as a whole.
std::vector< InstanceTopic > instances
Instance changes.