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

Add ability to get collections from Results #6948

Merged
merged 2 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/realm.h
Original file line number Diff line number Diff line change
Expand Up @@ -2654,6 +2654,24 @@ RLM_API realm_results_t* realm_results_limit(realm_results_t* results, size_t ma
*/
RLM_API bool realm_results_get(realm_results_t*, size_t index, realm_value_t* out_value);

/**
* Returns an instance of realm_list at the index passed as argument.
* @return A valid ptr to a list instance or nullptr in case of errors
*/
RLM_API realm_list_t* realm_results_get_list(realm_results_t*, size_t index);

/**
* Returns an instance of realm_set_t for the index passed as argument.
* @return A valid ptr to a set instance or nullptr in case of errors
*/
RLM_API realm_set_t* realm_results_get_set(realm_results_t*, size_t index);

/**
* Returns an instance of realm_dictionary for the index passed as argument.
* @return A valid ptr to a dictionary instance or nullptr in case of errors
*/
RLM_API realm_dictionary_t* realm_results_get_dictionary(realm_results_t*, size_t index);

/**
* Find the index for the value passed as parameter inside realm results pointer passed a input parameter.
* @param value the value to find inside the realm results
Expand Down
5 changes: 5 additions & 0 deletions src/realm/collection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ class CollectionBase : public Collection {
/// Get the column key for this collection.
virtual ColKey get_col_key() const noexcept = 0;

virtual PathElement get_path_element(size_t ndx) const
{
return PathElement(ndx);
}

/// Return true if the collection has changed since the last call to
/// `has_changed()`. Note that this function is not idempotent and updates
/// the internal state of the accessor if it has changed.
Expand Down
4 changes: 4 additions & 0 deletions src/realm/dictionary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ class Dictionary final : public CollectionBaseImpl<DictionaryBase>, public Colle

std::pair<Mixed, Mixed> get_pair(size_t ndx) const;
Mixed get_key(size_t ndx) const;
PathElement get_path_element(size_t ndx) const override
{
return {get_key(ndx).get_string()};
}

// Overriding members of CollectionBase:
CollectionBasePtr clone_collection() const final;
Expand Down
33 changes: 33 additions & 0 deletions src/realm/object-store/c_api/query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,39 @@ RLM_API bool realm_results_get(realm_results_t* results, size_t index, realm_val
});
}

RLM_API realm_list_t* realm_results_get_list(realm_results_t* results, size_t index)
{
return wrap_err([&]() {
realm_list_t* out = nullptr;
auto result_list = results->get_list(index);
if (result_list.is_valid())
out = new realm_list_t{result_list};
return out;
});
}

RLM_API realm_set_t* realm_results_get_set(realm_results_t* results, size_t index)
{
return wrap_err([&]() {
realm_set_t* out = nullptr;
auto result_set = results->get_set(index);
if (result_set.is_valid())
out = new realm_set_t{result_set};
return out;
});
}

RLM_API realm_dictionary_t* realm_results_get_dictionary(realm_results_t* results, size_t index)
{
return wrap_err([&]() {
realm_dictionary_t* out = nullptr;
auto result_dictionary = results->get_dictionary(index);
if (result_dictionary.is_valid())
out = new realm_dictionary_t{result_dictionary};
return out;
});
}

RLM_API bool realm_results_find(realm_results_t* results, realm_value_t* value, size_t* out_index, bool* out_found)
{
if (out_index)
Expand Down
34 changes: 34 additions & 0 deletions src/realm/object-store/results.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,40 @@ Mixed Results::get_any(size_t ndx)
throw OutOfBounds{"get_any() on Results", ndx, do_size()};
}

List Results::get_list(size_t ndx)
{
util::CheckedUniqueLock lock(m_mutex);
REALM_ASSERT(m_mode == Mode::Collection);
ensure_up_to_date();
if (size_t actual = actual_index(ndx); actual < m_collection->size()) {
return List{m_realm, m_collection->get_list(m_collection->get_path_element(actual))};
}
throw OutOfBounds{"get_list() on Results", ndx, m_collection->size()};
}

object_store::Set Results::get_set(size_t ndx)
{
util::CheckedUniqueLock lock(m_mutex);
REALM_ASSERT(m_mode == Mode::Collection);
ensure_up_to_date();
if (size_t actual = actual_index(ndx); actual < m_collection->size()) {
return object_store::Set{m_realm, m_collection->get_set(m_collection->get_path_element(actual))};
}
throw OutOfBounds{"get_set() on Results", ndx, m_collection->size()};
}

object_store::Dictionary Results::get_dictionary(size_t ndx)
{
util::CheckedUniqueLock lock(m_mutex);
REALM_ASSERT(m_mode == Mode::Collection);
ensure_up_to_date();
if (size_t actual = actual_index(ndx); actual < m_collection->size()) {
return object_store::Dictionary{m_realm,
m_collection->get_dictionary(m_collection->get_path_element(actual))};
}
throw OutOfBounds{"get_dictionary() on Results", ndx, m_collection->size()};
}

std::pair<StringData, Mixed> Results::get_dictionary_element(size_t ndx)
{
util::CheckedUniqueLock lock(m_mutex);
Expand Down
10 changes: 10 additions & 0 deletions src/realm/object-store/results.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,18 @@

namespace realm {
class Mixed;
class List;
class SectionedResults;

namespace _impl {
class ResultsNotifierBase;
}

namespace object_store {
class Dictionary;
class Set;
} // namespace object_store

class Results {
public:
// Results can be either be backed by nothing, a thin wrapper around a table,
Expand Down Expand Up @@ -113,6 +119,10 @@ class Results {
// Get an element in a list
Mixed get_any(size_t index) REQUIRES(!m_mutex);

List get_list(size_t index) REQUIRES(!m_mutex);
object_store::Dictionary get_dictionary(size_t index) REQUIRES(!m_mutex);
object_store::Set get_set(size_t index) REQUIRES(!m_mutex);

// Get the key/value pair at an index of the results.
// This method is only valid when applied to a results based on a
// object_store::Dictionary::get_values(), and will assert this.
Expand Down
68 changes: 68 additions & 0 deletions test/object-store/c_api/c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4996,6 +4996,73 @@ TEST_CASE("C API: nested collections", "[c_api]") {
checked(realm_refresh(realm, nullptr));
};

SECTION("results of mixed") {
SECTION("dictionary") {
REQUIRE(realm_set_dictionary(obj1.get(), foo_any_col_key));
realm_value_t value;
realm_get_value(obj1.get(), foo_any_col_key, &value);
REQUIRE(value.type == RLM_TYPE_DICTIONARY);
auto dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_any_col_key));
auto nlist = cptr_checked(realm_dictionary_insert_list(dict.get(), rlm_str_val("A")));
auto ndict = cptr_checked(realm_dictionary_insert_dictionary(dict.get(), rlm_str_val("B")));
auto nset = cptr_checked(realm_dictionary_insert_set(dict.get(), rlm_str_val("C")));

// verify that we can fetch a collection from a result of mixed
auto results = cptr_checked(realm_dictionary_to_results(dict.get()));
const auto sz = results->size();
REQUIRE(sz == dict->size());
REQUIRE(results->is_valid());
realm_value_t val;
realm_results_get(results.get(), 0, &val);
REQUIRE(val.type == RLM_TYPE_LIST);
realm_results_get(results.get(), 1, &val);
REQUIRE(val.type == RLM_TYPE_DICTIONARY);
realm_results_get(results.get(), 2, &val);
REQUIRE(val.type == RLM_TYPE_SET);
auto result_list = cptr_checked(realm_results_get_list(results.get(), 0));
REQUIRE(result_list);
REQUIRE(result_list->size() == nlist->size());
auto result_dictionary = cptr_checked(realm_results_get_dictionary(results.get(), 1));
REQUIRE(result_dictionary);
REQUIRE(result_dictionary->size() == ndict->size());
auto result_set = cptr_checked(realm_results_get_set(results.get(), 2));
REQUIRE(result_set);
REQUIRE(result_set->size() == nset->size());
}
SECTION("list") {
REQUIRE(realm_set_list(obj1.get(), foo_any_col_key));
realm_value_t value;
realm_get_value(obj1.get(), foo_any_col_key, &value);
REQUIRE(value.type == RLM_TYPE_LIST);
auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
auto nlist = cptr_checked(realm_list_insert_list(list.get(), 0));
auto ndict = cptr_checked(realm_list_insert_dictionary(list.get(), 1));
auto nset = cptr_checked(realm_list_insert_set(list.get(), 2));

// verify that we can fetch a collection from a result of mixed
auto results = cptr_checked(realm_list_to_results(list.get()));
const auto sz = results->size();
REQUIRE(sz == list->size());
REQUIRE(results->is_valid());
realm_value_t val;
realm_results_get(results.get(), 0, &val);
REQUIRE(val.type == RLM_TYPE_LIST);
realm_results_get(results.get(), 1, &val);
REQUIRE(val.type == RLM_TYPE_DICTIONARY);
realm_results_get(results.get(), 2, &val);
REQUIRE(val.type == RLM_TYPE_SET);
auto result_list = cptr_checked(realm_results_get_list(results.get(), 0));
REQUIRE(result_list);
REQUIRE(result_list->size() == nlist->size());
auto result_dictionary = cptr_checked(realm_results_get_dictionary(results.get(), 1));
REQUIRE(result_dictionary);
REQUIRE(result_dictionary->size() == ndict->size());
auto result_set = cptr_checked(realm_results_get_set(results.get(), 2));
REQUIRE(result_set);
REQUIRE(result_set->size() == nset->size());
}
}

SECTION("dictionary") {
struct UserData {
size_t deletions;
Expand Down Expand Up @@ -5063,6 +5130,7 @@ TEST_CASE("C API: nested collections", "[c_api]") {
size_t size;
checked(realm_dictionary_size(dict.get(), &size));
REQUIRE(size == 4);
checked(realm_commit(realm));
}

SECTION("list") {
Expand Down
28 changes: 24 additions & 4 deletions test/object-store/dictionary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,14 @@ TEST_CASE("nested dictionary in mixed", "[dictionary]") {
};

write([&] {
dict_mixed.insert_collection("test", CollectionType::List);
dict_mixed.insert_collection("list", CollectionType::List);
dict_mixed.insert_collection("set", CollectionType::Set);
dict_mixed.insert_collection("dictionary", CollectionType::Dictionary);
});

REQUIRE(change_dictionary.insertions.count() == 1);
REQUIRE(change_dictionary.insertions.count() == 3);

auto list = dict_mixed.get_list("test");
auto list = dict_mixed.get_list("list");

SECTION("notification on nested list") {
CollectionChangeSet change;
Expand Down Expand Up @@ -143,7 +145,7 @@ TEST_CASE("nested dictionary in mixed", "[dictionary]") {
});
REQUIRE_INDICES(change.insertions, 0, 1);
write([&] {
dict_mixed.insert("test", 42);
dict_mixed.insert("list", 42);
});
REQUIRE_INDICES(change.deletions, 0, 1);
REQUIRE(change.collection_root_was_deleted);
Expand Down Expand Up @@ -175,6 +177,24 @@ TEST_CASE("nested dictionary in mixed", "[dictionary]") {
REQUIRE(change.collection_root_was_deleted);
}
}
SECTION("dictionary as Results") {
auto results = dict_mixed.get_values();

auto val = results.get<Mixed>(0);
REQUIRE(val.is_type(type_Dictionary));
auto dict = results.get_dictionary(0);
REQUIRE(dict.is_valid());

val = results.get<Mixed>(1);
REQUIRE(val.is_type(type_List));
auto list = results.get_list(1);
REQUIRE(list.is_valid());

val = results.get<Mixed>(2);
REQUIRE(val.is_type(type_Set));
auto set = results.get_set(2);
REQUIRE(set.is_valid());
}
}

TEMPLATE_TEST_CASE("dictionary types", "[dictionary]", cf::MixedVal, cf::Int, cf::Bool, cf::Float, cf::Double,
Expand Down