Skip to content

Commit

Permalink
Virtual interface for getting nested collections
Browse files Browse the repository at this point in the history
  • Loading branch information
jedelbo committed May 13, 2023
1 parent e638686 commit 460d5ab
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 77 deletions.
9 changes: 9 additions & 0 deletions src/realm/collection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,15 @@ class CollectionBase : public Collection {
return ndx;
}

virtual DictionaryPtr get_dictionary(const PathElement&) const
{
return nullptr;
}
virtual ListMixedPtr get_list(const PathElement&) const
{
return nullptr;
}

virtual void set_owner(const Obj& obj, ColKey) = 0;
virtual void set_owner(std::shared_ptr<CollectionParent> parent, CollectionParent::Index index) = 0;

Expand Down
4 changes: 4 additions & 0 deletions src/realm/collection_parent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,15 @@ class LstBase;
class SetBase;
class Dictionary;

template <class T>
class Lst;

using CollectionPtr = std::shared_ptr<Collection>;
using LstBasePtr = std::unique_ptr<LstBase>;
using SetBasePtr = std::unique_ptr<SetBase>;
using CollectionBasePtr = std::shared_ptr<CollectionBase>;
using CollectionListPtr = std::shared_ptr<CollectionList>;
using ListMixedPtr = std::shared_ptr<Lst<Mixed>>;
using DictionaryPtr = std::shared_ptr<Dictionary>;

/// The status of an accessor after a call to `update_if_needed()`.
Expand Down
14 changes: 6 additions & 8 deletions src/realm/dictionary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,13 +430,12 @@ void Dictionary::insert_dictionary(StringData key)
insert(key, Mixed(0, CollectionType::Dictionary));
}

DictionaryPtr Dictionary::get_dictionary(StringData key) const
DictionaryPtr Dictionary::get_dictionary(const PathElement& path_elem) const
{
auto weak = const_cast<Dictionary*>(this)->weak_from_this();
REALM_ASSERT(!weak.expired());
auto shared = weak.lock();
auto shared = weak.expired() ? std::make_shared<Dictionary>(*this) : weak.lock();
DictionaryPtr ret = std::make_shared<Dictionary>(m_col_key, get_level() + 1);
ret->set_owner(shared, key);
ret->set_owner(shared, path_elem.get_key());
return ret;
}

Expand All @@ -445,13 +444,12 @@ void Dictionary::insert_list(StringData key)
insert(key, Mixed(0, CollectionType::List));
}

std::shared_ptr<Lst<Mixed>> Dictionary::get_list(StringData key) const
std::shared_ptr<Lst<Mixed>> Dictionary::get_list(const PathElement& path_elem) const
{
auto weak = const_cast<Dictionary*>(this)->weak_from_this();
REALM_ASSERT(!weak.expired());
auto shared = weak.lock();
auto shared = weak.expired() ? std::make_shared<Dictionary>(*this) : weak.lock();
std::shared_ptr<Lst<Mixed>> ret = std::make_shared<Lst<Mixed>>(m_col_key, get_level() + 1);
ret->set_owner(shared, key);
ret->set_owner(shared, path_elem.get_key());
return ret;
}

Expand Down
4 changes: 2 additions & 2 deletions src/realm/dictionary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ class Dictionary final : public CollectionBaseImpl<DictionaryBase>, public Colle
Obj create_and_insert_linked_object(Mixed key);

void insert_dictionary(StringData key);
DictionaryPtr get_dictionary(StringData key) const;
DictionaryPtr get_dictionary(const PathElement& path_elem) const override;
void insert_list(StringData key);
std::shared_ptr<Lst<Mixed>> get_list(StringData key) const;
ListMixedPtr get_list(const PathElement& path_elem) const override;

// throws std::out_of_range if key is not found
Mixed get(Mixed key) const;
Expand Down
14 changes: 6 additions & 8 deletions src/realm/list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -426,13 +426,12 @@ void Lst<Mixed>::insert_dictionary(size_t ndx)
m_tree->set_key(ndx, key);
}

DictionaryPtr Lst<Mixed>::get_dictionary(size_t ndx) const
DictionaryPtr Lst<Mixed>::get_dictionary(const PathElement& path_elem) const
{
auto weak = const_cast<Lst<Mixed>*>(this)->weak_from_this();
REALM_ASSERT(!weak.expired());
auto shared = weak.lock();
auto shared = weak.expired() ? std::make_shared<Lst<Mixed>>(*this) : weak.lock();
DictionaryPtr ret = std::make_shared<Dictionary>(m_col_key, get_level() + 1);
ret->set_owner(shared, m_tree->get_key(ndx));
ret->set_owner(shared, m_tree->get_key(path_elem.get_ndx()));
return ret;
}

Expand All @@ -447,13 +446,12 @@ void Lst<Mixed>::insert_list(size_t ndx)
m_tree->set_key(ndx, key);
}

std::shared_ptr<Lst<Mixed>> Lst<Mixed>::get_list(size_t ndx) const
std::shared_ptr<Lst<Mixed>> Lst<Mixed>::get_list(const PathElement& path_elem) const
{
auto weak = const_cast<Lst<Mixed>*>(this)->weak_from_this();
REALM_ASSERT(!weak.expired());
auto shared = weak.lock();
auto shared = weak.expired() ? std::make_shared<Lst<Mixed>>(*this) : weak.lock();
std::shared_ptr<Lst<Mixed>> ret = std::make_shared<Lst<Mixed>>(m_col_key, get_level() + 1);
ret->set_owner(shared, m_tree->get_key(ndx));
ret->set_owner(shared, m_tree->get_key(path_elem.get_ndx()));
return ret;
}

Expand Down
4 changes: 2 additions & 2 deletions src/realm/list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,9 @@ class Lst<Mixed> final : public CollectionBaseImpl<LstBase>, public CollectionPa
Mixed remove(size_t ndx);

void insert_dictionary(size_t ndx);
DictionaryPtr get_dictionary(size_t ndx) const;
DictionaryPtr get_dictionary(const PathElement& path_elem) const override;
void insert_list(size_t ndx);
std::shared_ptr<Lst<Mixed>> get_list(size_t ndx) const;
ListMixedPtr get_list(const PathElement& path_elem) const override;

// Overriding members of CollectionBase:
size_t size() const final
Expand Down
131 changes: 82 additions & 49 deletions src/realm/obj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1921,23 +1921,27 @@ Dictionary Obj::get_dictionary(ColKey col_key) const
return Dictionary(Obj(*this), col_key);
}

void Obj::set_list(ColKey col_key)
void Obj::set_collection(ColKey col_key, CollectionType type)
{
REALM_ASSERT(col_key.get_type() == col_type_Mixed);
update_if_needed();
auto old_val = get<Mixed>(col_key);
if (!old_val.is_type(type_List)) {
set(col_key, Mixed(0, CollectionType::List));
}
}

void Obj::set_dictionary(ColKey col_key)
{
REALM_ASSERT(col_key.get_type() == col_type_Mixed);
update_if_needed();
auto old_val = get<Mixed>(col_key);
if (!old_val.is_type(type_Dictionary)) {
set(col_key, Mixed(ref_type(0), CollectionType::Dictionary));
switch (type) {
case CollectionType::Set:
if (!old_val.is_type(type_Set)) {
set(col_key, Mixed(0, CollectionType::Set));
}
break;
case CollectionType::List:
if (!old_val.is_type(type_List)) {
set(col_key, Mixed(0, CollectionType::List));
}
break;
case CollectionType::Dictionary:
if (!old_val.is_type(type_Dictionary)) {
set(col_key, Mixed(ref_type(0), CollectionType::Dictionary));
}
break;
}
}

Expand Down Expand Up @@ -1971,50 +1975,79 @@ CollectionPtr Obj::get_collection_ptr(const Path& path) const
auto col_key = m_table->get_column_key(path[0].get_key());
REALM_ASSERT(col_key);
size_t nesting_levels = m_table->get_nesting_levels(col_key);
if (nesting_levels == 0) {
return get_collection_ptr(col_key);
}
CollectionListPtr list = get_collection_list(col_key);
if (path.size() > 1) {
size_t levels_left = path.size() - 2;
for (size_t level = 1; level < path.size(); level++) {
CollectionListPtr list;
size_t level = 0;
while (nesting_levels > 0) {
if (!list) {
list = get_collection_list(col_key);
}
else {
if (level == path.size()) {
return list;
}
auto& path_elem = path[level];
if (levels_left == 0) {
if (list->get_collection_type() == CollectionType::List) {
// if list and index is one past last,'
// then we can insert automatically
if (path_elem.get_ndx() == list->size()) {
list->insert_collection(path_elem);
}
}
else {
// If dictionary, inserting an already
// existing element is idempotent
list->insert_collection(path_elem);
if (list->get_collection_type() == CollectionType::List) {
// if list and index is one past last,'
// then we can insert automatically
if (path_elem.get_ndx() == list->size()) {
list->insert_collection_list(path_elem);
}
return list->get_collection(path_elem);
}
else {
if (list->get_collection_type() == CollectionType::List) {
// if list and index is one past last,'
// then we can insert automatically
if (path_elem.get_ndx() == list->size()) {
list->insert_collection_list(path_elem);
}
}
else {
// If dictionary, inserting an already
// existing element is idempotent
list->insert_collection_list(path_elem);
}
list = list->get_collection_list(path_elem);
// If dictionary, inserting an already
// existing element is idempotent
list->insert_collection_list(path_elem);
}
list = list->get_collection_list(path_elem);
}
level++;
nesting_levels--;
}
CollectionBasePtr collection;
if (list) {
if (level == path.size()) {
return list;
}
auto& path_elem = path[level];
if (list->get_collection_type() == CollectionType::List) {
// if list and index is one past last,'
// then we can insert automatically
if (path_elem.get_ndx() == list->size()) {
list->insert_collection(path_elem);
}
levels_left--;
}
else {
// If dictionary, inserting an already
// existing element is idempotent
list->insert_collection(path_elem);
}
collection = list->get_collection(path_elem);
}
else {
collection = get_collection_ptr(col_key);
}

// Return intermediate collection
return list;
level++;

while (level < path.size()) {
auto& path_elem = path[level];
Mixed ref;
if (collection->get_collection_type() == CollectionType::List) {
ref = collection->get_any(path_elem.get_ndx());
}
else {
ref = dynamic_cast<Dictionary*>(collection.get())->get(path_elem.get_key());
}
if (ref.is_type(type_List)) {
collection = collection->get_list(path_elem);
}
else if (ref.is_type(type_Dictionary)) {
collection = collection->get_dictionary(path_elem);
}
level++;
}

return collection;
}

CollectionBasePtr Obj::get_collection_ptr(ColKey col_key) const
Expand Down
3 changes: 1 addition & 2 deletions src/realm/obj.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,6 @@ class Obj : public CollectionParent {

template <typename U>
Lst<U> get_list(ColKey col_key) const;
void set_list(ColKey col_key);
template <typename U>
LstPtr<U> get_list_ptr(ColKey col_key) const;
template <typename U>
Expand Down Expand Up @@ -298,7 +297,7 @@ class Obj : public CollectionParent {
Dictionary get_dictionary(ColKey col_key) const;
Dictionary get_dictionary(StringData col_name) const;

void set_dictionary(ColKey col_key);
void set_collection(ColKey col_key, CollectionType type);
DictionaryPtr get_dictionary_ptr(ColKey col_key) const;
DictionaryPtr get_dictionary_ptr(const Path& path) const;

Expand Down
17 changes: 11 additions & 6 deletions test/test_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ TEST(List_Nested_InMixed)

Obj obj = table->create_object();

obj.set_dictionary(col_any);
obj.set_collection(col_any, CollectionType::Dictionary);
auto dict = obj.get_dictionary_ptr(col_any);
CHECK(dict->is_empty());
dict->insert("Four", 4);
Expand Down Expand Up @@ -795,10 +795,12 @@ TEST(List_Nested_InMixed)

tr->promote_to_write();
dict2->insert_list("List");
auto list = dict2->get_list("List");
CHECK(list->is_empty());
list->add(8);
list->add(9);
{
auto list = dict2->get_list("List");
CHECK(list->is_empty());
list->add(8);
list->add(9);
}
tr->verify();
{
std::stringstream ss;
Expand Down Expand Up @@ -827,14 +829,17 @@ TEST(List_Nested_InMixed)
}
*/

auto list = obj.get_collection_ptr({"something", "Dict", "List"});
CHECK_EQUAL(dynamic_cast<Lst<Mixed>*>(list.get())->get(0).get_int(), 8);

tr->promote_to_write();
// Assign another value. The old dictionary should be disposed.
obj.set(col_any, Mixed(5));
tr->verify();
tr->commit_and_continue_as_read();

tr->promote_to_write();
obj.set_list(col_any);
obj.set_collection(col_any, CollectionType::List);
auto list2 = std::dynamic_pointer_cast<Lst<Mixed>>(obj.get_collection_ptr(col_any));
CHECK(list2->is_empty());
list2->add("Hello");
Expand Down

0 comments on commit 460d5ab

Please sign in to comment.