mirror of
https://github.com/silverqx/TinyORM.git
synced 2025-12-30 07:19:34 -06:00
added sortBy() sort by multiple columns overload
- added unit tests - updated docs
This commit is contained in:
@@ -599,6 +599,37 @@ The `sort` method sorts the models collection by primary keys:
|
||||
|
||||
You may pass a predicate and projection callbacks to the `sort` method with your own algorithms. Refer to the CPP reference documentation on [`ranges::sort`](https://en.cppreference.com/w/cpp/algorithm/ranges/sort), which is what the `sort` method calls internally.
|
||||
|
||||
You can eg. sort by multiple columns, for an alternative method of multi-column sorting look at [sortBy](#method-sortby):
|
||||
|
||||
ModelsCollection<User> users {
|
||||
{{"name", "Kate"}, {"votes", 350}},
|
||||
{{"name", "John"}, {"votes", 200}},
|
||||
{{"name", "John"}, {"votes", 150}},
|
||||
{{"name", "Kate"}, {"votes", 200}},
|
||||
};
|
||||
|
||||
auto sorted = users.sort([](const User *const left,
|
||||
const User *const right)
|
||||
{
|
||||
const auto leftValue = left->getAttribute<QString>("name");
|
||||
const auto rightValue = right->getAttribute<QString>("name");
|
||||
|
||||
if (leftValue == rightValue)
|
||||
return left->getAttribute<quint64>("votes") <
|
||||
right->getAttribute<quint64>("votes");
|
||||
|
||||
return leftValue < rightValue;
|
||||
});
|
||||
|
||||
/*
|
||||
{
|
||||
{{"name", "John"}, {"votes", 150}},
|
||||
{{"name", "John"}, {"votes", 200}},
|
||||
{{"name", "Kate"}, {"votes", 200}},
|
||||
{{"name", "Kate"}, {"votes", 350}},
|
||||
}
|
||||
*/
|
||||
|
||||
The order of equal elements is not guaranteed to be preserved.
|
||||
|
||||
:::info
|
||||
@@ -640,6 +671,43 @@ You may pass the projection callback to determine how to sort the collection's m
|
||||
}
|
||||
*/
|
||||
|
||||
If you would like to sort your collection by multiple columns, you may pass a vector of comparison lambda expressions that define each sort operation to the `sortBy` method, in the following example is the `name` column sorted in ascending order and the second `votes` column is sorted in descending order:
|
||||
|
||||
using AttributeUtils = Orm::Tiny::Utils::Attribute;
|
||||
|
||||
ModelsCollection<User> users {
|
||||
{{"name", "Kate"}, {"votes", 350}},
|
||||
{{"name", "John"}, {"votes", 200}},
|
||||
{{"name", "John"}, {"votes", 150}},
|
||||
{{"name", "Kate"}, {"votes", 200}},
|
||||
};
|
||||
|
||||
auto sorted = users.sortBy({
|
||||
[](const User *const left, const User *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortBy(
|
||||
left->getAttribute<QString>("name"),
|
||||
right->getAttribute<QString>("name"));
|
||||
},
|
||||
[](const User *const left, const User *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortByDesc(
|
||||
left->getAttribute<quint64>("votes"),
|
||||
right->getAttribute<quint64>("votes"));
|
||||
},
|
||||
});
|
||||
|
||||
/*
|
||||
{
|
||||
{{"name", "John"}, {"votes", 200}},
|
||||
{{"name", "John"}, {"votes", 150}},
|
||||
{{"name", "Kate"}, {"votes", 350}},
|
||||
{{"name", "Kate"}, {"votes", 200}},
|
||||
}
|
||||
*/
|
||||
|
||||
The `AttributeUtils::compareForSortBy` and `compareForSortByDesc` methods are helper methods, they are needed because the Qt framework doesn't define `<=>` spaceship operator on its types, it doesn't support the three-way comparison.
|
||||
|
||||
The order of equal elements is not guaranteed to be preserved.
|
||||
|
||||
#### `sortByDesc()` {#method-sortbydesc}
|
||||
|
||||
@@ -321,6 +321,12 @@ namespace Types
|
||||
ModelsCollection<ModelRawType *>
|
||||
sortByDesc(const QString &column);
|
||||
|
||||
/*! Sort the collection by the given callback (supports multi-columns sorting). */
|
||||
ModelsCollection<ModelRawType *>
|
||||
sortBy(const QVector<std::function<
|
||||
std::strong_ordering(const ModelRawType *,
|
||||
const ModelRawType *)>> &callbacks);
|
||||
|
||||
/*! Sort the collection using the given projection. */
|
||||
template<typename P>
|
||||
ModelsCollection<ModelRawType *>
|
||||
@@ -349,6 +355,12 @@ namespace Types
|
||||
ModelsCollection<ModelRawType *>
|
||||
stableSortByDesc(const QString &column);
|
||||
|
||||
/*! Sort the collection by the given callback (supports multi-columns sorting). */
|
||||
ModelsCollection<ModelRawType *>
|
||||
stableSortBy(const QVector<std::function<
|
||||
std::strong_ordering(const ModelRawType *,
|
||||
const ModelRawType *)>> &callbacks);
|
||||
|
||||
/*! Stable sort the collection using the given projection. */
|
||||
template<typename P>
|
||||
ModelsCollection<ModelRawType *>
|
||||
@@ -1371,6 +1383,38 @@ namespace Types
|
||||
return sortBy<T>(column, true);
|
||||
}
|
||||
|
||||
template<DerivedCollectionModel Model>
|
||||
ModelsCollection<typename ModelsCollection<Model>::ModelRawType *>
|
||||
ModelsCollection<Model>::sortBy(
|
||||
const QVector<std::function<
|
||||
std::strong_ordering(const ModelRawType *,
|
||||
const ModelRawType *)>> &callbacks)
|
||||
{
|
||||
// Nothing to do
|
||||
if (this->isEmpty())
|
||||
return {};
|
||||
|
||||
auto result = toPointersCollection();
|
||||
|
||||
std::ranges::sort(result, [&callbacks](const ModelRawType *const left,
|
||||
const ModelRawType *const right)
|
||||
{
|
||||
for (const auto &callback : callbacks) {
|
||||
const auto compared = std::invoke(callback, left, right);
|
||||
|
||||
// If the values are the same then sort by the next callback
|
||||
if (compared == std::strong_ordering::equal)
|
||||
continue;
|
||||
|
||||
return compared == std::strong_ordering::less;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<DerivedCollectionModel Model>
|
||||
template<typename P>
|
||||
ModelsCollection<typename ModelsCollection<Model>::ModelRawType *>
|
||||
@@ -1455,6 +1499,38 @@ namespace Types
|
||||
return stableSortBy<T>(column, true);
|
||||
}
|
||||
|
||||
template<DerivedCollectionModel Model>
|
||||
ModelsCollection<typename ModelsCollection<Model>::ModelRawType *>
|
||||
ModelsCollection<Model>::stableSortBy(
|
||||
const QVector<std::function<
|
||||
std::strong_ordering(const ModelRawType *,
|
||||
const ModelRawType *)>> &callbacks)
|
||||
{
|
||||
// Nothing to do
|
||||
if (this->isEmpty())
|
||||
return {};
|
||||
|
||||
auto result = toPointersCollection();
|
||||
|
||||
std::ranges::stable_sort(result, [&callbacks](const ModelRawType *const left,
|
||||
const ModelRawType *const right)
|
||||
{
|
||||
for (const auto &callback : callbacks) {
|
||||
const auto compared = std::invoke(callback, left, right);
|
||||
|
||||
// If the values are the same then sort by the next callback
|
||||
if (compared == std::strong_ordering::equal)
|
||||
continue;
|
||||
|
||||
return compared == std::strong_ordering::less;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<DerivedCollectionModel Model>
|
||||
template<typename P>
|
||||
ModelsCollection<typename ModelsCollection<Model>::ModelRawType *>
|
||||
|
||||
@@ -61,6 +61,22 @@ namespace Orm::Tiny::Utils
|
||||
static QVector<AttributeItem>
|
||||
exceptAttributesForReplicate(const Model &model,
|
||||
const std::unordered_set<QString> &except = {});
|
||||
|
||||
/*! Compare attributes helper function for the ModelsCollection::sortBy(). */
|
||||
template<typename T, typename U>
|
||||
requires ranges::totally_ordered_with<T, U>
|
||||
static std::strong_ordering compareForSortBy(T &&left, U &&right)
|
||||
noexcept(noexcept(std::forward<T>(left) == std::forward<U>(right)) &&
|
||||
noexcept(std::forward<T>(left) < std::forward<U>(right)) &&
|
||||
noexcept(std::forward<T>(left) > std::forward<U>(right)));
|
||||
/*! Compare attributes in the descending oder helper function
|
||||
for the ModelsCollection::sortBy(). */
|
||||
template<typename T, typename U>
|
||||
requires ranges::totally_ordered_with<T, U>
|
||||
static std::strong_ordering compareForSortByDesc(T &&left, U &&right)
|
||||
noexcept(noexcept(std::forward<T>(left) == std::forward<U>(right)) &&
|
||||
noexcept(std::forward<T>(left) < std::forward<U>(right)) &&
|
||||
noexcept(std::forward<T>(left) > std::forward<U>(right)));
|
||||
};
|
||||
|
||||
/* public */
|
||||
@@ -99,6 +115,40 @@ namespace Orm::Tiny::Utils
|
||||
| ranges::to<QVector<AttributeItem>>();
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
requires ranges::totally_ordered_with<T, U>
|
||||
std::strong_ordering Attribute::compareForSortBy(T &&left, U &&right)
|
||||
noexcept(noexcept(std::forward<T>(left) == std::forward<U>(right)) &&
|
||||
noexcept(std::forward<T>(left) < std::forward<U>(right)) &&
|
||||
noexcept(std::forward<T>(left) > std::forward<U>(right)))
|
||||
{
|
||||
if (std::forward<T>(left) == std::forward<U>(right))
|
||||
return std::strong_ordering::equal;
|
||||
if (std::forward<T>(left) < std::forward<U>(right))
|
||||
return std::strong_ordering::less;
|
||||
if (std::forward<T>(left) > std::forward<U>(right))
|
||||
return std::strong_ordering::greater;
|
||||
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
requires ranges::totally_ordered_with<T, U>
|
||||
std::strong_ordering Attribute::compareForSortByDesc(T &&left, U &&right)
|
||||
noexcept(noexcept(std::forward<T>(left) == std::forward<U>(right)) &&
|
||||
noexcept(std::forward<T>(left) < std::forward<U>(right)) &&
|
||||
noexcept(std::forward<T>(left) > std::forward<U>(right)))
|
||||
{
|
||||
if (std::forward<T>(left) == std::forward<U>(right))
|
||||
return std::strong_ordering::equal;
|
||||
if (std::forward<T>(left) < std::forward<U>(right))
|
||||
return std::strong_ordering::greater;
|
||||
if (std::forward<T>(left) > std::forward<U>(right))
|
||||
return std::strong_ordering::less;
|
||||
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
} // namespace Orm::Tiny::Utils
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
@@ -26,6 +26,8 @@ using Orm::Utils::NullVariant;
|
||||
|
||||
using TypeUtils = Orm::Utils::Type;
|
||||
|
||||
using AttributeUtils = Orm::Tiny::Utils::Attribute;
|
||||
|
||||
using TestUtils::Databases;
|
||||
|
||||
using Common = TestUtils::Common::Collection;
|
||||
@@ -128,6 +130,9 @@ private Q_SLOTS:
|
||||
void sortBy() const;
|
||||
void sortByDesc() const;
|
||||
|
||||
void sortBy_MoreColumns() const;
|
||||
void sortBy_MoreColumns_SecondDescending() const;
|
||||
|
||||
void sortBy_Projection() const;
|
||||
void sortByDesc_Projection() const;
|
||||
|
||||
@@ -143,6 +148,9 @@ private Q_SLOTS:
|
||||
void stableSortBy() const;
|
||||
void stableSortByDesc() const;
|
||||
|
||||
void stableSortBy_MoreColumns() const;
|
||||
void stableSortBy_MoreColumns_SecondDescending() const;
|
||||
|
||||
void stableSortBy_Projection() const;
|
||||
void stableSortByDesc_Projection() const;
|
||||
|
||||
@@ -1548,6 +1556,93 @@ void tst_Collection_Models::sortByDesc() const
|
||||
QCOMPARE(sorted, expectedAlbums);
|
||||
}
|
||||
|
||||
void tst_Collection_Models::sortBy_MoreColumns() const
|
||||
{
|
||||
ModelsCollection<Album> albums {
|
||||
{{NAME, "album3"}, {SIZE_, 1}},
|
||||
{{NAME, "album1"}, {SIZE_, 1}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}},
|
||||
{{NAME, "album1"}, {SIZE_, 2}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}},
|
||||
};
|
||||
|
||||
auto sorted = albums.sortBy(
|
||||
{
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortBy(left->getAttribute<QString>(NAME),
|
||||
right->getAttribute<QString>(NAME));
|
||||
},
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortBy(left->getAttribute<quint64>(SIZE_),
|
||||
right->getAttribute<quint64>(SIZE_));
|
||||
},
|
||||
});
|
||||
QCOMPARE(typeid (sorted), typeid (ModelsCollection<Album *>));
|
||||
|
||||
ModelsCollection<Album> expectedAlbums {
|
||||
{{NAME, "album1"}, {SIZE_, 1}},
|
||||
{{NAME, "album1"}, {SIZE_, 2}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}},
|
||||
{{NAME, "album3"}, {SIZE_, 1}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}},
|
||||
};
|
||||
QCOMPARE(sorted, expectedAlbums);
|
||||
}
|
||||
|
||||
void tst_Collection_Models::sortBy_MoreColumns_SecondDescending() const
|
||||
{
|
||||
ModelsCollection<Album> albums {
|
||||
{{NAME, "album3"}, {SIZE_, 1}},
|
||||
{{NAME, "album1"}, {SIZE_, 1}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}},
|
||||
{{NAME, "album1"}, {SIZE_, 2}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}},
|
||||
};
|
||||
|
||||
auto sorted = albums.sortBy(
|
||||
{
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortBy(left->getAttribute<QString>(NAME),
|
||||
right->getAttribute<QString>(NAME));
|
||||
},
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortByDesc(
|
||||
left->getAttribute<quint64>(SIZE_),
|
||||
right->getAttribute<quint64>(SIZE_));
|
||||
},
|
||||
});
|
||||
QCOMPARE(typeid (sorted), typeid (ModelsCollection<Album *>));
|
||||
|
||||
ModelsCollection<Album> expectedAlbums {
|
||||
{{NAME, "album1"}, {SIZE_, 2}},
|
||||
{{NAME, "album1"}, {SIZE_, 1}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}},
|
||||
{{NAME, "album3"}, {SIZE_, 1}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}},
|
||||
};
|
||||
QCOMPARE(sorted, expectedAlbums);
|
||||
}
|
||||
|
||||
void tst_Collection_Models::sortBy_Projection() const
|
||||
{
|
||||
ModelsCollection<Album> albums {
|
||||
@@ -1808,6 +1903,101 @@ void tst_Collection_Models::stableSortByDesc() const
|
||||
QCOMPARE(sorted, expectedAlbums);
|
||||
}
|
||||
|
||||
void tst_Collection_Models::stableSortBy_MoreColumns() const
|
||||
{
|
||||
ModelsCollection<Album> albums {
|
||||
{{NAME, "album3"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "b"}}, // stable sort must guarantee this order
|
||||
{{NAME, "album1"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "a"}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album1"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
};
|
||||
|
||||
auto sorted = albums.stableSortBy(
|
||||
{
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortBy(left->getAttribute<QString>(NAME),
|
||||
right->getAttribute<QString>(NAME));
|
||||
},
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortBy(left->getAttribute<quint64>(SIZE_),
|
||||
right->getAttribute<quint64>(SIZE_));
|
||||
},
|
||||
});
|
||||
QCOMPARE(typeid (sorted), typeid (ModelsCollection<Album *>));
|
||||
|
||||
ModelsCollection<Album> expectedAlbums {
|
||||
{{NAME, "album1"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album1"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}, {NOTE, ""}},
|
||||
{{NAME, "album3"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "b"}}, // stable sort must guarantee this order
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "a"}},
|
||||
};
|
||||
QCOMPARE(sorted, expectedAlbums);
|
||||
}
|
||||
|
||||
void tst_Collection_Models::stableSortBy_MoreColumns_SecondDescending() const
|
||||
{
|
||||
ModelsCollection<Album> albums {
|
||||
{{NAME, "album3"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "b"}}, // stable sort must guarantee this order
|
||||
{{NAME, "album1"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "a"}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album1"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
};
|
||||
|
||||
auto sorted = albums.stableSortBy(
|
||||
{
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortBy(left->getAttribute<QString>(NAME),
|
||||
right->getAttribute<QString>(NAME));
|
||||
},
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortByDesc(
|
||||
left->getAttribute<quint64>(SIZE_),
|
||||
right->getAttribute<quint64>(SIZE_));
|
||||
},
|
||||
});
|
||||
QCOMPARE(typeid (sorted), typeid (ModelsCollection<Album *>));
|
||||
|
||||
ModelsCollection<Album> expectedAlbums {
|
||||
{{NAME, "album1"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album1"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album3"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "b"}}, // stable sort must guarantee this order
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "a"}},
|
||||
};
|
||||
QCOMPARE(sorted, expectedAlbums);
|
||||
}
|
||||
|
||||
void tst_Collection_Models::stableSortBy_Projection() const
|
||||
{
|
||||
ModelsCollection<Album> albums {
|
||||
|
||||
@@ -26,6 +26,8 @@ using Orm::Utils::NullVariant;
|
||||
|
||||
using TypeUtils = Orm::Utils::Type;
|
||||
|
||||
using AttributeUtils = Orm::Tiny::Utils::Attribute;
|
||||
|
||||
using TestUtils::Databases;
|
||||
|
||||
using Common = TestUtils::Common::Collection;
|
||||
@@ -129,6 +131,9 @@ private Q_SLOTS:
|
||||
void sortBy() const;
|
||||
void sortByDesc() const;
|
||||
|
||||
void sortBy_MoreColumns() const;
|
||||
void sortBy_MoreColumns_SecondDescending() const;
|
||||
|
||||
void sortBy_Projection() const;
|
||||
void sortByDesc_Projection() const;
|
||||
|
||||
@@ -144,6 +149,9 @@ private Q_SLOTS:
|
||||
void stableSortBy() const;
|
||||
void stableSortByDesc() const;
|
||||
|
||||
void stableSortBy_MoreColumns() const;
|
||||
void stableSortBy_MoreColumns_SecondDescending() const;
|
||||
|
||||
void stableSortBy_Projection() const;
|
||||
void stableSortByDesc_Projection() const;
|
||||
|
||||
@@ -1926,6 +1934,95 @@ void tst_Collection_Relations::sortByDesc() const
|
||||
QCOMPARE(sorted, expectedAlbums);
|
||||
}
|
||||
|
||||
void tst_Collection_Relations::sortBy_MoreColumns() const
|
||||
{
|
||||
ModelsCollection<Album> albums {
|
||||
{{NAME, "album3"}, {SIZE_, 1}},
|
||||
{{NAME, "album1"}, {SIZE_, 1}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}},
|
||||
{{NAME, "album1"}, {SIZE_, 2}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}},
|
||||
};
|
||||
auto albumsInit = albums.toPointers();
|
||||
|
||||
auto sorted = albumsInit.sortBy(
|
||||
{
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortBy(left->getAttribute<QString>(NAME),
|
||||
right->getAttribute<QString>(NAME));
|
||||
},
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortBy(left->getAttribute<quint64>(SIZE_),
|
||||
right->getAttribute<quint64>(SIZE_));
|
||||
},
|
||||
});
|
||||
QCOMPARE(typeid (sorted), typeid (ModelsCollection<Album *>));
|
||||
|
||||
ModelsCollection<Album> expectedAlbums {
|
||||
{{NAME, "album1"}, {SIZE_, 1}},
|
||||
{{NAME, "album1"}, {SIZE_, 2}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}},
|
||||
{{NAME, "album3"}, {SIZE_, 1}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}},
|
||||
};
|
||||
QCOMPARE(sorted, expectedAlbums);
|
||||
}
|
||||
|
||||
void tst_Collection_Relations::sortBy_MoreColumns_SecondDescending() const
|
||||
{
|
||||
ModelsCollection<Album> albums {
|
||||
{{NAME, "album3"}, {SIZE_, 1}},
|
||||
{{NAME, "album1"}, {SIZE_, 1}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}},
|
||||
{{NAME, "album1"}, {SIZE_, 2}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}},
|
||||
};
|
||||
auto albumsInit = albums.toPointers();
|
||||
|
||||
auto sorted = albumsInit.sortBy(
|
||||
{
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortBy(left->getAttribute<QString>(NAME),
|
||||
right->getAttribute<QString>(NAME));
|
||||
},
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortByDesc(
|
||||
left->getAttribute<quint64>(SIZE_),
|
||||
right->getAttribute<quint64>(SIZE_));
|
||||
},
|
||||
});
|
||||
QCOMPARE(typeid (sorted), typeid (ModelsCollection<Album *>));
|
||||
|
||||
ModelsCollection<Album> expectedAlbums {
|
||||
{{NAME, "album1"}, {SIZE_, 2}},
|
||||
{{NAME, "album1"}, {SIZE_, 1}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}},
|
||||
{{NAME, "album3"}, {SIZE_, 1}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}},
|
||||
};
|
||||
QCOMPARE(sorted, expectedAlbums);
|
||||
}
|
||||
|
||||
void tst_Collection_Relations::sortBy_Projection() const
|
||||
{
|
||||
ModelsCollection<Album> albums {
|
||||
@@ -2196,6 +2293,103 @@ void tst_Collection_Relations::stableSortByDesc() const
|
||||
QCOMPARE(sorted, expectedAlbums);
|
||||
}
|
||||
|
||||
void tst_Collection_Relations::stableSortBy_MoreColumns() const
|
||||
{
|
||||
ModelsCollection<Album> albums {
|
||||
{{NAME, "album3"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "b"}}, // stable sort must guarantee this order
|
||||
{{NAME, "album1"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "a"}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album1"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
};
|
||||
auto albumsInit = albums.toPointers();
|
||||
|
||||
auto sorted = albumsInit.stableSortBy(
|
||||
{
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortBy(left->getAttribute<QString>(NAME),
|
||||
right->getAttribute<QString>(NAME));
|
||||
},
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortBy(left->getAttribute<quint64>(SIZE_),
|
||||
right->getAttribute<quint64>(SIZE_));
|
||||
},
|
||||
});
|
||||
QCOMPARE(typeid (sorted), typeid (ModelsCollection<Album *>));
|
||||
|
||||
ModelsCollection<Album> expectedAlbums {
|
||||
{{NAME, "album1"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album1"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}, {NOTE, ""}},
|
||||
{{NAME, "album3"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "b"}}, // stable sort must guarantee this order
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "a"}},
|
||||
};
|
||||
QCOMPARE(sorted, expectedAlbums);
|
||||
}
|
||||
|
||||
void tst_Collection_Relations::stableSortBy_MoreColumns_SecondDescending() const
|
||||
{
|
||||
ModelsCollection<Album> albums {
|
||||
{{NAME, "album3"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "b"}}, // stable sort must guarantee this order
|
||||
{{NAME, "album1"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "a"}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album1"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
};
|
||||
auto albumsInit = albums.toPointers();
|
||||
|
||||
auto sorted = albumsInit.stableSortBy(
|
||||
{
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortBy(left->getAttribute<QString>(NAME),
|
||||
right->getAttribute<QString>(NAME));
|
||||
},
|
||||
[](const Album *const left, const Album *const right)
|
||||
{
|
||||
return AttributeUtils::compareForSortByDesc(
|
||||
left->getAttribute<quint64>(SIZE_),
|
||||
right->getAttribute<quint64>(SIZE_));
|
||||
},
|
||||
});
|
||||
QCOMPARE(typeid (sorted), typeid (ModelsCollection<Album *>));
|
||||
|
||||
ModelsCollection<Album> expectedAlbums {
|
||||
{{NAME, "album1"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album1"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 4}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 3}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album2"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album3"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 2}, {NOTE, ""}},
|
||||
{{NAME, "album4"}, {SIZE_, 1}, {NOTE, ""}},
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "b"}}, // stable sort must guarantee this order
|
||||
{{NAME, "album5"}, {SIZE_, 1}, {NOTE, "a"}},
|
||||
};
|
||||
QCOMPARE(sorted, expectedAlbums);
|
||||
}
|
||||
|
||||
void tst_Collection_Relations::stableSortBy_Projection() const
|
||||
{
|
||||
ModelsCollection<Album> albums {
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Models
|
||||
using Orm::Constants::ID;
|
||||
using Orm::Constants::NAME;
|
||||
using Orm::Constants::NOTE;
|
||||
using Orm::Constants::SIZE_;
|
||||
|
||||
using Orm::Tiny::Model;
|
||||
using Orm::Tiny::Relations::HasMany;
|
||||
@@ -48,6 +49,7 @@ private:
|
||||
ID,
|
||||
NAME,
|
||||
NOTE,
|
||||
SIZE_, // Database table doesn't contain the size column but I need it in tests
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user