Skip to content

Commit

Permalink
Get path from collection objects
Browse files Browse the repository at this point in the history
  • Loading branch information
jedelbo committed Apr 12, 2023
1 parent ad3834b commit e9bd77a
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 18 deletions.
7 changes: 7 additions & 0 deletions src/realm/collection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,13 @@ class CollectionBaseImpl : public Interface, protected ArrayParent {
public:
static_assert(std::is_base_of_v<CollectionBase, Interface>);

Path get_path() const
{
auto path = m_parent->get_path();
m_parent->add_index(path.path_from_top, m_index);
return path;
}

// Overriding members of CollectionBase:
ColKey get_col_key() const noexcept final
{
Expand Down
21 changes: 21 additions & 0 deletions src/realm/collection_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,27 @@ UpdateStatus CollectionList::update_if_needed_with_status() const
}


auto CollectionList::get_path() const noexcept -> Path
{
auto path = m_parent->get_path();
m_parent->add_index(path.path_from_top, m_index);
return path;
}

void CollectionList::add_index(std::vector<PathElement>& path, Index index) const noexcept
{
if (m_key_type == type_Int) {
auto int_keys = static_cast<BPlusTree<Int>*>(m_keys.get());
auto ndx = int_keys->find_first(mpark::get<int64_t>(index));
REALM_ASSERT(ndx != realm::not_found);
path.emplace_back(ndx);
}
else {
path.emplace_back(mpark::get<std::string>(index));
}
}


ref_type CollectionList::get_child_ref(size_t) const noexcept
{
return m_parent->get_collection_ref(m_col_key);
Expand Down
3 changes: 3 additions & 0 deletions src/realm/collection_list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class CollectionList : public Collection,

bool init_from_parent(bool allow_create) const;

Path get_path() const noexcept final;
void add_index(std::vector<PathElement>& path, Index ndx) const noexcept final;

size_t get_level() const noexcept final
{
return m_level;
Expand Down
14 changes: 13 additions & 1 deletion src/realm/collection_parent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@

namespace realm {

template <>
const int64_t* PathElement::get_if<int64_t>() const
{
return m_type == Type::integer ? &int_val : nullptr;
}

template <>
const std::string* PathElement::get_if<std::string>() const
{
return m_type == Type::string ? &string_val : nullptr;
}

/***************************** CollectionParent ******************************/

CollectionParent::~CollectionParent() {}
Expand Down Expand Up @@ -260,4 +272,4 @@ CollectionBasePtr CollectionParent::get_collection_ptr(ColKey col_key) const
return {};
}

} // namespace realm
} // namespace realm
80 changes: 80 additions & 0 deletions src/realm/collection_parent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,92 @@ enum class UpdateStatus {
NoChange,
};

struct PathElement {
union {
std::string string_val;
int64_t int_val;
};
enum Type { string, integer } m_type;

PathElement(const std::string& str)
: string_val(str)
, m_type(Type::string)
{
}
PathElement(int64_t ndx)
: int_val(ndx)
, m_type(Type::integer)
{
}
PathElement(const PathElement& other)
{
*this = other;
}

PathElement(PathElement&& other) noexcept
: m_type(other.m_type)
{
if (other.m_type == Type::string) {
new (&string_val) std::string(std::move(other.string_val));
}
else {
int_val = other.int_val;
}
}
~PathElement()
{
if (m_type == Type::string) {
string_val.std::string::~string();
}
}

PathElement& operator=(const PathElement& other)
{
m_type = other.m_type;
if (other.m_type == Type::string) {
string_val = other.string_val;
}
else {
int_val = other.int_val;
}
return *this;
}
bool operator==(const PathElement& other) const
{
if (m_type == other.m_type) {
return (m_type == Type::string) ? string_val == other.string_val : int_val == other.int_val;
}
return false;
}
const std::string& get_string() const
{
return string_val;
}
int64_t get_int() const
{
return int_val;
}
template <class T>
const T* get_if() const;
};

struct Path {
TableKey top_table;
ObjKey top_objkey;
std::vector<PathElement> path_from_top;
};

class CollectionParent {
public:
using Index = mpark::variant<ColKey, int64_t, std::string>;

// Return the nesting level of the parent
virtual size_t get_level() const noexcept = 0;
// Return the path to this object. The path is calculated from
// the topmost Obj - which must be an Obj with a primary key.
virtual Path get_path() const noexcept = 0;
// Add a translation of Index to PathElement
virtual void add_index(std::vector<PathElement>& path, Index ndx) const noexcept = 0;
/// Get table of owning object
virtual TableRef get_table() const noexcept = 0;

Expand Down
28 changes: 19 additions & 9 deletions src/realm/obj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ Obj::FatPath Obj::get_fat_path() const
return result;
}

Obj::Path Obj::get_path() const
Path Obj::get_path() const noexcept
{
Path result;
if (m_table->is_embedded()) {
Expand All @@ -941,24 +941,28 @@ Obj::Path Obj::get_path() const
if (backlinks.size() == 1) {
TableRef origin_table = m_table->get_opposite_table(col_key);
Obj obj = origin_table->get_object(backlinks[0]); // always the first (and only)
result = obj.get_path();
auto next_col_key = m_table->get_opposite_column(col_key);
result.path_from_top.push_back(Mixed(obj.get_table()->get_column_name(next_col_key)));

ColumnAttrMask attr = next_col_key.get_attrs();
Mixed index;
if (attr.test(col_attr_List)) {
REALM_ASSERT(next_col_key.get_type() == col_type_LinkList);
LnkLst link_list = obj.get_linklist(next_col_key);
auto i = link_list.find_first(get_key());
Lst<ObjKey> link_list(next_col_key);
size_t i = find_link_value_in_collection(link_list, obj, next_col_key, get_key());
REALM_ASSERT(i != realm::not_found);
result.path_from_top.push_back(Mixed(int64_t(i)));
result = link_list.get_path();
result.path_from_top.emplace_back(int64_t(i));
}
else if (attr.test(col_attr_Dictionary)) {
auto dict = obj.get_dictionary(next_col_key);
auto ndx = dict.find_first(get_link());
Dictionary dict(next_col_key);
size_t ndx = find_link_value_in_collection(dict, obj, next_col_key, get_link());
REALM_ASSERT(ndx != realm::not_found);
result.path_from_top.push_back(dict.get_key(ndx));
result = dict.get_path();
result.path_from_top.push_back(std::string(dict.get_key(ndx).get_string()));
}
else {
result = obj.get_path();
result.path_from_top.push_back(std::string(obj.get_table()->get_column_name(next_col_key)));
}

return IteratorControl::Stop; // early out
Expand All @@ -973,6 +977,12 @@ Obj::Path Obj::get_path() const
return result;
}

void Obj::add_index(std::vector<PathElement>& path, Index index) const noexcept
{
auto col_key = mpark::get<ColKey>(index);
StringData col_name = get_table()->get_column_name(col_key);
path.emplace_back(std::string(col_name));
}

namespace {
const char to_be_escaped[] = "\"\n\r\t\f\\\b";
Expand Down
8 changes: 3 additions & 5 deletions src/realm/obj.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ class Obj : public CollectionParent {
{
return 0;
}
Path get_path() const noexcept final;
void add_index(std::vector<PathElement>& path, Index ndx) const noexcept final;

UpdateStatus update_if_needed_with_status() const final;
bool update_if_needed() const final;
TableRef get_table() const noexcept final
Expand Down Expand Up @@ -171,11 +174,6 @@ class Obj : public CollectionParent {

std::string to_string() const;

// Get the path in a minimal format without including object accessors.
// If you need to obtain additional information for each object in the path,
// you should use get_fat_path() or traverse_path() instead (see below).
Path get_path() const;

// Get the fat path to this object expressed as a vector of fat path elements.
// each Fat path elements include a Obj allowing for low cost access to the
// objects data.
Expand Down
6 changes: 3 additions & 3 deletions src/realm/sync/instruction_replication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -778,11 +778,11 @@ void SyncReplication::populate_path_instr(Instruction::PathInstruction& instr, c
size_t sz = path.path_from_top.size();
instr.path.m_path.reserve(sz - 1);
for (size_t i = 1; i < sz; i++) {
if (auto pval = path.path_from_top[i].get_if<Int>()) {
if (auto pval = path.path_from_top[i].get_if<int64_t>()) {
instr.path.push_back(uint32_t(*pval));
}
else if (auto pval = path.path_from_top[i].get_if<StringData>()) {
InternString interned_field_name = m_encoder.intern_string(*pval);
else if (auto pval = path.path_from_top[i].get_if<std::string>()) {
InternString interned_field_name = m_encoder.intern_string(pval->c_str());
instr.path.push_back(interned_field_name);
}
}
Expand Down
82 changes: 82 additions & 0 deletions test/test_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1022,3 +1022,85 @@ TEST(List_NestedDict_Unresolved)
CHECK_EQUAL(obj.get_backlink_count(), 1);
CHECK_EQUAL(foo_ll0->get("A"), Mixed(obj.get_link()));
}

TEST(List_NestedList_Path)
{
Group g;
auto top_table = g.add_table_with_primary_key("top", type_String, "_id");
auto embedded_table = g.add_table("embedded", Table::Type::Embedded);
auto list_col =
top_table->add_column(*embedded_table, "embedded_list", {CollectionType::List, CollectionType::List});
auto dict_col =
top_table->add_column(*embedded_table, "embedded_dict", {CollectionType::Dictionary, CollectionType::List});
auto string_col = top_table->add_column_list(type_String, "strings");
auto float_col =
top_table->add_column(type_Float, "floats", false, {CollectionType::Dictionary, CollectionType::List});
auto int_col =
embedded_table->add_column(type_Int, "integers", false, {CollectionType::Dictionary, CollectionType::List});

Obj o = top_table->create_object_with_primary_key("Adam");

// First level list
{
auto list_string = o.get_list<String>(string_col);
auto path = list_string.get_path();
CHECK_EQUAL(path.path_from_top.size(), 1);
CHECK_EQUAL(path.path_from_top[0].get_string(), "strings");
}

// List nested in Dictionary
{
auto dict = o.get_collection_list(float_col);
auto list_foo = dict->insert_collection("Foo");
auto list_float = dynamic_cast<Lst<Float>*>(list_foo.get());
list_float->add(5.f);
auto path = list_float->get_path();
CHECK_EQUAL(path.path_from_top.size(), 2);
CHECK_EQUAL(path.path_from_top[0].get_string(), "floats");
CHECK_EQUAL(path.path_from_top[1].get_string(), "Foo");
}

// List nested in Dictionary contained in embedded object contained in list of list
{
auto list = o.get_collection_list(list_col);
list->insert_collection(0);
list->insert_collection(1);
auto coll = list->insert_collection(2);
auto ll = dynamic_cast<LnkLst*>(coll.get());
ll->create_and_insert_linked_object(0);
auto embedded_obj = ll->create_and_insert_linked_object(1);
auto dict = embedded_obj.get_collection_list(int_col);
auto list_foo = dict->insert_collection("Foo");
auto list_int = dynamic_cast<Lst<Int>*>(list_foo.get());
list_int->add(5);
auto path = list_int->get_path();
CHECK_EQUAL(path.path_from_top.size(), 5);
CHECK_EQUAL(path.path_from_top[0].get_string(), "embedded_list");
CHECK_EQUAL(path.path_from_top[1].get_int(), 2);
CHECK_EQUAL(path.path_from_top[2].get_int(), 1);
CHECK_EQUAL(path.path_from_top[3].get_string(), "integers");
CHECK_EQUAL(path.path_from_top[4].get_string(), "Foo");
}

// List nested in Dictionary contained in embedded object contained in Dictionary of list
{
auto list = o.get_collection_list(dict_col);
list->insert_collection("A");
list->insert_collection("B");
auto coll = list->insert_collection("C");
auto ll = dynamic_cast<LnkLst*>(coll.get());
ll->create_and_insert_linked_object(0);
auto embedded_obj = ll->create_and_insert_linked_object(1);
auto dict = embedded_obj.get_collection_list(int_col);
auto list_foo = dict->insert_collection("Foo");
auto list_int = dynamic_cast<Lst<Int>*>(list_foo.get());
list_int->add(5);
auto path = list_int->get_path();
CHECK_EQUAL(path.path_from_top.size(), 5);
CHECK_EQUAL(path.path_from_top[0].get_string(), "embedded_dict");
CHECK_EQUAL(path.path_from_top[1].get_string(), "C");
CHECK_EQUAL(path.path_from_top[2].get_int(), 1);
CHECK_EQUAL(path.path_from_top[3].get_string(), "integers");
CHECK_EQUAL(path.path_from_top[4].get_string(), "Foo");
}
}

0 comments on commit e9bd77a

Please sign in to comment.