Skip to content

Commit f92baa3

Browse files
committed
Add ability to get path to modified collections in object notifications
1 parent 291bf9a commit f92baa3

24 files changed

+209
-53
lines changed

src/realm.h

+21
Original file line numberDiff line numberDiff line change
@@ -1953,6 +1953,13 @@ RLM_API bool realm_object_changes_is_deleted(const realm_object_changes_t*);
19531953
*/
19541954
RLM_API size_t realm_object_changes_get_num_modified_properties(const realm_object_changes_t*);
19551955

1956+
/**
1957+
* Get the number of paths to embedded collections that were modified.
1958+
*
1959+
* This function cannot fail.
1960+
*/
1961+
RLM_API size_t realm_object_changes_get_num_modified_paths(const realm_object_changes_t*);
1962+
19561963
/**
19571964
* Get the column keys for the properties that were modified in an object
19581965
* notification.
@@ -1967,6 +1974,20 @@ RLM_API size_t realm_object_changes_get_num_modified_properties(const realm_obje
19671974
RLM_API size_t realm_object_changes_get_modified_properties(const realm_object_changes_t*,
19681975
realm_property_key_t* out_modified, size_t max);
19691976

1977+
/**
1978+
* Get the column keys for the properties that were modified in an object
1979+
* notification.
1980+
*
1981+
* This function cannot fail.
1982+
*
1983+
* @param out_modified Where the paths should be written. May be NULL.
1984+
* @param max The maximum number of paths to write.
1985+
* @return The number of paths written to @a out_modified, or the number
1986+
* of modified paths if @a out_modified is NULL.
1987+
*/
1988+
RLM_API size_t realm_object_changes_get_modified_paths(const realm_object_changes_t*, realm_string_t* out_modified,
1989+
size_t max);
1990+
19701991
/**
19711992
* Get the number of various types of changes in a collection notification.
19721993
*

src/realm/collection.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class DummyParent : public CollectionParent {
3939
{
4040
return {};
4141
}
42+
void translate_path(const StablePath&, Path&) const final {}
4243
void add_index(Path&, const Index&) const noexcept final {}
4344
size_t find_index(const Index&) const noexcept final
4445
{

src/realm/collection_parent.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ class CollectionParent : public std::enable_shared_from_this<CollectionParent> {
9595
/// Get table of owning object
9696
virtual TableRef get_table() const noexcept = 0;
9797

98+
virtual void translate_path(const StablePath&, Path&) const = 0;
99+
98100
protected:
99101
friend class Collection;
100102
template <class>

src/realm/dictionary.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,28 @@ Dictionary::Iterator Dictionary::find(Mixed key) const noexcept
638638
return end();
639639
}
640640

641+
642+
void Dictionary::translate_path(const StablePath& stable_path, Path& path) const
643+
{
644+
auto& index = stable_path[m_level];
645+
auto ndx = find_index(index);
646+
StringData key = do_get_key(ndx).get_string();
647+
path.emplace_back(key);
648+
if (stable_path.size() > m_level + 1) {
649+
Mixed val = do_get(ndx);
650+
if (val.is_type(type_Dictionary)) {
651+
DummyParent parent(this->get_table(), val.get_ref());
652+
Dictionary dict(parent, 0);
653+
dict.translate_path(stable_path, path);
654+
}
655+
else if (val.is_type(type_List)) {
656+
DummyParent parent(this->get_table(), val.get_ref());
657+
Lst<Mixed> list(parent, 0);
658+
list.translate_path(stable_path, path);
659+
}
660+
}
661+
}
662+
641663
void Dictionary::add_index(Path& path, const Index& index) const
642664
{
643665
auto ndx = m_values->find_key(index.get_salt());

src/realm/dictionary.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ class Dictionary final : public CollectionBaseImpl<DictionaryBase>, public Colle
205205
{
206206
return Base::get_stable_path();
207207
}
208+
void translate_path(const StablePath& stable_path, Path& path) const final;
208209

209210
void add_index(Path& path, const Index& ndx) const final;
210211
size_t find_index(const Index&) const final;

src/realm/list.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,26 @@ void Lst<Mixed>::set_collection_ref(Index index, ref_type ref, CollectionType ty
790790
m_tree->set(ndx, Mixed(ref, type));
791791
}
792792

793+
void Lst<Mixed>::translate_path(const StablePath& stable_path, Path& path) const
794+
{
795+
auto& index = stable_path[m_level];
796+
auto ndx = find_index(index);
797+
path.emplace_back(ndx);
798+
if (stable_path.size() > m_level + 1) {
799+
Mixed val = get(ndx);
800+
if (val.is_type(type_Dictionary)) {
801+
DummyParent parent(this->get_table(), val.get_ref());
802+
Dictionary dict(parent, 0);
803+
dict.translate_path(stable_path, path);
804+
}
805+
else if (val.is_type(type_List)) {
806+
DummyParent parent(this->get_table(), val.get_ref());
807+
Lst<Mixed> list(parent, 0);
808+
list.translate_path(stable_path, path);
809+
}
810+
}
811+
}
812+
793813
void Lst<Mixed>::add_index(Path& path, const Index& index) const
794814
{
795815
auto ndx = m_tree->find_key(index.get_salt());

src/realm/list.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ class Lst<Mixed> final : public CollectionBaseImpl<LstBase>, public CollectionPa
484484
{
485485
return Base::get_stable_path();
486486
}
487+
void translate_path(const StablePath& stable_path, Path& path) const final;
487488

488489
ColKey get_col_key() const noexcept override
489490
{

src/realm/obj.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -2079,6 +2079,16 @@ CollectionPtr Obj::get_collection_ptr(const Path& path) const
20792079
return collection;
20802080
}
20812081

2082+
void Obj::translate_path(const StablePath& stable_path, Path& path) const
2083+
{
2084+
ColKey col_key = m_table->get_column_key(stable_path[0]);
2085+
path.emplace_back(m_table->get_column_name(col_key));
2086+
if (stable_path.size() > 1) {
2087+
CollectionBasePtr collection = get_collection_ptr(col_key);
2088+
dynamic_cast<CollectionParent*>(collection.get())->translate_path(stable_path, path);
2089+
}
2090+
}
2091+
20822092
CollectionPtr Obj::get_collection_by_stable_path(const StablePath& path) const
20832093
{
20842094
// First element in path is phony column key

src/realm/obj.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class Obj : public CollectionParent {
7878
Path get_short_path() const noexcept final;
7979
ColKey get_col_key() const noexcept final;
8080
StablePath get_stable_path() const noexcept final;
81+
void translate_path(const StablePath&, Path&) const final;
8182
void add_index(Path& path, const Index& ndx) const final;
8283
size_t find_index(const Index&) const final
8384
{

src/realm/object-store/binding_context.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ class BindingContext {
165165
// Populated with information about which columns were changed
166166
// May be shorter than the actual number of columns if the later columns
167167
// are not modified
168-
std::unordered_map<int64_t, ColumnInfo> changes;
168+
std::unordered_map<ColKey, ColumnInfo> changes;
169169

170170
// Simple lexographic ordering
171171
friend bool operator<(ObserverState const& lft, ObserverState const& rgt)

src/realm/object-store/c_api/notifications.cpp

+38
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ RLM_API size_t realm_object_changes_get_num_modified_properties(const realm_obje
113113
return changes->columns.size();
114114
}
115115

116+
RLM_API size_t realm_object_changes_get_num_modified_paths(const realm_object_changes_t* changes)
117+
{
118+
return changes->modified_paths.size();
119+
}
120+
116121
RLM_API size_t realm_object_changes_get_modified_properties(const realm_object_changes_t* changes,
117122
realm_property_key_t* out_properties, size_t max)
118123
{
@@ -130,6 +135,39 @@ RLM_API size_t realm_object_changes_get_modified_properties(const realm_object_c
130135
return i;
131136
}
132137

138+
RLM_API size_t realm_object_changes_get_modified_paths(const realm_object_changes_t* const_changes,
139+
realm_string_t* out_paths, size_t max)
140+
{
141+
if (!out_paths)
142+
return const_changes->modified_paths.size();
143+
144+
realm_object_changes_t* changes = const_cast<realm_object_changes_t*>(const_changes);
145+
changes->path_buffer.resize(changes->modified_paths.size());
146+
size_t i = 0;
147+
for (const auto& p : changes->modified_paths) {
148+
if (i >= max) {
149+
break;
150+
}
151+
std::string& path = changes->path_buffer[i];
152+
path = p[0].get_key();
153+
for (auto path_elem = p.begin() + 1; path_elem != p.end(); ++path_elem) {
154+
if (path_elem->is_key()) {
155+
path += '.';
156+
path += path_elem->get_key();
157+
}
158+
else {
159+
char buffer[10];
160+
sprintf(buffer, "[%u]", unsigned(path_elem->get_ndx()));
161+
path += buffer;
162+
}
163+
}
164+
out_paths[i].data = path.data();
165+
out_paths[i].size = path.size();
166+
++i;
167+
}
168+
return i;
169+
}
170+
133171
RLM_API realm_notification_token_t* realm_list_add_notification_callback(realm_list_t* list,
134172
realm_userdata_t userdata,
135173
realm_free_userdata_func_t free,

src/realm/object-store/c_api/types.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ struct realm_object_changes : realm::c_api::WrapC, realm::CollectionChangeSet {
428428
{
429429
return new realm_object_changes{static_cast<const realm::CollectionChangeSet&>(*this)};
430430
}
431+
std::vector<std::string> path_buffer;
431432
};
432433

433434
struct realm_collection_changes : realm::c_api::WrapC, realm::CollectionChangeSet {

src/realm/object-store/collection_notifications.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,9 @@ struct CollectionChangeSet {
105105

106106
// Per-column version of `modifications`
107107
std::unordered_map<int64_t, IndexSet> columns;
108+
std::vector<Path> modified_paths;
108109

109-
std::set<StablePath> paths;
110+
std::set<StablePath> stable_paths;
110111

111112
bool empty() const noexcept
112113
{

src/realm/object-store/impl/collection_change_builder.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -704,5 +704,5 @@ CollectionChangeSet CollectionChangeBuilder::finalize() &&
704704

705705
return {std::move(deletions), std::move(insertions), std::move(modifications_in_old),
706706
std::move(modifications), std::move(moves), collection_root_was_deleted,
707-
collection_was_cleared, std::move(columns)};
707+
collection_was_cleared, std::move(columns), std::move(modified_paths)};
708708
}

src/realm/object-store/impl/list_notifier.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,9 @@ void ListNotifier::run()
142142
}
143143
}
144144

145-
if (m_change.paths.size()) {
145+
if (m_change.stable_paths.size()) {
146146
if (auto coll = dynamic_cast<CollectionParent*>(m_list.get())) {
147-
for (auto& p : m_change.paths) {
147+
for (auto& p : m_change.stable_paths) {
148148
// Report changes in substructure as modifications on this list
149149
auto ndx = coll->find_index(p[0]);
150150
if (ndx != realm::not_found)

src/realm/object-store/impl/object_notifier.cpp

+10-4
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,19 @@ void ObjectNotifier::run()
109109

110110
const auto& change = it->second;
111111

112-
auto column_modifications = change.get_columns_modified(m_obj_key);
113-
if (!column_modifications)
112+
auto path_modifications = change.get_paths_modified(m_obj_key);
113+
if (!path_modifications)
114114
return;
115115

116116
// Finally we add all changes to `m_change` which is later used to notify about the changed columns.
117117
m_change.modifications.add(0);
118-
for (auto col : *column_modifications) {
119-
m_change.columns[col.value].add(0);
118+
auto obj = m_table->get_object(m_obj_key);
119+
for (const StablePath& stable_path : *path_modifications) {
120+
m_change.columns[m_table->get_column_key(stable_path[0]).value].add(0);
121+
if (stable_path.size() > 1) {
122+
Path path;
123+
obj.translate_path(stable_path, path);
124+
m_change.modified_paths.emplace_back(std::move(path));
125+
}
120126
}
121127
}

src/realm/object-store/impl/results_notifier.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -393,9 +393,9 @@ void ListResultsNotifier::run()
393393
std::iota(m_run_indices->begin(), m_run_indices->end(), 0);
394394
}
395395

396-
if (m_change.paths.size()) {
396+
if (m_change.stable_paths.size()) {
397397
if (auto coll = dynamic_cast<CollectionParent*>(m_list.get())) {
398-
for (auto& p : m_change.paths) {
398+
for (auto& p : m_change.stable_paths) {
399399
// Report changes in substructure as modifications on this list
400400
auto ndx = coll->find_index(p[0]);
401401
if (ndx != realm::not_found)

src/realm/object-store/impl/transact_log_handler.cpp

+16-11
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,10 @@ void KVOAdapter::before(Transaction& sg)
110110
m_invalidated.push_back(observer.info);
111111
continue;
112112
}
113-
auto column_modifications = table.get_columns_modified(key);
114-
if (column_modifications) {
115-
for (auto col : *column_modifications) {
116-
observer.changes[col.value].kind = BindingContext::ColumnInfo::Kind::Set;
113+
auto tbl = sg.get_table(observer.table_key);
114+
if (auto path_modifications = table.get_paths_modified(key)) {
115+
for (const StablePath& path : *path_modifications) {
116+
observer.changes[tbl->get_column_key(path[0])].kind = BindingContext::ColumnInfo::Kind::Set;
117117
}
118118
}
119119
}
@@ -123,7 +123,7 @@ void KVOAdapter::before(Transaction& sg)
123123
// We may have pre-emptively marked the column as modified if the
124124
// LinkList was selected but the actual changes made ended up being
125125
// a no-op
126-
list.observer->changes.erase(list.col_key.value);
126+
list.observer->changes.erase(list.col_key);
127127
continue;
128128
}
129129
// If the containing row was deleted then changes will be empty
@@ -132,7 +132,7 @@ void KVOAdapter::before(Transaction& sg)
132132
continue;
133133
}
134134
// otherwise the column should have been marked as modified
135-
auto it = list.observer->changes.find(list.col_key.value);
135+
auto it = list.observer->changes.find(list.col_key);
136136
REALM_ASSERT(it != list.observer->changes.end());
137137
auto& builder = list.builder;
138138
auto& changes = it->second;
@@ -364,17 +364,19 @@ class TransactLogObserver : public TransactLogValidationMixin {
364364
return true;
365365
}
366366

367-
bool select_collection(ColKey col, ObjKey obj, const StablePath& path)
367+
bool select_collection(ColKey, ObjKey obj, const StablePath& path)
368368
{
369-
modify_object(col, obj);
369+
if (m_active_table) {
370+
m_active_table->modifications_add(obj, path);
371+
}
370372
auto table = current_table();
371373
for (auto& c : m_info.collections) {
372374

373375
if (c.table_key == table && c.obj_key == obj && c.path.is_prefix_of(path)) {
374376
if (c.path.size() != path.size()) {
375377
StablePath sub_path;
376378
sub_path.insert(sub_path.begin(), path.begin() + c.path.size(), path.end());
377-
c.changes->paths.insert(std::move(sub_path));
379+
c.changes->stable_paths.insert(std::move(sub_path));
378380
}
379381
else {
380382
m_active_collection = c.changes;
@@ -465,8 +467,11 @@ class TransactLogObserver : public TransactLogValidationMixin {
465467

466468
bool modify_object(ColKey col, ObjKey key)
467469
{
468-
if (m_active_table)
469-
m_active_table->modifications_add(key, col);
470+
if (m_active_table) {
471+
StablePath path;
472+
path.push_back(StableIndex(col, 0));
473+
m_active_table->modifications_add(key, path);
474+
}
470475
return true;
471476
}
472477

src/realm/object-store/object_changeset.cpp

+9-5
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ void ObjectChangeSet::insertions_add(ObjKey obj)
2525
m_insertions.insert(obj);
2626
}
2727

28-
void ObjectChangeSet::modifications_add(ObjKey obj, ColKey col)
28+
void ObjectChangeSet::modifications_add(ObjKey obj, const StablePath& path)
2929
{
3030
// don't report modifications on new objects
3131
if (m_insertions.find(obj) == m_insertions.end()) {
32-
m_modifications[obj].insert(col);
32+
m_modifications[obj].insert(path);
3333
}
3434
}
3535

@@ -82,17 +82,21 @@ bool ObjectChangeSet::modifications_contains(ObjKey obj, const std::vector<ColKe
8282
}
8383

8484
// If a filter was set we need to check if the changed column is part of this filter.
85-
const std::unordered_set<ColKey>& changed_columns_for_object = m_modifications.at(obj);
85+
const std::set<StablePath>& changed_paths_for_object = m_modifications.at(obj);
8686
for (const auto& column_key_in_filter : filtered_column_keys) {
87-
if (changed_columns_for_object.count(column_key_in_filter)) {
87+
auto it =
88+
std::find_if(changed_paths_for_object.begin(), changed_paths_for_object.end(), [&](const StablePath& p) {
89+
return p[0].get_index().val == column_key_in_filter.get_index().val;
90+
});
91+
if (it != changed_paths_for_object.end()) {
8892
return true;
8993
}
9094
}
9195

9296
return false;
9397
}
9498

95-
const ObjectChangeSet::ColumnSet* ObjectChangeSet::get_columns_modified(ObjKey obj) const
99+
const ObjectChangeSet::PathSet* ObjectChangeSet::get_paths_modified(ObjKey obj) const
96100
{
97101
auto it = m_modifications.find(obj);
98102
if (it == m_modifications.end()) {

0 commit comments

Comments
 (0)