Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support assigning nested collections via templated API #7478

Merged
merged 14 commits into from
Apr 25, 2024
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Enhancements
* Introduce sync 'progress_estimate' parameter (value from 0.0 to 1.0) for existing sync 'ProgressNotifierCallback' api to report sync progress on current batch of upload/download until completion ([#7450](https://github.com/realm/realm-core/issues/7450))
* Support assigning nested collections via templated API (PR [#7478](https://github.com/realm/realm-core/pull/7478))

### Fixed
* <How do the end-user experience this issue? what was the impact?> ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?)
Expand Down
4 changes: 0 additions & 4 deletions src/realm/collection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,6 @@ UpdateStatus CollectionBase::do_init_from_parent(BPlusTreeBase* tree, ref_type r
tree->init_from_ref(ref);
}
else {
if (tree->init_from_parent()) {
// All is well
return UpdateStatus::Updated;
}
if (!allow_create) {
tree->detach();
return UpdateStatus::Detached;
Expand Down
2 changes: 1 addition & 1 deletion src/realm/list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ class Lst final : public CollectionBaseImpl<LstBase> {
m_tree->set_parent(const_cast<ArrayParent*>(parent), 0);
}
Base::update_content_version();
return do_init_from_parent(m_tree.get(), 0, allow_create);
return do_init_from_parent(m_tree.get(), Base::get_collection_ref(), allow_create);
}

template <class Func>
Expand Down
56 changes: 0 additions & 56 deletions src/realm/object-store/dictionary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ class Dictionary : public object_store::Collection {
Obj get_object(StringData key) const;
};


template <typename Fn>
auto Dictionary::dispatch(Fn&& fn) const
{
Expand Down Expand Up @@ -178,62 +177,7 @@ inline Obj Dictionary::get<Obj>(StringData key) const
return get_object(key);
}

template <typename T, typename Context>
void Dictionary::insert(Context& ctx, StringData key, T&& value, CreatePolicy policy)
{
if (ctx.is_null(value)) {
this->insert(key, Mixed());
return;
}
if (m_is_embedded) {
validate_embedded(ctx, value, policy);
auto obj_key = dict().create_and_insert_linked_object(key).get_key();
ctx.template unbox<Obj>(value, policy, obj_key);
return;
}
dispatch([&](auto t) {
this->insert(key, ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
});
}

template <typename Context>
auto Dictionary::get(Context& ctx, StringData key) const
{
return dispatch([&](auto t) {
return ctx.box(this->get<std::decay_t<decltype(*t)>>(key));
});
}

template <typename T, typename Context>
void Dictionary::assign(Context& ctx, T&& values, CreatePolicy policy)
{
if (ctx.is_same_dictionary(*this, values))
return;

if (ctx.is_null(values)) {
remove_all();
return;
}

if (!policy.diff)
remove_all();

ctx.enumerate_dictionary(values, [&](StringData key, auto&& value) {
if (policy.diff) {
util::Optional<Mixed> old_value = dict().try_get(key);
auto new_value = ctx.template unbox<Mixed>(value);
if (!old_value || *old_value != new_value) {
dict().insert(key, new_value);
}
}
else {
this->insert(ctx, key, value, policy);
}
});
}

} // namespace object_store
} // namespace realm


#endif /* REALM_OS_DICTIONARY_HPP */
24 changes: 23 additions & 1 deletion src/realm/object-store/impl/object_accessor_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ namespace realm {
using AnyDict = std::map<std::string, std::any>;
using AnyVector = std::vector<std::any>;

struct UnmanagedObject {
std::string object_type;
std::any properties;
};

// An object accessor context which can be used to create and access objects
// using std::any as the type-erased value type. In addition, this serves as
// the reference implementation of an accessor context that must be implemented
Expand Down Expand Up @@ -370,7 +375,7 @@ inline util::Optional<UUID> CppContext::unbox(std::any& v, CreatePolicy, ObjKey)
}

template <>
inline Mixed CppContext::unbox(std::any& v, CreatePolicy, ObjKey) const
inline Mixed CppContext::unbox(std::any& v, CreatePolicy policy, ObjKey) const
{
if (v.has_value()) {
const std::type_info& this_type{v.type()};
Expand Down Expand Up @@ -408,6 +413,23 @@ inline Mixed CppContext::unbox(std::any& v, CreatePolicy, ObjKey) const
else if (this_type == typeid(UUID)) {
return Mixed(util::any_cast<UUID>(v));
}
else if (this_type == typeid(AnyDict)) {
return Mixed(0, CollectionType::Dictionary);
}
else if (this_type == typeid(AnyVector)) {
return Mixed(0, CollectionType::List);
}
else if (this_type == typeid(UnmanagedObject)) {
UnmanagedObject unmanaged_obj = util::any_cast<UnmanagedObject>(v);
auto os = realm->schema().find(unmanaged_obj.object_type);
CppContext child_ctx(realm, &*os);
auto obj = child_ctx.unbox<Obj>(unmanaged_obj.properties, policy, ObjKey());
return Mixed(obj);
}
else if (this_type == typeid(Obj)) {
Obj obj = util::any_cast<Obj>(v);
return Mixed(obj);
}
}
return Mixed{};
}
Expand Down
117 changes: 0 additions & 117 deletions src/realm/object-store/list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,6 @@ class List : public object_store::Collection {
template <typename T>
auto& as() const;

template <typename T, typename Context>
void set_if_different(Context&, size_t row_ndx, T&& value, CreatePolicy);

friend struct std::hash<List>;
};

Expand Down Expand Up @@ -164,120 +161,6 @@ auto List::dispatch(Fn&& fn) const
return switch_on_type(get_type(), std::forward<Fn>(fn));
}

template <typename Context>
auto List::get(Context& ctx, size_t row_ndx) const
{
return dispatch([&](auto t) {
return ctx.box(this->get<std::decay_t<decltype(*t)>>(row_ndx));
});
}

template <typename T, typename Context>
size_t List::find(Context& ctx, T&& value) const
{
return dispatch([&](auto t) {
return this->find(ctx.template unbox<std::decay_t<decltype(*t)>>(value, CreatePolicy::Skip));
});
}

template <typename T, typename Context>
void List::add(Context& ctx, T&& value, CreatePolicy policy)
{
if (m_is_embedded) {
validate_embedded(ctx, value, policy);
auto key = as<Obj>().create_and_insert_linked_object(size()).get_key();
ctx.template unbox<Obj>(value, policy, key);
return;
}
dispatch([&](auto t) {
this->add(ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
});
}

template <typename T, typename Context>
void List::insert(Context& ctx, size_t list_ndx, T&& value, CreatePolicy policy)
{
if (m_is_embedded) {
validate_embedded(ctx, value, policy);
auto key = as<Obj>().create_and_insert_linked_object(list_ndx).get_key();
ctx.template unbox<Obj>(value, policy, key);
return;
}
dispatch([&](auto t) {
this->insert(list_ndx, ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
});
}

template <typename T, typename Context>
void List::set(Context& ctx, size_t list_ndx, T&& value, CreatePolicy policy)
{
if (m_is_embedded) {
validate_embedded(ctx, value, policy);

auto& list = as<Obj>();
auto key = policy.diff ? list.get(list_ndx) : list.create_and_set_linked_object(list_ndx).get_key();
ctx.template unbox<Obj>(value, policy, key);
return;
}
dispatch([&](auto t) {
this->set(list_ndx, ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
});
}

template <typename T, typename Context>
void List::set_if_different(Context& ctx, size_t row_ndx, T&& value, CreatePolicy policy)
{
if (m_is_embedded) {
validate_embedded(ctx, value, policy);
auto key = policy.diff ? this->get<Obj>(row_ndx) : as<Obj>().create_and_set_linked_object(row_ndx);
ctx.template unbox<Obj>(value, policy, key.get_key());
return;
}
dispatch([&](auto t) {
using U = std::decay_t<decltype(*t)>;
if constexpr (std::is_same_v<U, Obj>) {
auto old_value = this->get<U>(row_ndx);
auto new_value = ctx.template unbox<U>(value, policy, old_value.get_key());
if (new_value.get_key() != old_value.get_key())
this->set(row_ndx, new_value);
}
else {
auto old_value = this->get<U>(row_ndx);
auto new_value = ctx.template unbox<U>(value, policy);
if (old_value != new_value)
this->set(row_ndx, new_value);
}
});
}

template <typename T, typename Context>
void List::assign(Context& ctx, T&& values, CreatePolicy policy)
{
if (ctx.is_same_list(*this, values))
return;

if (ctx.is_null(values)) {
remove_all();
return;
}

if (!policy.diff)
remove_all();

size_t sz = size();
size_t index = 0;
ctx.enumerate_collection(values, [&](auto&& element) {
if (index >= sz)
this->add(ctx, element, policy);
else if (policy.diff)
this->set_if_different(ctx, index, element, policy);
else
this->set(ctx, index, element, policy);
index++;
});
while (index < sz)
remove(--sz);
}
} // namespace realm

namespace std {
Expand Down
Loading
Loading