Skip to content

Commit

Permalink
Merge pull request #102 from realm/lr/sort
Browse files Browse the repository at this point in the history
Lr/sort
  • Loading branch information
Lasse Reinhold authored Feb 20, 2018

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 09f777d + 8c353d7 commit 5579f54
Showing 6 changed files with 138 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/realm/cluster.hpp
Original file line number Diff line number Diff line change
@@ -192,6 +192,7 @@ class ClusterNode : public Array {

protected:
friend class ArrayBacklink;
friend class ObjList;

const ClusterTree& m_tree_top;
ClusterKeyArray m_keys;
46 changes: 45 additions & 1 deletion src/realm/mixed.hpp
Original file line number Diff line number Diff line change
@@ -149,6 +149,51 @@ class Mixed {
template <class Ch, class Tr>
friend std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>&, const Mixed&);

inline int compare(const Mixed& b) {
if (get_type() == type_Bool) {
if (get_bool() > b.get_bool())
return 1;
else if (get_bool() < b.get_bool())
return -1;
}
else if (get_type() == type_Int) {
if (get_int() > b.get_int())
return 1;
else if (get_int() < b.get_int())
return -1;
}
else if (get_type() == type_String) {
if (get_string() > b.get_string())
return 1;
else if (get_string() < b.get_string())
return -1;
}
else if (get_type() == type_Double) {
if (get_double() > b.get_double())
return 1;
else if (get_double() < b.get_double())
return -1;
}
else if (get_type() == type_Float) {
if (get_float() > b.get_float())
return 1;
else if (get_float() < b.get_float())
return -1;
}
else if (get_type() == type_Timestamp) {
if (get_timestamp() > b.get_timestamp())
return 1;
else if (get_timestamp() < b.get_timestamp())
return -1;
}
else {
REALM_ASSERT_RELEASE(false && "Compare not supported for this column type");
}

return 0;
}


private:
DataType m_type;
union {
@@ -613,7 +658,6 @@ inline bool operator!=(Timestamp a, Wrap<Mixed> b) noexcept
return type_Timestamp != Mixed(b).get_type() || a != Timestamp(Mixed(b).get_timestamp());
}


} // namespace realm

#endif // REALM_MIXED_HPP
37 changes: 36 additions & 1 deletion src/realm/obj_list.cpp
Original file line number Diff line number Diff line change
@@ -54,7 +54,42 @@ void ObjList::do_sort(const DescriptorOrdering& ordering)

if (const auto* sort_descr = dynamic_cast<const SortDescriptor*>(common_descr)) {

SortDescriptor::Sorter sort_predicate = sort_descr->sorter(m_key_values);
SortDescriptor::Sorter sort_predicate = sort_descr->sorter(m_key_values);
auto& col = sort_predicate.m_columns[0];
ColKey ck = col.col_key;

for (size_t i = 0; i < v.size(); i++) {
ObjKey key = v[i].key_for_object;

if (!col.translated_keys.empty()) {
key = col.translated_keys[v[i].index_in_view];
}

// Sorting can be specified by multiple columns, so that if two entries in the first column are
// identical, then the rows are ordered according to the second column, and so forth. For the
// first column, we cache all the payload of fields of the view in a std::vector<Mixed>
ConstObj obj = col.table->get_object(key);
DataType dt = sort_predicate.m_columns[0].table->get_column_type(ck);
auto& vec = sort_predicate.m_columns[0].payload;
if (dt == type_Int) {
vec.emplace_back(obj.get<Int>(ck));
}
else if (dt == type_String) {
vec.emplace_back(obj.get<String>(ck));
}
else if (dt == type_Float) {
vec.emplace_back(obj.get<Float>(ck));
}
else if (dt == type_Double) {
vec.emplace_back(obj.get<Double>(ck));
}
else if (dt == type_Bool) {
vec.emplace_back(obj.get<Bool>(ck));
}
else if (dt == type_Timestamp) {
vec.emplace_back(obj.get<Timestamp>(ck));
}
}

std::sort(v.begin(), v.end(), std::ref(sort_predicate));

26 changes: 22 additions & 4 deletions src/realm/sort_descriptor.cpp
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ CommonDescriptor::Sorter::Sorter(std::vector<std::vector<ColumnId>> const& colum

m_columns.reserve(columns.size());
for (size_t i = 0; i < columns.size(); ++i) {
m_columns.push_back({{}, {}, columns[i].back().table, columns[i].back().col_key, ascending[i]});
m_columns.push_back({ {}, {}, columns[i].back().table, columns[i].back().col_key, ascending[i], {} });
REALM_ASSERT_EX(!columns[i].empty(), i);
if (columns[i].size() == 1) { // no link chain
continue;
@@ -168,10 +168,28 @@ bool SortDescriptor::Sorter::operator()(IndexPair i, IndexPair j, bool total_ord
key_j = m_columns[t].translated_keys[j.index_in_view];
}

ConstObj obj_i = m_columns[t].table->get_object(key_i);
ConstObj obj_j = m_columns[t].table->get_object(key_j);
if (int c = obj_i.cmp(obj_j, m_columns[t].col_key))
int c;

// Sorting can be specified by multiple columns, so that if two entries in the first column are
// identical, then the rows are ordered according to the second column, and so forth. For the
// first column, all the payload of the View is cached in a std::vector<Mixed>.
if (t == 0) {
Mixed mixed_i(m_columns[0].payload[i.index_in_view]);
Mixed mixed_j(m_columns[0].payload[j.index_in_view]);

c = mixed_j.compare(mixed_i);
}
else {
ConstObj obj_i = m_columns[t].table->get_object(key_i);
ConstObj obj_j = m_columns[t].table->get_object(key_j);

c = obj_i.cmp(obj_j, m_columns[t].col_key);
}

if (c) {
return m_columns[t].ascending ? c > 0 : c < 0;
}

}
// make sort stable by using original index as final comparison
return total_ordering ? i.index_in_view < j.index_in_view : 0;
6 changes: 6 additions & 0 deletions src/realm/sort_descriptor.hpp
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@
#include <vector>
#include <realm/cluster.hpp>
#include <realm/handover_defs.hpp>
#include <realm/mixed.hpp>

namespace realm {

@@ -90,11 +91,16 @@ class CommonDescriptor {
struct SortColumn {
std::vector<bool> is_null;
std::vector<ObjKey> translated_keys;

const Table* table;
ColKey col_key;
bool ascending;

// This vector is only filled out for the *first* SortColumn in m_columns
std::vector<Mixed> payload;
};
std::vector<SortColumn> m_columns;
friend class ObjList;
};
virtual Sorter sorter(KeyColumn const& row_indexes) const;

28 changes: 28 additions & 0 deletions test/test_shared.cpp
Original file line number Diff line number Diff line change
@@ -94,6 +94,34 @@ using unit_test::TestContext;
// `experiments/testcase.cpp` and then run `sh build.sh
// check-testcase` (or one of its friends) from the command line.

#if 0
// Sorting benchmark
ONLY(Query_QuickSort2)
{
Random random(random_int<unsigned long>()); // Seed from slow global generator

// Triggers QuickSort because range > len
Table ttt;
auto ints = ttt.add_column(type_Int, "1");
auto strings = ttt.add_column(type_String, "2");

for (size_t t = 0; t < 10000; t++) {
Obj o = ttt.create_object();
// o.set<int64_t>(ints, random.draw_int_mod(1100));
o.set<StringData>(strings, "a");
}

Query q = ttt.where();

std::cerr << "GO";

for (size_t t = 0; t < 1000; t++) {
TableView tv = q.find_all();
tv.sort(strings);
// tv.ints(strings);
}
}
#endif

#if 0
// String query benchmark

0 comments on commit 5579f54

Please sign in to comment.