diff --git a/src/libs/services/database/impl/Cluster.cpp b/src/libs/services/database/impl/Cluster.cpp index 5ec3217d..1d000ef7 100644 --- a/src/libs/services/database/impl/Cluster.cpp +++ b/src/libs/services/database/impl/Cluster.cpp @@ -28,177 +28,179 @@ #include "SqlQuery.hpp" #include "Utils.hpp" -namespace Database { - -Cluster::Cluster(ObjectPtr type, std::string_view name) - : _name {std::string {name, 0, _maxNameLength}}, - _clusterType {getDboPtr(type)} +namespace Database { -} + namespace + { + Wt::Dbo::Query createQuery(Session& session, const Cluster::FindParameters& params) + { + session.checkSharedLocked(); -Cluster::pointer -Cluster::create(Session& session, ObjectPtr type, std::string_view name) -{ - return session.getDboSession().add(std::unique_ptr {new Cluster {type, name}}); -} + auto query{ session.getDboSession().query("SELECT DISTINCT c.id FROM cluster c") }; -std::size_t -Cluster::getCount(Session& session) -{ - session.checkSharedLocked(); + if (params.track.isValid()) + { + query.join("track_cluster t_c ON t_c.cluster_id = c.id"); + query.join("track t ON t.id = t_c.track_id"); + query.where("t.id = ?").bind(params.track); + } - return session.getDboSession().query("SELECT COUNT(*) FROM cluster"); -} + if (params.clusterType.isValid()) + query.where("c.cluster_type_id = ?").bind(params.clusterType); -RangeResults -Cluster::find(Session& session, Range range) -{ - session.checkSharedLocked(); - auto query {session.getDboSession().query("SELECT id FROM cluster")}; + return query; + } + } - return Utils::execQuery(query, range); -} + Cluster::Cluster(ObjectPtr type, std::string_view name) + : _name{ std::string {name, 0, _maxNameLength} }, + _clusterType{ getDboPtr(type) } + { + } -RangeResults -Cluster::findOrphans(Session& session, Range range) -{ - session.checkSharedLocked(); - auto query {session.getDboSession().query("SELECT DISTINCT c.id FROM cluster c WHERE NOT EXISTS(SELECT 1 FROM track_cluster t_c WHERE t_c.cluster_id = c.id)")}; + Cluster::pointer Cluster::create(Session& session, ObjectPtr type, std::string_view name) + { + return session.getDboSession().add(std::unique_ptr {new Cluster{ type, name }}); + } - return Utils::execQuery(query, range); -} + std::size_t Cluster::getCount(Session& session) + { + session.checkSharedLocked(); -Cluster::pointer -Cluster::find(Session& session, ClusterId id) -{ - session.checkSharedLocked(); + return session.getDboSession().query("SELECT COUNT(*) FROM cluster"); + } - return session.getDboSession().find().where("id = ?").bind(id).resultValue(); -} + RangeResults Cluster::find(Session& session, const FindParameters& params) + { + session.checkSharedLocked(); + auto query{ createQuery(session, params) }; -void -Cluster::addTrack(ObjectPtr track) -{ - _tracks.insert(getDboPtr(track)); -} + return Utils::execQuery(query, params.range); + } -RangeResults -Cluster::getTracks(Range range) const -{ - assert(session()); + RangeResults Cluster::findOrphans(Session& session, Range range) + { + session.checkSharedLocked(); + auto query{ session.getDboSession().query("SELECT DISTINCT c.id FROM cluster c WHERE NOT EXISTS(SELECT 1 FROM track_cluster t_c WHERE t_c.cluster_id = c.id)") }; - auto query {session()->query("SELECT t.id FROM track t INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id") - .where("c.id = ?").bind(getId())}; + return Utils::execQuery(query, range); + } - return Utils::execQuery(query, range); -} + Cluster::pointer Cluster::find(Session& session, ClusterId id) + { + session.checkSharedLocked(); -std::size_t -Cluster::getReleasesCount() const -{ - assert(session()); + return session.getDboSession().find().where("id = ?").bind(id).resultValue(); + } - return session()->query("SELECT COUNT(DISTINCT r.id) FROM release r INNER JOIN track t on t.release_id = r.id INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id") - .where("c.id = ?").bind(getId()); -} + void Cluster::addTrack(ObjectPtr track) + { + _tracks.insert(getDboPtr(track)); + } + + RangeResults Cluster::getTracks(Range range) const + { + assert(session()); + + auto query{ session()->query("SELECT t.id FROM track t INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id") + .where("c.id = ?").bind(getId()) }; + + return Utils::execQuery(query, range); + } + + std::size_t Cluster::getReleasesCount() const + { + assert(session()); + + return session()->query("SELECT COUNT(DISTINCT r.id) FROM release r INNER JOIN track t on t.release_id = r.id INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id") + .where("c.id = ?").bind(getId()); + } -ClusterType::ClusterType(std::string_view name) - : _name {name} -{ -} + ClusterType::ClusterType(std::string_view name) + : _name{ name } + { + } -ClusterType::pointer -ClusterType::create(Session& session, const std::string& name) -{ - return session.getDboSession().add(std::unique_ptr {new ClusterType {name}}); -} + ClusterType::pointer ClusterType::create(Session& session, const std::string& name) + { + return session.getDboSession().add(std::unique_ptr {new ClusterType{ name }}); + } -std::size_t -ClusterType::getCount(Session& session) -{ - session.checkSharedLocked(); + std::size_t ClusterType::getCount(Session& session) + { + session.checkSharedLocked(); - return session.getDboSession().query("SELECT COUNT(*) FROM cluster_type"); -} + return session.getDboSession().query("SELECT COUNT(*) FROM cluster_type"); + } -RangeResults -ClusterType::findOrphans(Session& session, Range range) -{ - session.checkSharedLocked(); + RangeResults ClusterType::findOrphans(Session& session, Range range) + { + session.checkSharedLocked(); - auto query {session.getDboSession().query( - "SELECT c_t.id from cluster_type c_t" - " LEFT OUTER JOIN cluster c ON c_t.id = c.cluster_type_id") - .where("c.id IS NULL")}; + auto query{ session.getDboSession().query( + "SELECT c_t.id from cluster_type c_t" + " LEFT OUTER JOIN cluster c ON c_t.id = c.cluster_type_id") + .where("c.id IS NULL") }; - return Utils::execQuery(query, range); -} + return Utils::execQuery(query, range); + } -RangeResults -ClusterType::findUsed(Session& session, Range range) -{ - session.checkSharedLocked(); + RangeResults ClusterType::findUsed(Session& session, Range range) + { + session.checkSharedLocked(); - auto query {session.getDboSession().query( - "SELECT DISTINCT c_t.id from cluster_type c_t") - .join("cluster c ON c_t.id = c.cluster_type_id")}; + auto query{ session.getDboSession().query( + "SELECT DISTINCT c_t.id from cluster_type c_t") + .join("cluster c ON c_t.id = c.cluster_type_id") }; - return Utils::execQuery(query, range); -} + return Utils::execQuery(query, range); + } -ClusterType::pointer -ClusterType::find(Session& session, std::string_view name) -{ - session.checkSharedLocked(); + ClusterType::pointer ClusterType::find(Session& session, std::string_view name) + { + session.checkSharedLocked(); - return session.getDboSession().find().where("name = ?").bind(std::string {name}).resultValue(); -} + return session.getDboSession().find().where("name = ?").bind(std::string{ name }).resultValue(); + } -ClusterType::pointer -ClusterType::find(Session& session, ClusterTypeId id) -{ - session.checkSharedLocked(); + ClusterType::pointer ClusterType::find(Session& session, ClusterTypeId id) + { + session.checkSharedLocked(); - return session.getDboSession().find().where("id = ?").bind(id).resultValue(); -} + return session.getDboSession().find().where("id = ?").bind(id).resultValue(); + } -RangeResults -ClusterType::find(Session& session, Range range) -{ - session.checkSharedLocked(); + RangeResults ClusterType::find(Session& session, Range range) + { + session.checkSharedLocked(); - auto query {session.getDboSession().query("SELECT id from cluster_type")}; + auto query{ session.getDboSession().query("SELECT id from cluster_type") }; - return Utils::execQuery(query, range); -} + return Utils::execQuery(query, range); + } -Cluster::pointer -ClusterType::getCluster(const std::string& name) const -{ - assert(self()); - assert(session()); + Cluster::pointer ClusterType::getCluster(const std::string& name) const + { + assert(self()); + assert(session()); - return session()->find() - .where("name = ?").bind(name) - .where("cluster_type_id = ?").bind(getId()).resultValue(); -} + return session()->find() + .where("name = ?").bind(name) + .where("cluster_type_id = ?").bind(getId()).resultValue(); + } -std::vector -ClusterType::getClusters() const -{ - assert(self()); - assert(session()); - - auto res = session()->find() - .where("cluster_type_id = ?").bind(getId()) - .orderBy("name") - .resultList(); - - return std::vector(res.begin(), res.end()); -} + std::vector ClusterType::getClusters() const + { + assert(self()); + assert(session()); + auto res = session()->find() + .where("cluster_type_id = ?").bind(getId()) + .orderBy("name") + .resultList(); + return std::vector(res.begin(), res.end()); + } } // namespace Database - diff --git a/src/libs/services/database/include/services/database/Cluster.hpp b/src/libs/services/database/include/services/database/Cluster.hpp index 77a15671..049f1433 100644 --- a/src/libs/services/database/include/services/database/Cluster.hpp +++ b/src/libs/services/database/include/services/database/Cluster.hpp @@ -33,93 +33,104 @@ namespace Database { -class Track; -class ClusterType; -class ScanSettings; -class Session; + class Track; + class ClusterType; + class ScanSettings; + class Session; -class Cluster final : public Object -{ - public: - Cluster() = default; + class Cluster final : public Object + { + public: + struct FindParameters + { + Range range; + ClusterTypeId clusterType; // if non empty, clusters that belong to this cluster type + TrackId track; // if set, clusters involved in this track - // Find utility - static std::size_t getCount(Session& session); - static RangeResults find(Session& session, Range range); - static pointer find(Session& session, ClusterId id); - static RangeResults findOrphans(Session& session, Range range); + FindParameters& setRange(Range _range) { range = _range; return *this; } + FindParameters& setClusterType(ClusterTypeId _clusterType) { clusterType = _clusterType; return *this; } + FindParameters& setTrack(TrackId _track) { track = _track; return *this; } + }; - // Accessors - const std::string& getName() const { return _name; } - ObjectPtr getType() const { return _clusterType; } - std::size_t getTracksCount() const { return _tracks.size(); } - RangeResults getTracks(Range range) const; - std::size_t getReleasesCount() const; + Cluster() = default; - void addTrack(ObjectPtr track); + // Find utility + static std::size_t getCount(Session& session); + static RangeResults find(Session& session, const FindParameters& range); + static pointer find(Session& session, ClusterId id); + static RangeResults findOrphans(Session& session, Range range); - template - void persist(Action& a) - { - Wt::Dbo::field(a, _name, "name"); + // Accessors + const std::string& getName() const { return _name; } + ObjectPtr getType() const { return _clusterType; } + std::size_t getTracksCount() const { return _tracks.size(); } + RangeResults getTracks(Range range) const; + std::size_t getReleasesCount() const; - Wt::Dbo::belongsTo(a, _clusterType, "cluster_type", Wt::Dbo::OnDeleteCascade); - Wt::Dbo::hasMany(a, _tracks, Wt::Dbo::ManyToMany, "track_cluster", "", Wt::Dbo::OnDeleteCascade); - } + void addTrack(ObjectPtr track); - private: - friend class Session; - Cluster(ObjectPtr type, std::string_view name); - static pointer create(Session& session, ObjectPtr type, std::string_view name); + template + void persist(Action& a) + { + Wt::Dbo::field(a, _name, "name"); - static const std::size_t _maxNameLength = 128; + Wt::Dbo::belongsTo(a, _clusterType, "cluster_type", Wt::Dbo::OnDeleteCascade); + Wt::Dbo::hasMany(a, _tracks, Wt::Dbo::ManyToMany, "track_cluster", "", Wt::Dbo::OnDeleteCascade); + } - std::string _name; + private: + friend class Session; + Cluster(ObjectPtr type, std::string_view name); + static pointer create(Session& session, ObjectPtr type, std::string_view name); - Wt::Dbo::ptr _clusterType; - Wt::Dbo::collection< Wt::Dbo::ptr > _tracks; -}; + static const std::size_t _maxNameLength = 128; + + std::string _name; + + Wt::Dbo::ptr _clusterType; + Wt::Dbo::collection< Wt::Dbo::ptr > _tracks; + }; -class ClusterType final : public Object -{ - public: - ClusterType() = default; + class ClusterType final : public Object + { + public: + ClusterType() = default; - // Getters - static std::size_t getCount(Session& session); - static RangeResults find(Session& session, Range range); - static pointer find(Session& session, std::string_view name); - static pointer find(Session& session, ClusterTypeId id); - static RangeResults findOrphans(Session& session, Range range); - static RangeResults findUsed(Session& session, Range range); + // Getters + static std::size_t getCount(Session& session); + static RangeResults find(Session& session, Range range); + static pointer find(Session& session, std::string_view name); + static pointer find(Session& session, ClusterTypeId id); + static RangeResults findOrphans(Session& session, Range range); + static RangeResults findUsed(Session& session, Range range); - static void remove(Session& session, const std::string& name); + static void remove(Session& session, const std::string& name); - // Accessors - const std::string& getName() const { return _name; } - std::vector getClusters() const; - Cluster::pointer getCluster(const std::string& name) const; + // Accessors + const std::string& getName() const { return _name; } + std::vector getClusters() const; + Cluster::pointer getCluster(const std::string& name) const; - template - void persist(Action& a) - { - Wt::Dbo::field(a, _name, "name"); - Wt::Dbo::hasMany(a, _clusters, Wt::Dbo::ManyToOne, "cluster_type"); - Wt::Dbo::belongsTo(a, _scanSettings, "scan_settings", Wt::Dbo::OnDeleteCascade); - } + template + void persist(Action& a) + { + Wt::Dbo::field(a, _name, "name"); + Wt::Dbo::hasMany(a, _clusters, Wt::Dbo::ManyToOne, "cluster_type"); + Wt::Dbo::belongsTo(a, _scanSettings, "scan_settings", Wt::Dbo::OnDeleteCascade); + } - private: - friend class Session; - ClusterType(std::string_view name); - static pointer create(Session& session, const std::string& name); + private: + friend class Session; + ClusterType(std::string_view name); + static pointer create(Session& session, const std::string& name); - static const std::size_t _maxNameLength = 128; + static const std::size_t _maxNameLength = 128; - std::string _name; - Wt::Dbo::collection< Wt::Dbo::ptr > _clusters; - Wt::Dbo::ptr _scanSettings; -}; + std::string _name; + Wt::Dbo::collection< Wt::Dbo::ptr > _clusters; + Wt::Dbo::ptr _scanSettings; + }; } // namespace Database diff --git a/src/libs/services/database/test/Cluster.cpp b/src/libs/services/database/test/Cluster.cpp index f3d91845..5ed5f649 100644 --- a/src/libs/services/database/test/Cluster.cpp +++ b/src/libs/services/database/test/Cluster.cpp @@ -26,1070 +26,1077 @@ using namespace Database; TEST_F(DatabaseFixture, Cluster) { - { - auto transaction {session.createUniqueTransaction()}; - EXPECT_EQ(Cluster::getCount(session), 0); - EXPECT_EQ(ClusterType::getCount(session), 0); - } + { + auto transaction{ session.createUniqueTransaction() }; + EXPECT_EQ(Cluster::getCount(session), 0); + EXPECT_EQ(ClusterType::getCount(session), 0); + } - ScopedClusterType clusterType {session, "MyType"}; + ScopedClusterType clusterType{ session, "MyType" }; - { - auto transaction {session.createUniqueTransaction()}; - EXPECT_EQ(ClusterType::getCount(session), 1); - } + { + auto transaction{ session.createUniqueTransaction() }; + EXPECT_EQ(ClusterType::getCount(session), 1); + } - { - ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; + { + ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - EXPECT_EQ(Cluster::getCount(session), 1); - EXPECT_EQ(cluster->getType()->getId(), clusterType.getId()); + EXPECT_EQ(Cluster::getCount(session), 1); + EXPECT_EQ(cluster->getType()->getId(), clusterType.getId()); - auto clusters {Cluster::find(session, Range {})}; - ASSERT_EQ(clusters.results.size(), 1); - EXPECT_EQ(clusters.results.front(), cluster.getId()); + auto clusters{ Cluster::find(session, Cluster::FindParameters {}) }; + ASSERT_EQ(clusters.results.size(), 1); + EXPECT_EQ(clusters.results.front(), cluster.getId()); - clusters = Cluster::findOrphans(session, Range {}); - ASSERT_EQ(clusters.results.size(), 1); - EXPECT_EQ(clusters.results.front(), cluster.getId()); + clusters = Cluster::findOrphans(session, Range{}); + ASSERT_EQ(clusters.results.size(), 1); + EXPECT_EQ(clusters.results.front(), cluster.getId()); - auto clusterTypes {ClusterType::find(session, Range {})}; - ASSERT_EQ(clusterTypes.results.size(), 1); - EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); + auto clusterTypes{ ClusterType::find(session, Range {}) }; + ASSERT_EQ(clusterTypes.results.size(), 1); + EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); - clusterTypes = ClusterType::findUsed(session, Range {}); - ASSERT_EQ(clusterTypes.results.size(), 1); - EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); + clusterTypes = ClusterType::findUsed(session, Range{}); + ASSERT_EQ(clusterTypes.results.size(), 1); + EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); - clusterTypes = ClusterType::findOrphans(session, Range {}); - EXPECT_TRUE(clusterTypes.results.empty()); - } - } + clusterTypes = ClusterType::findOrphans(session, Range{}); + EXPECT_TRUE(clusterTypes.results.empty()); + } + } - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - auto clusterTypes {ClusterType::findOrphans(session, Range {})}; - ASSERT_EQ(clusterTypes.results.size(), 1); - EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); + auto clusterTypes{ ClusterType::findOrphans(session, Range {}) }; + ASSERT_EQ(clusterTypes.results.size(), 1); + EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); - ASSERT_TRUE(ClusterType::findUsed(session, Range {}).results.empty()); - } + ASSERT_TRUE(ClusterType::findUsed(session, Range{}).results.empty()); + } } TEST_F(DatabaseFixture, Cluster_singleTrack) { - ScopedTrack track {session, "MyTrack"}; - ScopedClusterType clusterType {session, "MyClusterType"}; + ScopedTrack track{ session, "MyTrack" }; + ScopedClusterType clusterType{ session, "MyClusterType" }; - { - auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Cluster::findOrphans(session, Range {}).results.empty()); - auto clusterTypes {ClusterType::findOrphans(session, Range {})}; - ASSERT_EQ(clusterTypes.results.size(), 1); - EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); - } + { + auto transaction{ session.createSharedTransaction() }; + EXPECT_TRUE(Cluster::findOrphans(session, Range{}).results.empty()); + auto clusterTypes{ ClusterType::findOrphans(session, Range {}) }; + ASSERT_EQ(clusterTypes.results.size(), 1); + EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); + } - ScopedCluster cluster1 {session, clusterType.lockAndGet(), "MyCluster1"}; - ScopedCluster cluster2 {session, clusterType.lockAndGet(), "MyCluster2"}; + ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; + ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; - { - auto transaction {session.createSharedTransaction()}; - auto clusters {Cluster::findOrphans(session, Range {})}; - EXPECT_EQ(clusters.results.size(), 2); - EXPECT_TRUE(track->getClusters().empty()); - EXPECT_TRUE(track->getClusterIds().empty()); - } + { + auto transaction{ session.createSharedTransaction() }; + auto clusters{ Cluster::findOrphans(session, Range {}) }; + EXPECT_EQ(clusters.results.size(), 2); + EXPECT_TRUE(track->getClusters().empty()); + EXPECT_TRUE(track->getClusterIds().empty()); + } - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - cluster1.get().modify()->addTrack(track.get()); - } + cluster1.get().modify()->addTrack(track.get()); + } - { - auto transaction {session.createSharedTransaction()}; - auto clusters {Cluster::findOrphans(session, Range {})}; - ASSERT_EQ(clusters.results.size(), 1); - EXPECT_EQ(clusters.results.front(), cluster2.getId()); + { + auto transaction{ session.createSharedTransaction() }; + auto clusters{ Cluster::find(session, Cluster::FindParameters {}.setTrack(track.getId())) }; + ASSERT_EQ(clusters.results.size(), 1); + EXPECT_EQ(clusters.results.front(), cluster1.getId()); + } - EXPECT_TRUE(ClusterType::findOrphans(session, Range {}).results.empty()); - } + { + auto transaction{ session.createSharedTransaction() }; + auto clusters{ Cluster::findOrphans(session, Range {}) }; + ASSERT_EQ(clusters.results.size(), 1); + EXPECT_EQ(clusters.results.front(), cluster2.getId()); - { - auto transaction {session.createSharedTransaction()}; + EXPECT_TRUE(ClusterType::findOrphans(session, Range{}).results.empty()); + } - auto tracks {Track::find(session, Track::FindParameters {}.setClusters({cluster1.getId()}))}; - ASSERT_EQ(tracks.results.size(), 1); - EXPECT_EQ(tracks.results.front(), track.getId()); + { + auto transaction{ session.createSharedTransaction() }; - tracks = Track::find(session, Track::FindParameters {}.setClusters({cluster2.getId()})); - EXPECT_TRUE(tracks.results.empty()); - } + auto tracks{ Track::find(session, Track::FindParameters {}.setClusters({cluster1.getId()})) }; + ASSERT_EQ(tracks.results.size(), 1); + EXPECT_EQ(tracks.results.front(), track.getId()); - { - auto transaction {session.createSharedTransaction()}; + tracks = Track::find(session, Track::FindParameters{}.setClusters({ cluster2.getId() })); + EXPECT_TRUE(tracks.results.empty()); + } - auto clusters {track->getClusters()}; - ASSERT_EQ(clusters.size(), 1); - EXPECT_EQ(clusters.front()->getId(), cluster1.getId()); + { + auto transaction{ session.createSharedTransaction() }; - auto clusterIds {track->getClusterIds()}; - ASSERT_EQ(clusterIds.size(), 1); - EXPECT_EQ(clusterIds.front(), cluster1.getId()); - } + auto clusters{ track->getClusters() }; + ASSERT_EQ(clusters.size(), 1); + EXPECT_EQ(clusters.front()->getId(), cluster1.getId()); + + auto clusterIds{ track->getClusterIds() }; + ASSERT_EQ(clusterIds.size(), 1); + EXPECT_EQ(clusterIds.front(), cluster1.getId()); + } } TEST_F(DatabaseFixture, Cluster_multiTracks) { - std::list tracks; - ScopedClusterType clusterType {session, "MyClusterType"}; - ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; + std::list tracks; + ScopedClusterType clusterType{ session, "MyClusterType" }; + ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; - for (std::size_t i {}; i < 10; ++i) - { - tracks.emplace_back(session, "MyTrack" + std::to_string(i)); + for (std::size_t i{}; i < 10; ++i) + { + tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - { - auto transaction {session.createUniqueTransaction()}; - cluster.get().modify()->addTrack(tracks.back().get()); - } - } + { + auto transaction{ session.createUniqueTransaction() }; + cluster.get().modify()->addTrack(tracks.back().get()); + } + } - { - auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Cluster::findOrphans(session, Range {}).results.empty()); + { + auto transaction{ session.createSharedTransaction() }; + EXPECT_TRUE(Cluster::findOrphans(session, Range{}).results.empty()); - EXPECT_EQ(cluster->getTracksCount(), tracks.size()); + EXPECT_EQ(cluster->getTracksCount(), tracks.size()); - for (TrackId trackId : cluster->getTracks(Range {}).results) - { - auto it {std::find_if(std::cbegin(tracks), std::cend(tracks), [&](const ScopedTrack& track) { return trackId == track.getId(); })}; - EXPECT_TRUE(it != std::cend(tracks)); - } - } + for (TrackId trackId : cluster->getTracks(Range{}).results) + { + auto it{ std::find_if(std::cbegin(tracks), std::cend(tracks), [&](const ScopedTrack& track) { return trackId == track.getId(); }) }; + EXPECT_TRUE(it != std::cend(tracks)); + } + } } TEST_F(DatabaseFixture, Cluster_multiTracksMultipleClustersTopRelease) { - ScopedClusterType clusterType {session, "ClusterType"}; - ScopedCluster cluster1 {session, clusterType.lockAndGet(), "Cluster1"}; - ScopedCluster cluster2 {session, clusterType.lockAndGet(), "Cluster2"}; - ScopedCluster cluster3 {session, clusterType.lockAndGet(), "Cluster3"}; - ScopedTrack trackA {session, "TrackA"}; - ScopedTrack trackB {session, "TrackB"}; - ScopedTrack trackC {session, "TrackC"}; - ScopedRelease releaseA {session, "ReleaseA"}; - ScopedRelease releaseB {session, "ReleaseB"}; - ScopedRelease releaseC {session, "ReleaseC"}; + ScopedClusterType clusterType{ session, "ClusterType" }; + ScopedCluster cluster1{ session, clusterType.lockAndGet(), "Cluster1" }; + ScopedCluster cluster2{ session, clusterType.lockAndGet(), "Cluster2" }; + ScopedCluster cluster3{ session, clusterType.lockAndGet(), "Cluster3" }; + ScopedTrack trackA{ session, "TrackA" }; + ScopedTrack trackB{ session, "TrackB" }; + ScopedTrack trackC{ session, "TrackC" }; + ScopedRelease releaseA{ session, "ReleaseA" }; + ScopedRelease releaseB{ session, "ReleaseB" }; + ScopedRelease releaseC{ session, "ReleaseC" }; - ScopedUser user {session, "MyUser"}; - ScopedTrackList trackList {session, "TrackList", TrackListType::Playlist, false, user.lockAndGet()}; + ScopedUser user{ session, "MyUser" }; + ScopedTrackList trackList{ session, "TrackList", TrackListType::Playlist, false, user.lockAndGet() }; - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - EXPECT_EQ(trackList->getDuration(), std::chrono::seconds {0}); - } + EXPECT_EQ(trackList->getDuration(), std::chrono::seconds{ 0 }); + } - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - cluster1.get().modify()->addTrack(trackA.get()); - cluster2.get().modify()->addTrack(trackB.get()); - cluster2.get().modify()->addTrack(trackC.get()); - cluster3.get().modify()->addTrack(trackC.get()); + cluster1.get().modify()->addTrack(trackA.get()); + cluster2.get().modify()->addTrack(trackB.get()); + cluster2.get().modify()->addTrack(trackC.get()); + cluster3.get().modify()->addTrack(trackC.get()); - trackA.get().modify()->setRelease(releaseA.get()); - trackB.get().modify()->setRelease(releaseB.get()); - trackC.get().modify()->setRelease(releaseC.get()); - } + trackA.get().modify()->setRelease(releaseA.get()); + trackB.get().modify()->setRelease(releaseB.get()); + trackC.get().modify()->setRelease(releaseC.get()); + } - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - session.create(trackA.get(), trackList.get()); - session.create(trackB.get(), trackList.get()); - session.create(trackB.get(), trackList.get()); - } + session.create(trackA.get(), trackList.get()); + session.create(trackB.get(), trackList.get()); + session.create(trackB.get(), trackList.get()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool hasMore; - const auto releases{trackList->getTopReleases({}, std::nullopt, hasMore)}; - ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0]->getId(), releaseB.getId()); - EXPECT_EQ(releases[1]->getId(), releaseA.getId()); - } + bool hasMore; + const auto releases{ trackList->getTopReleases({}, std::nullopt, hasMore) }; + ASSERT_EQ(releases.size(), 2); + EXPECT_EQ(releases[0]->getId(), releaseB.getId()); + EXPECT_EQ(releases[1]->getId(), releaseA.getId()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool hasMore; - auto releases{trackList->getTopReleases({cluster1.getId()}, std::nullopt, hasMore)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases[0]->getId(), releaseA.getId()); + bool hasMore; + auto releases{ trackList->getTopReleases({cluster1.getId()}, std::nullopt, hasMore) }; + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases[0]->getId(), releaseA.getId()); - releases = trackList->getTopReleases({cluster2.getId()}, std::nullopt, hasMore); - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases[0]->getId(), releaseB.getId()); + releases = trackList->getTopReleases({ cluster2.getId() }, std::nullopt, hasMore); + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases[0]->getId(), releaseB.getId()); - releases = trackList->getTopReleases({cluster2.getId(), cluster1.getId()}, std::nullopt, hasMore); - EXPECT_TRUE(releases.empty()); + releases = trackList->getTopReleases({ cluster2.getId(), cluster1.getId() }, std::nullopt, hasMore); + EXPECT_TRUE(releases.empty()); - releases = trackList->getTopReleases({cluster2.getId(), cluster3.getId()}, std::nullopt, hasMore); - EXPECT_TRUE(releases.empty()); - } + releases = trackList->getTopReleases({ cluster2.getId(), cluster3.getId() }, std::nullopt, hasMore); + EXPECT_TRUE(releases.empty()); + } - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - session.create(trackC.get(), trackList.get()); - session.create(trackC.get(), trackList.get()); - session.create(trackC.get(), trackList.get()); - } + session.create(trackC.get(), trackList.get()); + session.create(trackC.get(), trackList.get()); + session.create(trackC.get(), trackList.get()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool hasMore; - auto releases {trackList->getTopReleases({cluster2.getId(), cluster3.getId()}, std::nullopt, hasMore)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases[0]->getId(), releaseC.getId()); + bool hasMore; + auto releases{ trackList->getTopReleases({cluster2.getId(), cluster3.getId()}, std::nullopt, hasMore) }; + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases[0]->getId(), releaseC.getId()); - releases = trackList->getTopReleases({cluster2.getId()}, std::nullopt, hasMore); - ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0]->getId(), releaseC.getId()); - EXPECT_EQ(releases[1]->getId(), releaseB.getId()); - } + releases = trackList->getTopReleases({ cluster2.getId() }, std::nullopt, hasMore); + ASSERT_EQ(releases.size(), 2); + EXPECT_EQ(releases[0]->getId(), releaseC.getId()); + EXPECT_EQ(releases[1]->getId(), releaseB.getId()); + } } TEST_F(DatabaseFixture, Cluster_singleTrackSingleReleaseSingleCluster) { - ScopedTrack track {session, "MyTrackFile"}; - ScopedRelease release {session, "MyRelease"}; + ScopedTrack track{ session, "MyTrackFile" }; + ScopedRelease release{ session, "MyRelease" }; - { - auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Cluster::findOrphans(session, Range {}).results.empty()); - } + { + auto transaction{ session.createSharedTransaction() }; + EXPECT_TRUE(Cluster::findOrphans(session, Range{}).results.empty()); + } - ScopedClusterType clusterType {session, "MyClusterType"}; - ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; - ScopedCluster unusedCluster {session, clusterType.lockAndGet(), "MyClusterUnused"}; + ScopedClusterType clusterType{ session, "MyClusterType" }; + ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; + ScopedCluster unusedCluster{ session, clusterType.lockAndGet(), "MyClusterUnused" }; - { - auto transaction {session.createSharedTransaction()}; - ASSERT_EQ(Cluster::findOrphans(session, Range {}).results.size(), 2); - EXPECT_TRUE(Release::find(session, Release::FindParameters {}.setClusters({unusedCluster.getId()})).results.empty()); - EXPECT_EQ(Release::find(session, Release::FindParameters {}).results.size(), 1); - } + { + auto transaction{ session.createSharedTransaction() }; + ASSERT_EQ(Cluster::findOrphans(session, Range{}).results.size(), 2); + EXPECT_TRUE(Release::find(session, Release::FindParameters{}.setClusters({ unusedCluster.getId() })).results.empty()); + EXPECT_EQ(Release::find(session, Release::FindParameters{}).results.size(), 1); + } - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - track.get().modify()->setRelease(release.get()); - cluster.get().modify()->addTrack(track.get()); - } + track.get().modify()->setRelease(release.get()); + cluster.get().modify()->addTrack(track.get()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - { - auto clusters {Cluster::findOrphans(session, Range {})}; - ASSERT_EQ(clusters.results.size(), 1); - EXPECT_EQ(clusters.results.front(), unusedCluster.getId()); - } - } + { + auto clusters{ Cluster::findOrphans(session, Range {}) }; + ASSERT_EQ(clusters.results.size(), 1); + EXPECT_EQ(clusters.results.front(), unusedCluster.getId()); + } + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - auto releases {Release::find(session, Release::FindParameters {}.setClusters({cluster.getId()}))}; - ASSERT_EQ(releases.results.size(), 1); - EXPECT_EQ(releases.results.front(), release.getId()); - } + auto releases{ Release::find(session, Release::FindParameters {}.setClusters({cluster.getId()})) }; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - auto releases {Release::find(session, Release::FindParameters {}.setClusters({unusedCluster.getId()}))}; - EXPECT_EQ(releases.results.size(), 0); - } + auto releases{ Release::find(session, Release::FindParameters {}.setClusters({unusedCluster.getId()})) }; + EXPECT_EQ(releases.results.size(), 0); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - EXPECT_EQ(cluster->getReleasesCount(), 1); - EXPECT_EQ(cluster->getTracksCount(), 1); - EXPECT_EQ(unusedCluster->getReleasesCount(), 0); - EXPECT_EQ(unusedCluster->getTracksCount(), 0); - } + EXPECT_EQ(cluster->getReleasesCount(), 1); + EXPECT_EQ(cluster->getTracksCount(), 1); + EXPECT_EQ(unusedCluster->getReleasesCount(), 0); + EXPECT_EQ(unusedCluster->getTracksCount(), 0); + } } TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) { - ScopedTrack track {session, "MyTrackFile"}; - ScopedArtist artist {session, "MyArtist"}; - ScopedClusterType clusterType {session, "MyType"}; - ScopedCluster cluster1 {session, clusterType.lockAndGet(), "Cluster1"}; - ScopedCluster cluster2 {session, clusterType.lockAndGet(), "Cluster2"}; - ScopedCluster cluster3 {session, clusterType.lockAndGet(), "Cluster3"}; - { - auto transaction {session.createUniqueTransaction()}; + ScopedTrack track{ session, "MyTrackFile" }; + ScopedArtist artist{ session, "MyArtist" }; + ScopedClusterType clusterType{ session, "MyType" }; + ScopedCluster cluster1{ session, clusterType.lockAndGet(), "Cluster1" }; + ScopedCluster cluster2{ session, clusterType.lockAndGet(), "Cluster2" }; + ScopedCluster cluster3{ session, clusterType.lockAndGet(), "Cluster3" }; + { + auto transaction{ session.createUniqueTransaction() }; - auto trackArtistLink {TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist)}; - cluster1.get().modify()->addTrack(track.get()); - } + auto trackArtistLink{ TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist) }; + cluster1.get().modify()->addTrack(track.get()); + } - { - auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(ClusterType::findOrphans(session, Range {}).results.empty()); - EXPECT_EQ(Cluster::findOrphans(session, Range {}).results.size(), 2); - EXPECT_TRUE(Release::findOrphans(session, Range {}).results.empty()); - EXPECT_TRUE(Artist::findAllOrphans(session, Range {}).results.empty()); - } + { + auto transaction{ session.createSharedTransaction() }; + EXPECT_TRUE(ClusterType::findOrphans(session, Range{}).results.empty()); + EXPECT_EQ(Cluster::findOrphans(session, Range{}).results.size(), 2); + EXPECT_TRUE(Release::findOrphans(session, Range{}).results.empty()); + EXPECT_TRUE(Artist::findAllOrphans(session, Range{}).results.empty()); + } - { - auto transaction {session.createSharedTransaction()}; - EXPECT_EQ(track->getClusters().size(), 1); - EXPECT_EQ(track->getClusterIds().size(), 1); - } + { + auto transaction{ session.createSharedTransaction() }; + EXPECT_EQ(track->getClusters().size(), 1); + EXPECT_EQ(track->getClusterIds().size(), 1); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - auto artists {Artist::find(session, Artist::FindParameters {}.setClusters({cluster1.getId()}))}; - ASSERT_EQ(artists.results.size(), 1); - EXPECT_EQ(artists.results.front(), artist.getId()); + auto artists{ Artist::find(session, Artist::FindParameters {}.setClusters({cluster1.getId()})) }; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); - EXPECT_TRUE(Artist::find(session, Artist::FindParameters {}.setClusters({cluster2.getId()})).results.empty()); - EXPECT_TRUE(Artist::find(session, Artist::FindParameters {}.setClusters({cluster3.getId()})).results.empty()); + EXPECT_TRUE(Artist::find(session, Artist::FindParameters{}.setClusters({ cluster2.getId() })).results.empty()); + EXPECT_TRUE(Artist::find(session, Artist::FindParameters{}.setClusters({ cluster3.getId() })).results.empty()); - cluster2.get().modify()->addTrack(track.get()); - } + cluster2.get().modify()->addTrack(track.get()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - auto artists {Artist::find(session, Artist::FindParameters {}.setClusters({cluster1.getId()}))}; - ASSERT_EQ(artists.results.size(), 1); - EXPECT_EQ(artists.results.front(), artist.getId()); + auto artists{ Artist::find(session, Artist::FindParameters {}.setClusters({cluster1.getId()})) }; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); - artists = Artist::find(session, Artist::FindParameters {}.setClusters({cluster2.getId()})); - ASSERT_EQ(artists.results.size(), 1); - EXPECT_EQ(artists.results.front(), artist.getId()); + artists = Artist::find(session, Artist::FindParameters{}.setClusters({ cluster2.getId() })); + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); - artists = Artist::find(session, Artist::FindParameters {}.setClusters({cluster1.getId()})); - ASSERT_EQ(artists.results.size(), 1); - EXPECT_EQ(artists.results.front(), artist.getId()); + artists = Artist::find(session, Artist::FindParameters{}.setClusters({ cluster1.getId() })); + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); - EXPECT_TRUE(Artist::find(session, Artist::FindParameters {}.setClusters({cluster3.getId()})).results.empty()); - } + EXPECT_TRUE(Artist::find(session, Artist::FindParameters{}.setClusters({ cluster3.getId() })).results.empty()); + } } TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiRolesMultiClusters) { - ScopedTrack track {session, "MyTrackFile"}; - ScopedArtist artist {session, "MyArtist"}; - ScopedClusterType clusterType {session, "MyType"}; - ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; + ScopedTrack track{ session, "MyTrackFile" }; + ScopedArtist artist{ session, "MyArtist" }; + ScopedClusterType clusterType{ session, "MyType" }; + ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); - TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::ReleaseArtist); - cluster.get().modify()->addTrack(track.get()); - } + TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); + TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::ReleaseArtist); + cluster.get().modify()->addTrack(track.get()); + } - { - auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Cluster::findOrphans(session, Range {}).results.empty()); - EXPECT_TRUE(Release::findOrphans(session, Range {}).results.empty()); - EXPECT_TRUE(Artist::findAllOrphans(session, Range {}).results.empty()); - } + { + auto transaction{ session.createSharedTransaction() }; + EXPECT_TRUE(Cluster::findOrphans(session, Range{}).results.empty()); + EXPECT_TRUE(Release::findOrphans(session, Range{}).results.empty()); + EXPECT_TRUE(Artist::findAllOrphans(session, Range{}).results.empty()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - auto artists {Artist::find(session, Artist::FindParameters {}.setClusters({cluster.getId()}))}; - ASSERT_EQ(artists.results.size(), 1); - EXPECT_EQ(artists.results.front(), artist.getId()); - } + auto artists{ Artist::find(session, Artist::FindParameters {}.setClusters({cluster.getId()})) }; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); + } } TEST_F(DatabaseFixture, MultiTracksSingleArtistMultiClusters) { - constexpr std::size_t nbTracks {10}; - constexpr std::size_t nbClusters {5}; + constexpr std::size_t nbTracks{ 10 }; + constexpr std::size_t nbClusters{ 5 }; - std::list tracks; - std::list clusters; - ScopedArtist artist {session, "MyArtist"}; - ScopedClusterType clusterType {session, "MyType"}; + std::list tracks; + std::list clusters; + ScopedArtist artist{ session, "MyArtist" }; + ScopedClusterType clusterType{ session, "MyType" }; - for (std::size_t i {}; i < nbClusters; ++i) - clusters.emplace_back(session, clusterType.lockAndGet(), "MyCluster" + std::to_string(i)); + for (std::size_t i{}; i < nbClusters; ++i) + clusters.emplace_back(session, clusterType.lockAndGet(), "MyCluster" + std::to_string(i)); - for (std::size_t i {}; i < nbTracks ; ++i) - { - tracks.emplace_back(session, "MyTrackFile" + std::to_string(i)); + for (std::size_t i{}; i < nbTracks; ++i) + { + tracks.emplace_back(session, "MyTrackFile" + std::to_string(i)); - auto transaction {session.createUniqueTransaction()}; - TrackArtistLink::create(session, tracks.back().get(), artist.get(), TrackArtistLinkType::Artist); + auto transaction{ session.createUniqueTransaction() }; + TrackArtistLink::create(session, tracks.back().get(), artist.get(), TrackArtistLinkType::Artist); - for (auto& cluster : clusters) - cluster.get().modify()->addTrack(tracks.back().get()); - } + for (auto& cluster : clusters) + cluster.get().modify()->addTrack(tracks.back().get()); + } - { - auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Cluster::findOrphans(session, Range {}).results.empty()); - EXPECT_TRUE(Artist::findAllOrphans(session, Range {}).results.empty()); - } + { + auto transaction{ session.createSharedTransaction() }; + EXPECT_TRUE(Cluster::findOrphans(session, Range{}).results.empty()); + EXPECT_TRUE(Artist::findAllOrphans(session, Range{}).results.empty()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - std::vector clusterIds; - std::transform(std::cbegin(clusters), std::cend(clusters), std::back_inserter(clusterIds), [](const ScopedCluster& cluster) { return cluster.getId(); }); + std::vector clusterIds; + std::transform(std::cbegin(clusters), std::cend(clusters), std::back_inserter(clusterIds), [](const ScopedCluster& cluster) { return cluster.getId(); }); - auto artists {Artist::find(session, Artist::FindParameters {}.setClusters(clusterIds))}; - ASSERT_EQ(artists.results.size(), 1); - EXPECT_EQ(artists.results.front(), artist.getId()); - } + auto artists{ Artist::find(session, Artist::FindParameters {}.setClusters(clusterIds)) }; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); + } } TEST_F(DatabaseFixture, MultipleTracksSingleClusterSimilarity) { - std::list tracks; - ScopedClusterType clusterType {session, "MyClusterType"}; - ScopedCluster cluster {session, clusterType.lockAndGet(), "MyClusterType"}; + std::list tracks; + ScopedClusterType clusterType{ session, "MyClusterType" }; + ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyClusterType" }; - for (std::size_t i {}; i < 10; ++i) - { - tracks.emplace_back(session, "MyTrack" + std::to_string(i)); + for (std::size_t i{}; i < 10; ++i) + { + tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - { - auto transaction {session.createUniqueTransaction()}; - cluster.get().modify()->addTrack(tracks.back().get()); - } - } + { + auto transaction{ session.createUniqueTransaction() }; + cluster.get().modify()->addTrack(tracks.back().get()); + } + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - const auto similarTracks {Track::findSimilarTracks(session, {tracks.front().getId()}, Range {})}; - EXPECT_EQ(similarTracks.results.size(), tracks.size() - 1); - for (const TrackId similarTrackId : similarTracks.results) - { - EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 1), std::cend(tracks), [&](const auto& track) { return similarTrackId == track.getId(); }) != std::cend(tracks)); - } - } + const auto similarTracks{ Track::findSimilarTracks(session, {tracks.front().getId()}, Range {}) }; + EXPECT_EQ(similarTracks.results.size(), tracks.size() - 1); + for (const TrackId similarTrackId : similarTracks.results) + { + EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 1), std::cend(tracks), [&](const auto& track) { return similarTrackId == track.getId(); }) != std::cend(tracks)); + } + } } TEST_F(DatabaseFixture, MultipleTracksMultipleClustersSimilarity) { - std::list tracks; - ScopedClusterType clusterType {session, "MyClusterType"}; - ScopedCluster cluster1 {session, clusterType.lockAndGet(), "MyCluster1"}; - ScopedCluster cluster2 {session, clusterType.lockAndGet(), "MyCluster2"}; + std::list tracks; + ScopedClusterType clusterType{ session, "MyClusterType" }; + ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; + ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; - for (std::size_t i {}; i < 5; ++i) - { - tracks.emplace_back(session, "MyTrack" + std::to_string(i)); + for (std::size_t i{}; i < 5; ++i) + { + tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - { - auto transaction {session.createUniqueTransaction()}; - cluster1.get().modify()->addTrack(tracks.back().get()); - } - } + { + auto transaction{ session.createUniqueTransaction() }; + cluster1.get().modify()->addTrack(tracks.back().get()); + } + } - for (std::size_t i {5}; i < 10; ++i) - { - tracks.emplace_back(session, "MyTrack" + std::to_string(i)); + for (std::size_t i{ 5 }; i < 10; ++i) + { + tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - { - auto transaction {session.createUniqueTransaction()}; - cluster1.get().modify()->addTrack(tracks.back().get()); - cluster2.get().modify()->addTrack(tracks.back().get()); - } - } + { + auto transaction{ session.createUniqueTransaction() }; + cluster1.get().modify()->addTrack(tracks.back().get()); + cluster2.get().modify()->addTrack(tracks.back().get()); + } + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - { - auto similarTracks {Track::findSimilarTracks(session, {tracks.back().getId()}, Range {0, 4})}; - EXPECT_EQ(similarTracks.results.size(), 4); - for (const TrackId similarTrackId : similarTracks.results) - EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 5), std::next(std::cend(tracks), -1), [&](const auto& track) { return similarTrackId == track.getId(); }) != std::cend(tracks)); - } + { + auto similarTracks{ Track::findSimilarTracks(session, {tracks.back().getId()}, Range {0, 4}) }; + EXPECT_EQ(similarTracks.results.size(), 4); + for (const TrackId similarTrackId : similarTracks.results) + EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 5), std::next(std::cend(tracks), -1), [&](const auto& track) { return similarTrackId == track.getId(); }) != std::cend(tracks)); + } - { - auto similarTracks {Track::findSimilarTracks(session, {tracks.front().getId()}, Range {})}; - EXPECT_EQ(similarTracks.results.size(), tracks.size() - 1); - for (const TrackId similarTrackId : similarTracks.results) - EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 1), std::cend(tracks), [&](const auto& track) { return similarTrackId == track.getId(); }) != std::cend(tracks)); - } - } + { + auto similarTracks{ Track::findSimilarTracks(session, {tracks.front().getId()}, Range {}) }; + EXPECT_EQ(similarTracks.results.size(), tracks.size() - 1); + for (const TrackId similarTrackId : similarTracks.results) + EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 1), std::cend(tracks), [&](const auto& track) { return similarTrackId == track.getId(); }) != std::cend(tracks)); + } + } } TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistSingleCluster) { - ScopedTrack track {session, "MyTrack"}; - ScopedRelease release {session, "MyRelease"}; - ScopedArtist artist {session, "MyArtist"}; - ScopedClusterType clusterType {session, "MyType"}; - ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; + ScopedTrack track{ session, "MyTrack" }; + ScopedRelease release{ session, "MyRelease" }; + ScopedArtist artist{ session, "MyArtist" }; + ScopedClusterType clusterType{ session, "MyType" }; + ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); - track.get().modify()->setRelease(release.get()); - cluster.get().modify()->addTrack(track.get()); - } + TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); + track.get().modify()->setRelease(release.get()); + cluster.get().modify()->addTrack(track.get()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - EXPECT_TRUE(Cluster::findOrphans(session, Range {}).results.empty()); - EXPECT_TRUE(ClusterType::findOrphans(session, Range {}).results.empty()); - EXPECT_TRUE(Artist::findAllOrphans(session, Range {}).results.empty()); - EXPECT_TRUE(Release::findOrphans(session, Range {}).results.empty()); - } + EXPECT_TRUE(Cluster::findOrphans(session, Range{}).results.empty()); + EXPECT_TRUE(ClusterType::findOrphans(session, Range{}).results.empty()); + EXPECT_TRUE(Artist::findAllOrphans(session, Range{}).results.empty()); + EXPECT_TRUE(Release::findOrphans(session, Range{}).results.empty()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - auto artists {Artist::find(session, Artist::FindParameters {}.setClusters({cluster.getId()}))}; - ASSERT_EQ(artists.results.size(), 1); - EXPECT_EQ(artists.results.front(), artist.getId()); + auto artists{ Artist::find(session, Artist::FindParameters {}.setClusters({cluster.getId()})) }; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist.getId()); - auto releases {Release::find(session, Release::FindParameters {}.setArtist(artist.getId()))}; - ASSERT_EQ(releases.results.size(), 1); - EXPECT_EQ(releases.results.front(), release.getId()); + auto releases{ Release::find(session, Release::FindParameters {}.setArtist(artist.getId())) }; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); - releases = Release::find(session, Release::FindParameters {}.setArtist(artist.getId()).setClusters({cluster.getId()})); - ASSERT_EQ(releases.results.size(), 1); - EXPECT_EQ(releases.results.front(), release.getId()); - } + releases = Release::find(session, Release::FindParameters{}.setArtist(artist.getId()).setClusters({ cluster.getId() })); + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); + } } TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistMultiClusters) { - ScopedTrack track {session, "MyTrack"}; - ScopedRelease release {session, "MyRelease"}; - ScopedArtist artist {session, "MyArtist"}; - ScopedClusterType clusterType {session, "MyClusterType"}; - ScopedCluster cluster1 {session, clusterType.lockAndGet(), "MyCluster1"}; - ScopedCluster cluster2 {session, clusterType.lockAndGet(), "MyCluster2"}; + ScopedTrack track{ session, "MyTrack" }; + ScopedRelease release{ session, "MyRelease" }; + ScopedArtist artist{ session, "MyArtist" }; + ScopedClusterType clusterType{ session, "MyClusterType" }; + ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; + ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - auto trackArtistLink {TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist)}; - track.get().modify()->setRelease(release.get()); - cluster1.get().modify()->addTrack(track.get()); - cluster2.get().modify()->addTrack(track.get()); - } + auto trackArtistLink{ TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist) }; + track.get().modify()->setRelease(release.get()); + cluster1.get().modify()->addTrack(track.get()); + cluster2.get().modify()->addTrack(track.get()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - auto releases {Release::find(session, Release::FindParameters {}.setArtist(artist.getId()))}; - ASSERT_EQ(releases.results.size(), 1); - EXPECT_EQ(releases.results.front(), release.getId()); + auto releases{ Release::find(session, Release::FindParameters {}.setArtist(artist.getId())) }; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); - releases = Release::find(session, Release::FindParameters {}.setArtist(artist.getId()).setClusters({cluster1.getId(), cluster2.getId()})); - ASSERT_EQ(releases.results.size(), 1); - EXPECT_EQ(releases.results.front(), release.getId()); - } + releases = Release::find(session, Release::FindParameters{}.setArtist(artist.getId()).setClusters({ cluster1.getId(), cluster2.getId() })); + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); + } } TEST_F(DatabaseFixture, SingleTrackListMultipleTrackSingleCluster) { - ScopedUser user {session, "MyUser"}; - ScopedTrackList trackList {session, "MyTrackList", TrackListType::Playlist, false, user.lockAndGet()}; - ScopedClusterType clusterType {session, "MyClusterType"}; - ScopedCluster cluster {session, clusterType.lockAndGet(), "MyCluster"}; - std::list tracks; + ScopedUser user{ session, "MyUser" }; + ScopedTrackList trackList{ session, "MyTrackList", TrackListType::Playlist, false, user.lockAndGet() }; + ScopedClusterType clusterType{ session, "MyClusterType" }; + ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; + std::list tracks; - for (std::size_t i {}; i < 20; ++i) - { - tracks.emplace_back(session, "MyTrack" + std::to_string(i)); + for (std::size_t i{}; i < 20; ++i) + { + tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - auto transaction {session.createUniqueTransaction()}; + auto transaction{ session.createUniqueTransaction() }; - if (i < 5) - session.create(tracks.back().get(), trackList.get()); + if (i < 5) + session.create(tracks.back().get(), trackList.get()); - if (i < 10) - cluster.get().modify()->addTrack(tracks.back().get()); - } + if (i < 10) + cluster.get().modify()->addTrack(tracks.back().get()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - const auto similarTracks {trackList->getSimilarTracks()}; - EXPECT_EQ(similarTracks.size(), 5); + const auto similarTracks{ trackList->getSimilarTracks() }; + EXPECT_EQ(similarTracks.size(), 5); - for (auto similarTrack : similarTracks) - EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 5), std::cend(tracks), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack->getId(); })); - } + for (auto similarTrack : similarTracks) + EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 5), std::cend(tracks), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack->getId(); })); + } } TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClusters) { - ScopedUser user {session, "MyUser"}; - ScopedTrackList trackList {session, "MyTrackList", TrackListType::Playlist, false, user.lockAndGet()}; - ScopedClusterType clusterType {session, "MyClusterType"}; - ScopedCluster cluster1 {session, clusterType.lockAndGet(), "MyCluster1"}; - ScopedCluster cluster2 {session, clusterType.lockAndGet(), "MyCluster2"}; - std::list tracks; + ScopedUser user{ session, "MyUser" }; + ScopedTrackList trackList{ session, "MyTrackList", TrackListType::Playlist, false, user.lockAndGet() }; + ScopedClusterType clusterType{ session, "MyClusterType" }; + ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; + ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; + std::list tracks; - for (std::size_t i {}; i < 20; ++i) - { - tracks.emplace_back(session, "MyTrack" + std::to_string(i)); + for (std::size_t i{}; i < 20; ++i) + { + tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - auto transaction {session.createUniqueTransaction()}; + auto transaction{ session.createUniqueTransaction() }; - if (i < 5) - session.create(tracks.back().get(), trackList.get()); + if (i < 5) + session.create(tracks.back().get(), trackList.get()); - if (i < 10) - { - cluster1.get().modify()->addTrack(tracks.back().get()); - cluster2.get().modify()->addTrack(tracks.back().get()); - } - else if (i < 15) - { - cluster1.get().modify()->addTrack(tracks.back().get()); - } - } + if (i < 10) + { + cluster1.get().modify()->addTrack(tracks.back().get()); + cluster2.get().modify()->addTrack(tracks.back().get()); + } + else if (i < 15) + { + cluster1.get().modify()->addTrack(tracks.back().get()); + } + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - { - const auto similarTracks {trackList->getSimilarTracks(0, 5)}; - ASSERT_EQ(similarTracks.size(), 5); + { + const auto similarTracks{ trackList->getSimilarTracks(0, 5) }; + ASSERT_EQ(similarTracks.size(), 5); - for (auto similarTrack : similarTracks) - EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 5), std::next(std::cbegin(tracks), 10), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack->getId(); })); - } + for (auto similarTrack : similarTracks) + EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 5), std::next(std::cbegin(tracks), 10), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack->getId(); })); + } - { - const auto similarTracks {trackList->getSimilarTracks(5, 10)}; - ASSERT_EQ(similarTracks.size(), 5); + { + const auto similarTracks{ trackList->getSimilarTracks(5, 10) }; + ASSERT_EQ(similarTracks.size(), 5); - for (auto similarTrack : similarTracks) - EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 10), std::next(std::cbegin(tracks), 15), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack->getId(); })); - } + for (auto similarTrack : similarTracks) + EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 10), std::next(std::cbegin(tracks), 15), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack->getId(); })); + } - EXPECT_TRUE(trackList->getSimilarTracks(10, 10).empty()); - } + EXPECT_TRUE(trackList->getSimilarTracks(10, 10).empty()); + } } TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClustersRecentlyPlayed) { - ScopedUser user {session, "MyUser"}; - ScopedTrackList trackList {session, "MyTrackList", TrackListType::Playlist, false, user.lockAndGet()}; - ScopedClusterType clusterType {session, "MyClusterType"}; - ScopedCluster cluster1 {session, clusterType.lockAndGet(), "MyCluster1"}; - ScopedCluster cluster2 {session, clusterType.lockAndGet(), "MyCluster2"}; - ScopedCluster cluster3 {session, clusterType.lockAndGet(), "MyCluster3"}; - ScopedTrack track1 {session, "MyTrack1"}; - ScopedTrack track2 {session, "MyTrack1"}; - ScopedArtist artist1 {session, "MyArtist1"}; - ScopedArtist artist2 {session, "MyArtist2"}; - ScopedRelease release1 {session, "MyRelease1"}; - ScopedRelease release2 {session, "MyRelease2"}; + ScopedUser user{ session, "MyUser" }; + ScopedTrackList trackList{ session, "MyTrackList", TrackListType::Playlist, false, user.lockAndGet() }; + ScopedClusterType clusterType{ session, "MyClusterType" }; + ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; + ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; + ScopedCluster cluster3{ session, clusterType.lockAndGet(), "MyCluster3" }; + ScopedTrack track1{ session, "MyTrack1" }; + ScopedTrack track2{ session, "MyTrack1" }; + ScopedArtist artist1{ session, "MyArtist1" }; + ScopedArtist artist2{ session, "MyArtist2" }; + ScopedRelease release1{ session, "MyRelease1" }; + ScopedRelease release2{ session, "MyRelease2" }; - const Wt::WDateTime now {Wt::WDateTime::currentDateTime()}; + const Wt::WDateTime now{ Wt::WDateTime::currentDateTime() }; - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - track1.get().modify()->setRelease(release1.get()); - track2.get().modify()->setRelease(release2.get()); - TrackArtistLink::create(session, track1.get(), artist1.get(), TrackArtistLinkType::Artist); - TrackArtistLink::create(session, track2.get(), artist2.get(), TrackArtistLinkType::Artist); + track1.get().modify()->setRelease(release1.get()); + track2.get().modify()->setRelease(release2.get()); + TrackArtistLink::create(session, track1.get(), artist1.get(), TrackArtistLinkType::Artist); + TrackArtistLink::create(session, track2.get(), artist2.get(), TrackArtistLinkType::Artist); - cluster1.get().modify()->addTrack(track1.get()); - cluster2.get().modify()->addTrack(track2.get()); - cluster3.get().modify()->addTrack(track1.get()); - cluster3.get().modify()->addTrack(track2.get()); - } + cluster1.get().modify()->addTrack(track1.get()); + cluster2.get().modify()->addTrack(track2.get()); + cluster3.get().modify()->addTrack(track1.get()); + cluster3.get().modify()->addTrack(track2.get()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool moreResults {}; - EXPECT_TRUE(trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults).empty()); - EXPECT_TRUE(trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults).empty()); - EXPECT_TRUE(trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults).empty()); - } + bool moreResults{}; + EXPECT_TRUE(trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults).empty()); + EXPECT_TRUE(trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults).empty()); + EXPECT_TRUE(trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults).empty()); + } - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - session.create(track1.get(), trackList.get(), now); - } + session.create(track1.get(), trackList.get(), now); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool moreResults {}; - const auto artists {trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist1.getId()); + bool moreResults{}; + const auto artists{ trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults) }; + ASSERT_EQ(artists.size(), 1); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); - const auto releases {trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults)}; - ASSERT_EQ(releases.size() , 1); - EXPECT_EQ(releases.front()->getId(), release1.getId()); + const auto releases{ trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults) }; + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases.front()->getId(), release1.getId()); - const auto tracks {trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults)}; - ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front()->getId(), track1.getId()); - } + const auto tracks{ trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults) }; + ASSERT_EQ(tracks.size(), 1); + EXPECT_EQ(tracks.front()->getId(), track1.getId()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool moreResults {}; - const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster1.getId()}, std::nullopt, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist1.getId()); + bool moreResults{}; + const auto artists{ trackList->getArtistsOrderedByRecentFirst({cluster1.getId()}, std::nullopt, std::nullopt, moreResults) }; + ASSERT_EQ(artists.size(), 1); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); - const auto releases {trackList->getReleasesOrderedByRecentFirst({cluster1.getId()}, std::nullopt, moreResults)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release1.getId()); + const auto releases{ trackList->getReleasesOrderedByRecentFirst({cluster1.getId()}, std::nullopt, moreResults) }; + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases.front()->getId(), release1.getId()); - const auto tracks {trackList->getTracksOrderedByRecentFirst({cluster1.getId()}, std::nullopt, moreResults)}; - ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front()->getId(), track1.getId()); - } + const auto tracks{ trackList->getTracksOrderedByRecentFirst({cluster1.getId()}, std::nullopt, moreResults) }; + ASSERT_EQ(tracks.size(), 1); + EXPECT_EQ(tracks.front()->getId(), track1.getId()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool moreResults {}; - const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster3.getId()}, std::nullopt, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist1.getId()); + bool moreResults{}; + const auto artists{ trackList->getArtistsOrderedByRecentFirst({cluster3.getId()}, std::nullopt, std::nullopt, moreResults) }; + ASSERT_EQ(artists.size(), 1); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); - const auto releases {trackList->getReleasesOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release1.getId()); + const auto releases{ trackList->getReleasesOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults) }; + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases.front()->getId(), release1.getId()); - const auto tracks {trackList->getTracksOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults)}; - ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front()->getId(), track1.getId()); - } + const auto tracks{ trackList->getTracksOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults) }; + ASSERT_EQ(tracks.size(), 1); + EXPECT_EQ(tracks.front()->getId(), track1.getId()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool moreResults {}; - const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster1.getId()}, TrackArtistLinkType::Artist, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist1.getId()); - } + bool moreResults{}; + const auto artists{ trackList->getArtistsOrderedByRecentFirst({cluster1.getId()}, TrackArtistLinkType::Artist, std::nullopt, moreResults) }; + ASSERT_EQ(artists.size(), 1); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool moreResults {}; - const auto artists {trackList->getArtistsOrderedByRecentFirst({}, TrackArtistLinkType::Artist, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist1.getId()); - } + bool moreResults{}; + const auto artists{ trackList->getArtistsOrderedByRecentFirst({}, TrackArtistLinkType::Artist, std::nullopt, moreResults) }; + ASSERT_EQ(artists.size(), 1); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool moreResults {}; - EXPECT_TRUE(trackList->getArtistsOrderedByRecentFirst({cluster2.getId()}, std::nullopt, std::nullopt, moreResults).empty()); - EXPECT_TRUE(trackList->getReleasesOrderedByRecentFirst({cluster2.getId()}, std::nullopt, moreResults).empty()); - EXPECT_TRUE(trackList->getTracksOrderedByRecentFirst({cluster2.getId()}, std::nullopt, moreResults).empty()); + bool moreResults{}; + EXPECT_TRUE(trackList->getArtistsOrderedByRecentFirst({ cluster2.getId() }, std::nullopt, std::nullopt, moreResults).empty()); + EXPECT_TRUE(trackList->getReleasesOrderedByRecentFirst({ cluster2.getId() }, std::nullopt, moreResults).empty()); + EXPECT_TRUE(trackList->getTracksOrderedByRecentFirst({ cluster2.getId() }, std::nullopt, moreResults).empty()); - EXPECT_TRUE(trackList->getArtistsOrderedByRecentFirst({}, TrackArtistLinkType::ReleaseArtist, std::nullopt, moreResults).empty()); - } + EXPECT_TRUE(trackList->getArtistsOrderedByRecentFirst({}, TrackArtistLinkType::ReleaseArtist, std::nullopt, moreResults).empty()); + } - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - session.create(track2.get(), trackList.get(), now.addSecs(1)); - } + session.create(track2.get(), trackList.get(), now.addSecs(1)); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool moreResults {}; - const auto artists {trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0]->getId(), artist2.getId()); - EXPECT_EQ(artists[1]->getId(), artist1.getId()); + bool moreResults{}; + const auto artists{ trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults) }; + ASSERT_EQ(artists.size(), 2); + EXPECT_EQ(artists[0]->getId(), artist2.getId()); + EXPECT_EQ(artists[1]->getId(), artist1.getId()); - const auto releases {trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults)}; - ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0]->getId(), release2.getId()); - EXPECT_EQ(releases[1]->getId(), release1.getId()); + const auto releases{ trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults) }; + ASSERT_EQ(releases.size(), 2); + EXPECT_EQ(releases[0]->getId(), release2.getId()); + EXPECT_EQ(releases[1]->getId(), release1.getId()); - const auto tracks {trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults)}; - ASSERT_EQ(tracks.size(), 2); - EXPECT_EQ(tracks[0]->getId(), track2.getId()); - EXPECT_EQ(tracks[1]->getId(),track1.getId()); - } + const auto tracks{ trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults) }; + ASSERT_EQ(tracks.size(), 2); + EXPECT_EQ(tracks[0]->getId(), track2.getId()); + EXPECT_EQ(tracks[1]->getId(), track1.getId()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool moreResults {}; - const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster3.getId()}, std::nullopt, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0]->getId(), artist2.getId()); - EXPECT_EQ(artists[1]->getId(), artist1.getId()); + bool moreResults{}; + const auto artists{ trackList->getArtistsOrderedByRecentFirst({cluster3.getId()}, std::nullopt, std::nullopt, moreResults) }; + ASSERT_EQ(artists.size(), 2); + EXPECT_EQ(artists[0]->getId(), artist2.getId()); + EXPECT_EQ(artists[1]->getId(), artist1.getId()); - const auto releases {trackList->getReleasesOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults)}; - ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0]->getId(), release2.getId()); - EXPECT_EQ(releases[1]->getId(), release1.getId()); + const auto releases{ trackList->getReleasesOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults) }; + ASSERT_EQ(releases.size(), 2); + EXPECT_EQ(releases[0]->getId(), release2.getId()); + EXPECT_EQ(releases[1]->getId(), release1.getId()); - const auto tracks {trackList->getTracksOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults)}; - ASSERT_EQ(tracks.size(), 2); - EXPECT_EQ(tracks[0]->getId(), track2.getId()); - EXPECT_EQ(tracks[1]->getId(), track1.getId()); - } + const auto tracks{ trackList->getTracksOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults) }; + ASSERT_EQ(tracks.size(), 2); + EXPECT_EQ(tracks[0]->getId(), track2.getId()); + EXPECT_EQ(tracks[1]->getId(), track1.getId()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool moreResults {}; - const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster1.getId()}, std::nullopt, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist1.getId()); + bool moreResults{}; + const auto artists{ trackList->getArtistsOrderedByRecentFirst({cluster1.getId()}, std::nullopt, std::nullopt, moreResults) }; + ASSERT_EQ(artists.size(), 1); + EXPECT_EQ(artists.front()->getId(), artist1.getId()); - const auto releases {trackList->getReleasesOrderedByRecentFirst({cluster1.getId()}, std::nullopt, moreResults)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release1.getId()); + const auto releases{ trackList->getReleasesOrderedByRecentFirst({cluster1.getId()}, std::nullopt, moreResults) }; + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases.front()->getId(), release1.getId()); - const auto tracks {trackList->getTracksOrderedByRecentFirst({cluster1.getId()}, std::nullopt, moreResults)}; - ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front()->getId(), track1.getId()); - } + const auto tracks{ trackList->getTracksOrderedByRecentFirst({cluster1.getId()}, std::nullopt, moreResults) }; + ASSERT_EQ(tracks.size(), 1); + EXPECT_EQ(tracks.front()->getId(), track1.getId()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool moreResults {}; - const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster2.getId()}, std::nullopt, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 1); - EXPECT_EQ(artists.front()->getId(), artist2.getId()); + bool moreResults{}; + const auto artists{ trackList->getArtistsOrderedByRecentFirst({cluster2.getId()}, std::nullopt, std::nullopt, moreResults) }; + ASSERT_EQ(artists.size(), 1); + EXPECT_EQ(artists.front()->getId(), artist2.getId()); - const auto releases {trackList->getReleasesOrderedByRecentFirst({cluster2.getId()}, std::nullopt, moreResults)}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release2.getId()); + const auto releases{ trackList->getReleasesOrderedByRecentFirst({cluster2.getId()}, std::nullopt, moreResults) }; + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases.front()->getId(), release2.getId()); - const auto tracks {trackList->getTracksOrderedByRecentFirst({cluster2.getId()}, std::nullopt, moreResults)}; - ASSERT_EQ(tracks.size(), 1); - EXPECT_EQ(tracks.front()->getId(), track2.getId()); - } + const auto tracks{ trackList->getTracksOrderedByRecentFirst({cluster2.getId()}, std::nullopt, moreResults) }; + ASSERT_EQ(tracks.size(), 1); + EXPECT_EQ(tracks.front()->getId(), track2.getId()); + } - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - session.create(track1.get(), trackList.get(), now.addSecs(2)); - } + session.create(track1.get(), trackList.get(), now.addSecs(2)); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool moreResults {}; - const auto artists {trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0]->getId(), artist1.getId()); - EXPECT_EQ(artists[1]->getId(), artist2.getId()); + bool moreResults{}; + const auto artists{ trackList->getArtistsOrderedByRecentFirst({}, std::nullopt, std::nullopt, moreResults) }; + ASSERT_EQ(artists.size(), 2); + EXPECT_EQ(artists[0]->getId(), artist1.getId()); + EXPECT_EQ(artists[1]->getId(), artist2.getId()); - const auto releases {trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults)}; - ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0]->getId(), release1.getId()); - EXPECT_EQ(releases[1]->getId(), release2.getId()); + const auto releases{ trackList->getReleasesOrderedByRecentFirst({}, std::nullopt, moreResults) }; + ASSERT_EQ(releases.size(), 2); + EXPECT_EQ(releases[0]->getId(), release1.getId()); + EXPECT_EQ(releases[1]->getId(), release2.getId()); - const auto tracks {trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults)}; - ASSERT_EQ(tracks.size(), 2); - EXPECT_EQ(tracks[0]->getId(), track1.getId()); - EXPECT_EQ(tracks[1]->getId(), track2.getId()); - } + const auto tracks{ trackList->getTracksOrderedByRecentFirst({}, std::nullopt, moreResults) }; + ASSERT_EQ(tracks.size(), 2); + EXPECT_EQ(tracks[0]->getId(), track1.getId()); + EXPECT_EQ(tracks[1]->getId(), track2.getId()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - bool moreResults {}; - const auto artists {trackList->getArtistsOrderedByRecentFirst({cluster3.getId()}, std::nullopt, std::nullopt, moreResults)}; - ASSERT_EQ(artists.size(), 2); - EXPECT_EQ(artists[0]->getId(), artist1.getId()); - EXPECT_EQ(artists[1]->getId(), artist2.getId()); + bool moreResults{}; + const auto artists{ trackList->getArtistsOrderedByRecentFirst({cluster3.getId()}, std::nullopt, std::nullopt, moreResults) }; + ASSERT_EQ(artists.size(), 2); + EXPECT_EQ(artists[0]->getId(), artist1.getId()); + EXPECT_EQ(artists[1]->getId(), artist2.getId()); - const auto releases {trackList->getReleasesOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults)}; - ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0]->getId(), release1.getId()); - EXPECT_EQ(releases[1]->getId(), release2.getId()); + const auto releases{ trackList->getReleasesOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults) }; + ASSERT_EQ(releases.size(), 2); + EXPECT_EQ(releases[0]->getId(), release1.getId()); + EXPECT_EQ(releases[1]->getId(), release2.getId()); - const auto tracks {trackList->getTracksOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults)}; - ASSERT_EQ(tracks.size(), 2); - EXPECT_EQ(tracks[0]->getId(), track1.getId()); - EXPECT_EQ(tracks[1]->getId(), track2.getId()); - } + const auto tracks{ trackList->getTracksOrderedByRecentFirst({cluster3.getId()}, std::nullopt, moreResults) }; + ASSERT_EQ(tracks.size(), 2); + EXPECT_EQ(tracks[0]->getId(), track1.getId()); + EXPECT_EQ(tracks[1]->getId(), track2.getId()); + } } TEST_F(DatabaseFixture, MultipleTracksMultipleArtistsMultiClusters) { - ScopedArtist artist1 {session, "MyArtist1"}; - ScopedArtist artist2 {session, "MyArtist2"}; - ScopedArtist artist3 {session, "MyArtist3"}; - ScopedClusterType clusterType {session, "MyClusterType"}; - ScopedCluster cluster1 {session, clusterType.lockAndGet(), "MyCluster1"}; - ScopedCluster cluster2 {session, clusterType.lockAndGet(), "MyCluster2"}; + ScopedArtist artist1{ session, "MyArtist1" }; + ScopedArtist artist2{ session, "MyArtist2" }; + ScopedArtist artist3{ session, "MyArtist3" }; + ScopedClusterType clusterType{ session, "MyClusterType" }; + ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; + ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; - { - auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(artist1->findSimilarArtists().results.empty()); - EXPECT_TRUE(artist2->findSimilarArtists().results.empty()); - EXPECT_TRUE(artist3->findSimilarArtists().results.empty()); - } + { + auto transaction{ session.createSharedTransaction() }; + EXPECT_TRUE(artist1->findSimilarArtists().results.empty()); + EXPECT_TRUE(artist2->findSimilarArtists().results.empty()); + EXPECT_TRUE(artist3->findSimilarArtists().results.empty()); + } - std::list tracks; - for (std::size_t i {}; i < 10; ++i) - { - tracks.emplace_back(session, "MyTrack" + std::to_string(i)); + std::list tracks; + for (std::size_t i{}; i < 10; ++i) + { + tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - auto transaction {session.createUniqueTransaction()}; + auto transaction{ session.createUniqueTransaction() }; - if (i < 5) - TrackArtistLink::create(session, tracks.back().get(), artist1.get(), TrackArtistLinkType::Artist); - else - { - TrackArtistLink::create(session, tracks.back().get(), artist2.get(), TrackArtistLinkType::Artist); - cluster2.get().modify()->addTrack(tracks.back().get()); - } + if (i < 5) + TrackArtistLink::create(session, tracks.back().get(), artist1.get(), TrackArtistLinkType::Artist); + else + { + TrackArtistLink::create(session, tracks.back().get(), artist2.get(), TrackArtistLinkType::Artist); + cluster2.get().modify()->addTrack(tracks.back().get()); + } - cluster1.get().modify()->addTrack(tracks.back().get()); - } + cluster1.get().modify()->addTrack(tracks.back().get()); + } - tracks.emplace_back(session, "MyTrack" + std::to_string(tracks.size())); - { - auto transaction {session.createUniqueTransaction()}; - TrackArtistLink::create(session, tracks.back().get(), artist3.get(), TrackArtistLinkType::Artist); - cluster2.get().modify()->addTrack(tracks.back().get()); - } + tracks.emplace_back(session, "MyTrack" + std::to_string(tracks.size())); + { + auto transaction{ session.createUniqueTransaction() }; + TrackArtistLink::create(session, tracks.back().get(), artist3.get(), TrackArtistLinkType::Artist); + cluster2.get().modify()->addTrack(tracks.back().get()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - { - auto artists {artist1->findSimilarArtists()}; - ASSERT_EQ(artists.results.size(), 1); - EXPECT_EQ(artists.results.front(), artist2.getId()); - } + { + auto artists{ artist1->findSimilarArtists() }; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist2.getId()); + } - { - auto artists {artist1->findSimilarArtists({TrackArtistLinkType::Artist})}; - ASSERT_EQ(artists.results.size(), 1); - EXPECT_EQ(artists.results.front(), artist2.getId()); - } + { + auto artists{ artist1->findSimilarArtists({TrackArtistLinkType::Artist}) }; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist2.getId()); + } - { - auto artists {artist1->findSimilarArtists({TrackArtistLinkType::ReleaseArtist})}; - EXPECT_EQ(artists.results.empty(), 1); - } + { + auto artists{ artist1->findSimilarArtists({TrackArtistLinkType::ReleaseArtist}) }; + EXPECT_EQ(artists.results.empty(), 1); + } - { - auto artists {artist1->findSimilarArtists({TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist})}; - ASSERT_EQ(artists.results.size(), 1); - EXPECT_EQ(artists.results.front(), artist2.getId()); - } + { + auto artists{ artist1->findSimilarArtists({TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}) }; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front(), artist2.getId()); + } - { - auto artists {artist1->findSimilarArtists({TrackArtistLinkType::Composer})}; - EXPECT_TRUE(artists.results.empty()); - } + { + auto artists{ artist1->findSimilarArtists({TrackArtistLinkType::Composer}) }; + EXPECT_TRUE(artists.results.empty()); + } - { - auto artists {artist2->findSimilarArtists()}; - ASSERT_EQ(artists.results.size(), 2); - EXPECT_EQ(artists.results[0], artist1.getId()); - EXPECT_EQ(artists.results[1], artist3.getId()); - } - } + { + auto artists{ artist2->findSimilarArtists() }; + ASSERT_EQ(artists.results.size(), 2); + EXPECT_EQ(artists.results[0], artist1.getId()); + EXPECT_EQ(artists.results[1], artist3.getId()); + } + } } TEST_F(DatabaseFixture, MultipleTracksMultipleReleasesMultiClusters) { - ScopedRelease release1 {session, "MyRelease1"}; - ScopedRelease release2 {session, "MyRelease2"}; - ScopedRelease release3 {session, "MyRelease3"}; - ScopedClusterType clusterType {session, "MyClusterType"}; - ScopedCluster cluster1 {session, clusterType.lockAndGet(), "MyCluster1"}; - ScopedCluster cluster2 {session, clusterType.lockAndGet(), "MyCluster2"}; + ScopedRelease release1{ session, "MyRelease1" }; + ScopedRelease release2{ session, "MyRelease2" }; + ScopedRelease release3{ session, "MyRelease3" }; + ScopedClusterType clusterType{ session, "MyClusterType" }; + ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; + ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; - { - auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(release1->getSimilarReleases().empty()); - EXPECT_TRUE(release2->getSimilarReleases().empty()); - EXPECT_TRUE(release3->getSimilarReleases().empty()); - } + { + auto transaction{ session.createSharedTransaction() }; + EXPECT_TRUE(release1->getSimilarReleases().empty()); + EXPECT_TRUE(release2->getSimilarReleases().empty()); + EXPECT_TRUE(release3->getSimilarReleases().empty()); + } - std::list tracks; - for (std::size_t i {}; i < 10; ++i) - { - tracks.emplace_back(session, "MyTrack" + std::to_string(i)); + std::list tracks; + for (std::size_t i{}; i < 10; ++i) + { + tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - auto transaction {session.createUniqueTransaction()}; + auto transaction{ session.createUniqueTransaction() }; - if (i < 5) - tracks.back().get().modify()->setRelease(release1.get()); - else - { - tracks.back().get().modify()->setRelease(release2.get()); - cluster2.get().modify()->addTrack(tracks.back().get()); - } + if (i < 5) + tracks.back().get().modify()->setRelease(release1.get()); + else + { + tracks.back().get().modify()->setRelease(release2.get()); + cluster2.get().modify()->addTrack(tracks.back().get()); + } - cluster1.get().modify()->addTrack(tracks.back().get()); - } + cluster1.get().modify()->addTrack(tracks.back().get()); + } - tracks.emplace_back(session, "MyTrack" + std::to_string(tracks.size())); - { - auto transaction {session.createUniqueTransaction()}; - tracks.back().get().modify()->setRelease(release3.get()); - cluster2.get().modify()->addTrack(tracks.back().get()); - } + tracks.emplace_back(session, "MyTrack" + std::to_string(tracks.size())); + { + auto transaction{ session.createUniqueTransaction() }; + tracks.back().get().modify()->setRelease(release3.get()); + cluster2.get().modify()->addTrack(tracks.back().get()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - { - auto releases {release1->getSimilarReleases()}; - ASSERT_EQ(releases.size(), 1); - EXPECT_EQ(releases.front()->getId(), release2.getId()); - } + { + auto releases{ release1->getSimilarReleases() }; + ASSERT_EQ(releases.size(), 1); + EXPECT_EQ(releases.front()->getId(), release2.getId()); + } - { - auto releases {release2->getSimilarReleases()}; - ASSERT_EQ(releases.size(), 2); - EXPECT_EQ(releases[0]->getId(), release1.getId()); - EXPECT_EQ(releases[1]->getId(), release3.getId()); - } - } + { + auto releases{ release2->getSimilarReleases() }; + ASSERT_EQ(releases.size(), 2); + EXPECT_EQ(releases[0]->getId(), release1.getId()); + EXPECT_EQ(releases[1]->getId(), release3.getId()); + } + } } diff --git a/src/libs/subsonic/impl/SubsonicResponse.cpp b/src/libs/subsonic/impl/SubsonicResponse.cpp index b59898a0..c5f2ddd5 100644 --- a/src/libs/subsonic/impl/SubsonicResponse.cpp +++ b/src/libs/subsonic/impl/SubsonicResponse.cpp @@ -90,7 +90,7 @@ namespace API::Subsonic _childrenArrays[std::string{ key }].emplace_back(std::move(node)); } - void Response::Node::createEmptyArrayValue(const std::string& key) + void Response::Node::createEmptyArrayValue(std::string_view key) { if (_value) throw LmsException{ "Node already has a value" }; @@ -98,12 +98,12 @@ namespace API::Subsonic _childrenValues.emplace(key, std::vector{}); } - void Response::Node::addArrayValue(const std::string& key, std::string_view value) + void Response::Node::addArrayValue(std::string_view key, std::string_view value) { if (_value) throw LmsException{ "Node already has a value" }; - _childrenValues[key].push_back(std::string{ value }); + _childrenValues[std::string{ key }].push_back(std::string{ value }); } Response::Node& Response::Node::createChild(const std::string& key) diff --git a/src/libs/subsonic/impl/SubsonicResponse.hpp b/src/libs/subsonic/impl/SubsonicResponse.hpp index b532688d..c5f13aae 100644 --- a/src/libs/subsonic/impl/SubsonicResponse.hpp +++ b/src/libs/subsonic/impl/SubsonicResponse.hpp @@ -211,8 +211,8 @@ namespace API::Subsonic void addChild(const std::string& key, Node node); void createEmptyArrayChild(std::string_view key); void addArrayChild(std::string_view key, Node node); - void createEmptyArrayValue(const std::string& key); - void addArrayValue(const std::string& key, std::string_view value); + void createEmptyArrayValue(std::string_view key); + void addArrayValue(std::string_view key, std::string_view value); private: void setVersionAttribute(ProtocolVersion version); diff --git a/src/libs/subsonic/impl/responses/Song.cpp b/src/libs/subsonic/impl/responses/Song.cpp index 434fab66..65bd8f2f 100644 --- a/src/libs/subsonic/impl/responses/Song.cpp +++ b/src/libs/subsonic/impl/responses/Song.cpp @@ -147,17 +147,17 @@ namespace API::Subsonic trackResponse.setAttribute("starred", StringUtils::toISO8601String(dateTime)); // Report the first GENRE for this track - ClusterType::pointer clusterType{ ClusterType::find(dbSession, "GENRE") }; - if (clusterType) + ClusterType::pointer genreClusterType{ ClusterType::find(dbSession, "GENRE") }; + if (genreClusterType) { - auto clusters{ track->getClusterGroups({clusterType}, 1) }; + auto clusters{ track->getClusterGroups({genreClusterType}, 1) }; if (!clusters.empty() && !clusters.front().empty()) trackResponse.setAttribute("genre", clusters.front().front()->getName()); } // OpenSubsonic specific fields (must always be set) { - std::optional mbid {track->getRecordingMBID()}; + std::optional mbid{ track->getRecordingMBID() }; trackResponse.setAttribute("musicBrainzId", mbid ? mbid->getAsString() : ""); } @@ -172,7 +172,7 @@ namespace API::Subsonic // Don't report artists nor release artists as they are set in dedicated fields if (link && link->getType() != TrackArtistLinkType::Artist && link->getType() != TrackArtistLinkType::ReleaseArtist) trackResponse.addArrayChild("contributors", createContributorNode(link)); - } + } } auto addArtistLinks{ [&](std::string_view nodeName, TrackArtistLinkType type) @@ -194,6 +194,29 @@ namespace API::Subsonic addArtistLinks("artists", TrackArtistLinkType::Artist); addArtistLinks("albumartists", TrackArtistLinkType::ReleaseArtist); + auto addClusters{ [&](std::string_view field, std::string_view clusterTypeName) + { + trackResponse.createEmptyArrayValue(field); + + ClusterType::pointer clusterType{ ClusterType::find(dbSession, clusterTypeName) }; + if (clusterType) + { + Cluster::FindParameters params; + params.setTrack(track->getId()); + params.setClusterType(clusterType->getId()); + + for (const ClusterId clusterId : Cluster::find(dbSession, params).results) + { + Cluster::pointer cluster {Cluster::find(dbSession, clusterId)}; + if (cluster) + trackResponse.addArrayValue(field, cluster->getName()); + } + } + } }; + + addClusters("genres", "GENRE"); + addClusters("moods", "MOOD"); + return trackResponse; } } \ No newline at end of file