From 48ee8e35568c7a8f53022f1b236837e69c49ee61 Mon Sep 17 00:00:00 2001 From: emeric Date: Sat, 4 Nov 2023 19:07:21 +0100 Subject: [PATCH] SubsonicAPI: optimized search for artists --- src/libs/services/database/impl/Artist.cpp | 472 ++++++++------- .../include/services/database/Artist.hpp | 153 ++--- src/libs/services/database/test/Artist.cpp | 83 +-- src/libs/services/database/test/Cluster.cpp | 46 +- .../services/database/test/DatabaseTest.cpp | 110 ++-- .../services/database/test/StarredArtist.cpp | 16 +- .../feedback/impl/FeedbackService.cpp | 2 +- .../impl/clusters/ClustersEngine.cpp | 2 +- .../impl/ScanStepRemoveOrphanDbFiles.cpp | 2 +- .../subsonic/impl/entrypoints/Browsing.cpp | 14 +- .../subsonic/impl/entrypoints/Searching.cpp | 6 +- src/lms/ui/Utils.cpp | 4 +- src/lms/ui/explore/ArtistCollector.cpp | 8 +- src/lms/ui/explore/ArtistView.cpp | 558 +++++++++--------- src/lms/ui/explore/ReleaseView.cpp | 2 +- src/lms/ui/explore/TrackListHelpers.cpp | 2 +- .../recommendation/LmsRecommendation.cpp | 2 +- 17 files changed, 745 insertions(+), 737 deletions(-) diff --git a/src/libs/services/database/impl/Artist.cpp b/src/libs/services/database/impl/Artist.cpp index 8d0849ef..c9ca60a4 100644 --- a/src/libs/services/database/impl/Artist.cpp +++ b/src/libs/services/database/impl/Artist.cpp @@ -33,284 +33,298 @@ namespace Database { + namespace + { + template + Wt::Dbo::Query createQuery(Session& session, std::string_view itemToSelect, const Artist::FindParameters& params) + { + session.checkSharedLocked(); -Artist::Artist(const std::string& name, const std::optional& MBID) -: _name {std::string(name, 0 , _maxNameLength)}, -_sortName {_name}, -_MBID {MBID ? MBID->getAsString() : ""} -{ -} + auto query{ session.getDboSession().query("SELECT DISTINCT " + std::string{ itemToSelect } + " FROM artist a") }; + if (params.sortMethod == ArtistSortMethod::LastWritten + || params.writtenAfter.isValid() + || params.linkType + || params.track.isValid() + || params.release.isValid() + || params.clusters.size() == 1) + { + query.join("track t ON t.id = t_a_l.track_id"); + query.join("track_artist_link t_a_l ON t_a_l.artist_id = a.id"); + } -Artist::pointer -Artist::create(Session& session, const std::string& name, const std::optional& MBID) -{ - return session.getDboSession().add(std::unique_ptr {new Artist {name, MBID}}); -} + if (params.linkType) + query.where("t_a_l.type = ?").bind(*params.linkType); -std::size_t -Artist::getCount(Session& session) -{ - session.checkSharedLocked(); + if (params.writtenAfter.isValid()) + query.where("t.file_last_write > ?").bind(params.writtenAfter); - return session.getDboSession().query("SELECT COUNT(*) FROM artist"); -} + if (!params.keywords.empty()) + { + std::vector clauses; + std::vector sortClauses; -std::vector -Artist::find(Session& session, const std::string& name) -{ - session.checkSharedLocked(); + for (std::string_view keyword : params.keywords) + { + clauses.push_back("a.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'"); + query.bind("%" + Utils::escapeLikeKeyword(keyword) + "%"); + } - Wt::Dbo::collection> res = session.getDboSession().find() - .where("name = ?").bind(std::string {name, 0, _maxNameLength}) - .orderBy("LENGTH(mbid) DESC"); // put mbid entries first + for (std::string_view keyword : params.keywords) + { + sortClauses.push_back("a.sort_name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'"); + query.bind("%" + Utils::escapeLikeKeyword(keyword) + "%"); + } - return std::vector(res.begin(), res.end()); -} + query.where("(" + StringUtils::joinStrings(clauses, " AND ") + ") OR (" + StringUtils::joinStrings(sortClauses, " AND ") + ")"); + } -Artist::pointer -Artist::find(Session& session, const UUID& mbid) -{ - session.checkSharedLocked(); - return session.getDboSession().find().where("mbid = ?").bind(std::string {mbid.getAsString()}).resultValue(); -} + if (params.starringUser.isValid()) + { + assert(params.feedbackBackend); + query.join("starred_artist s_a ON s_a.artist_id = a.id") + .where("s_a.user_id = ?").bind(params.starringUser) + .where("s_a.backend = ?").bind(*params.feedbackBackend) + .where("s_a.sync_state <> ?").bind(SyncState::PendingRemove); + } -Artist::pointer -Artist::find(Session& session, ArtistId id) -{ - session.checkSharedLocked(); - return session.getDboSession().find().where("id = ?").bind(id).resultValue(); -} + if (params.clusters.size() == 1) + { + query.join("track_cluster t_c ON t_c.track_id = t.id") + .where("t_c.cluster_id = ?").bind(params.clusters.front()); + } + else if (params.clusters.size() > 1) + { + std::ostringstream oss; + oss << "a.id IN (SELECT DISTINCT a.id FROM artist a" + " INNER JOIN track t ON t.id = t_a_l.track_id" + " INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.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"; -bool -Artist::exists(Session& session, ArtistId id) -{ - session.checkSharedLocked(); - return session.getDboSession().query("SELECT 1 FROM artist").where("id = ?").bind(id).resultValue() == 1; -} + WhereClause clusterClause; + for (const ClusterId clusterId : params.clusters) + { + clusterClause.Or(WhereClause("c.id = ?")); + query.bind(clusterId); + } -static -Wt::Dbo::Query -createQuery(Session& session, const Artist::FindParameters& params) -{ - session.checkSharedLocked(); + oss << " " << clusterClause.get(); + oss << " GROUP BY t.id,a.id HAVING COUNT(DISTINCT c.id) = " << params.clusters.size() << ")"; - auto query {session.getDboSession().query("SELECT DISTINCT a.id FROM artist a")}; - if (params.sortMethod == ArtistSortMethod::LastWritten - || params.writtenAfter.isValid() - || params.linkType - || params.track.isValid() - || params.release.isValid() - || params.clusters.size() == 1) - { - query.join("track t ON t.id = t_a_l.track_id"); - query.join("track_artist_link t_a_l ON t_a_l.artist_id = a.id"); - } + query.where(oss.str()); + } - if (params.linkType) - query.where("t_a_l.type = ?").bind(*params.linkType); + if (params.track.isValid()) + query.where("t.id = ?").bind(params.track); - if (params.writtenAfter.isValid()) - query.where("t.file_last_write > ?").bind(params.writtenAfter); + if (params.release.isValid()) + query.where("t.release_id = ?").bind(params.release); - if (!params.keywords.empty()) - { - std::vector clauses; - std::vector sortClauses; + switch (params.sortMethod) + { + case ArtistSortMethod::None: + break; + case ArtistSortMethod::ByName: + query.orderBy("a.name COLLATE NOCASE"); + break; + case ArtistSortMethod::BySortName: + query.orderBy("a.sort_name COLLATE NOCASE"); + break; + case ArtistSortMethod::Random: + query.orderBy("RANDOM()"); + break; + case ArtistSortMethod::LastWritten: + query.orderBy("t.file_last_write DESC"); + break; + case ArtistSortMethod::StarredDateDesc: + assert(params.starringUser.isValid()); + query.orderBy("s_a.date_time DESC"); + break; + } - for (std::string_view keyword : params.keywords) - { - clauses.push_back("a.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'"); - query.bind("%" + Utils::escapeLikeKeyword(keyword) + "%"); - } + return query; + } - for (std::string_view keyword : params.keywords) - { - sortClauses.push_back("a.sort_name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'"); - query.bind("%" + Utils::escapeLikeKeyword(keyword) + "%"); - } + template + Wt::Dbo::Query createQuery(Session& session, const Artist::FindParameters& params) + { + std::string_view itemToSelect; + + if constexpr (std::is_same_v) + itemToSelect = "a.id"; + else if constexpr (std::is_same_v>) + itemToSelect = "a"; + else + static_assert("Unhandled type"); - query.where("(" + StringUtils::joinStrings(clauses, " AND ") + ") OR (" + StringUtils::joinStrings(sortClauses, " AND ") + ")"); - } + return createQuery(session, itemToSelect, params); + } + } - if (params.starringUser.isValid()) - { - assert(params.feedbackBackend); - query.join("starred_artist s_a ON s_a.artist_id = a.id") - .where("s_a.user_id = ?").bind(params.starringUser) - .where("s_a.backend = ?").bind(*params.feedbackBackend) - .where("s_a.sync_state <> ?").bind(SyncState::PendingRemove); - } + Artist::Artist(const std::string& name, const std::optional& MBID) + : _name{ std::string(name, 0 , _maxNameLength) }, + _sortName{ _name }, + _MBID{ MBID ? MBID->getAsString() : "" } + { + } - if(params.clusters.size() == 1) - { - query.join("track_cluster t_c ON t_c.track_id = t.id") - .where("t_c.cluster_id = ?").bind(params.clusters.front()); - } - else if (params.clusters.size() > 1) - { - std::ostringstream oss; - oss << "a.id IN (SELECT DISTINCT a.id FROM artist a" - " INNER JOIN track t ON t.id = t_a_l.track_id" - " INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.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"; + Artist::pointer Artist::create(Session& session, const std::string& name, const std::optional& MBID) + { + return session.getDboSession().add(std::unique_ptr {new Artist{ name, MBID }}); + } - WhereClause clusterClause; - for (const ClusterId clusterId : params.clusters) - { - clusterClause.Or(WhereClause("c.id = ?")); - query.bind(clusterId); - } + std::size_t Artist::getCount(Session& session) + { + session.checkSharedLocked(); - oss << " " << clusterClause.get(); - oss << " GROUP BY t.id,a.id HAVING COUNT(DISTINCT c.id) = " << params.clusters.size() << ")"; + return session.getDboSession().query("SELECT COUNT(*) FROM artist"); + } - query.where(oss.str()); - } + std::vector Artist::find(Session& session, const std::string& name) + { + session.checkSharedLocked(); - if (params.track.isValid()) - query.where("t.id = ?").bind(params.track); + Wt::Dbo::collection> res = session.getDboSession().find() + .where("name = ?").bind(std::string{ name, 0, _maxNameLength }) + .orderBy("LENGTH(mbid) DESC"); // put mbid entries first - if (params.release.isValid()) - query.where("t.release_id = ?").bind(params.release); + return std::vector(res.begin(), res.end()); + } - switch (params.sortMethod) - { - case ArtistSortMethod::None: - break; - case ArtistSortMethod::ByName: - query.orderBy("a.name COLLATE NOCASE"); - break; - case ArtistSortMethod::BySortName: - query.orderBy("a.sort_name COLLATE NOCASE"); - break; - case ArtistSortMethod::Random: - query.orderBy("RANDOM()"); - break; - case ArtistSortMethod::LastWritten: - query.orderBy("t.file_last_write DESC"); - break; - case ArtistSortMethod::StarredDateDesc: - assert(params.starringUser.isValid()); - query.orderBy("s_a.date_time DESC"); - break; - } + Artist::pointer Artist::find(Session& session, const UUID& mbid) + { + session.checkSharedLocked(); + return session.getDboSession().find().where("mbid = ?").bind(std::string{ mbid.getAsString() }).resultValue(); + } - return query; -} + Artist::pointer Artist::find(Session& session, ArtistId id) + { + session.checkSharedLocked(); + return session.getDboSession().find().where("id = ?").bind(id).resultValue(); + } -RangeResults -Artist::findAllOrphans(Session& session, Range range) -{ - session.checkSharedLocked(); - auto query {session.getDboSession().query("SELECT DISTINCT a.id FROM artist a WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id WHERE t.id = t_a_l.track_id)")}; + bool Artist::exists(Session& session, ArtistId id) + { + session.checkSharedLocked(); + return session.getDboSession().query("SELECT 1 FROM artist").where("id = ?").bind(id).resultValue() == 1; + } - return Utils::execQuery(query, range); -} -RangeResults -Artist::find(Session& session, const FindParameters& params) -{ - session.checkSharedLocked(); + RangeResults Artist::findOrphanIds(Session& session, Range range) + { + session.checkSharedLocked(); + auto query{ session.getDboSession().query("SELECT DISTINCT a.id FROM artist a WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id WHERE t.id = t_a_l.track_id)") }; - auto query {createQuery(session, params)}; - return Utils::execQuery(query, params.range); -} + return Utils::execQuery(query, range); + } -RangeResults -Artist::findSimilarArtists(EnumSet artistLinkTypes, Range range) const -{ - assert(session()); + RangeResults Artist::findIds(Session& session, const FindParameters& params) + { + session.checkSharedLocked(); - std::ostringstream oss; - oss << - "SELECT a.id FROM artist a" - " INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id" - " INNER JOIN track t ON t.id = t_a_l.track_id" - " INNER JOIN track_cluster t_c ON t_c.track_id = t.id" - " WHERE " - " t_c.cluster_id IN (SELECT DISTINCT c.id from cluster c" - " INNER JOIN track t ON c.id = t_c.cluster_id" - " INNER JOIN track_cluster t_c ON t_c.track_id = t.id" - " INNER JOIN artist a ON a.id = t_a_l.artist_id" - " INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id" - " WHERE a.id = ?)" - " AND a.id <> ?"; + auto query{ createQuery(session, params) }; + return Utils::execQuery(query, params.range); + } - if (!artistLinkTypes.empty()) - { - oss << " AND t_a_l.type IN ("; + RangeResults Artist::find(Session& session, const FindParameters& params) + { + session.checkSharedLocked(); - bool first {true}; - for (TrackArtistLinkType type : artistLinkTypes) - { - (void) type; - if (!first) - oss << ", "; - oss << "?"; - first = false; - } - oss << ")"; - } + auto query{ createQuery>(session, params) }; + return Utils::execQuery(query, params.range); + } - auto query {session()->query(oss.str()) - .bind(getId()) - .bind(getId()) - .groupBy("a.id") - .orderBy("COUNT(*) DESC, RANDOM()")}; + RangeResults Artist::findSimilarArtistIds(EnumSet artistLinkTypes, Range range) const + { + assert(session()); - for (TrackArtistLinkType type : artistLinkTypes) - query.bind(type); + std::ostringstream oss; + oss << + "SELECT a.id FROM artist a" + " INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id" + " INNER JOIN track t ON t.id = t_a_l.track_id" + " INNER JOIN track_cluster t_c ON t_c.track_id = t.id" + " WHERE " + " t_c.cluster_id IN (SELECT DISTINCT c.id from cluster c" + " INNER JOIN track t ON c.id = t_c.cluster_id" + " INNER JOIN track_cluster t_c ON t_c.track_id = t.id" + " INNER JOIN artist a ON a.id = t_a_l.artist_id" + " INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id" + " WHERE a.id = ?)" + " AND a.id <> ?"; - return Utils::execQuery(query, range); -} + if (!artistLinkTypes.empty()) + { + oss << " AND t_a_l.type IN ("; -std::vector> -Artist::getClusterGroups(std::vector clusterTypes, std::size_t size) const -{ - assert(session()); + bool first{ true }; + for (TrackArtistLinkType type : artistLinkTypes) + { + (void)type; + if (!first) + oss << ", "; + oss << "?"; + first = false; + } + oss << ")"; + } - WhereClause where; + auto query{ session()->query(oss.str()) + .bind(getId()) + .bind(getId()) + .groupBy("a.id") + .orderBy("COUNT(*) DESC, RANDOM()") }; - std::ostringstream oss; - oss << "SELECT c FROM cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN cluster_type c_type ON c.cluster_type_id = c_type.id INNER JOIN artist a ON t_a_l.artist_id = a.id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id"; + for (TrackArtistLinkType type : artistLinkTypes) + query.bind(type); - where.And(WhereClause("a.id = ?")).bind(getId().toString()); - { - WhereClause clusterClause; - for (auto clusterType : clusterTypes) - clusterClause.Or(WhereClause("c_type.id = ?")).bind(clusterType->getId().toString()); + return Utils::execQuery(query, range); + } - where.And(clusterClause); - } - oss << " " << where.get(); - oss << "GROUP BY c.id ORDER BY COUNT(DISTINCT c.id) DESC"; + std::vector> Artist::getClusterGroups(std::vector clusterTypes, std::size_t size) const + { + assert(session()); - Wt::Dbo::Query> query = session()->query>( oss.str() ); + WhereClause where; - for (const std::string& bindArg : where.getBindArgs()) - query.bind(bindArg); + std::ostringstream oss; + oss << "SELECT c FROM cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN cluster_type c_type ON c.cluster_type_id = c_type.id INNER JOIN artist a ON t_a_l.artist_id = a.id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id"; - Wt::Dbo::collection> queryRes = query; + where.And(WhereClause("a.id = ?")).bind(getId().toString()); + { + WhereClause clusterClause; + for (auto clusterType : clusterTypes) + clusterClause.Or(WhereClause("c_type.id = ?")).bind(clusterType->getId().toString()); - std::map> clustersByType; - for (Cluster::pointer cluster : queryRes) - { - if (clustersByType[cluster->getType()->getId()].size() < size) - clustersByType[cluster->getType()->getId()].push_back(cluster); - } + where.And(clusterClause); + } + oss << " " << where.get(); + oss << "GROUP BY c.id ORDER BY COUNT(DISTINCT c.id) DESC"; - std::vector> res; - for (const auto& [clusterTypeId, clusters] : clustersByType) - res.push_back(clusters); + Wt::Dbo::Query> query = session()->query>(oss.str()); - return res; -} + for (const std::string& bindArg : where.getBindArgs()) + query.bind(bindArg); -void -Artist::setSortName(const std::string& sortName) -{ - _sortName = std::string(sortName, 0 , _maxNameLength); -} + Wt::Dbo::collection> queryRes = query; + + std::map> clustersByType; + for (Cluster::pointer cluster : queryRes) + { + if (clustersByType[cluster->getType()->getId()].size() < size) + clustersByType[cluster->getType()->getId()].push_back(cluster); + } + + std::vector> res; + for (const auto& [clusterTypeId, clusters] : clustersByType) + res.push_back(clusters); + + return res; + } + + void Artist::setSortName(const std::string& sortName) + { + _sortName = std::string(sortName, 0, _maxNameLength); + } } // namespace Database diff --git a/src/libs/services/database/include/services/database/Artist.hpp b/src/libs/services/database/include/services/database/Artist.hpp index 40331f9d..7fcaadac 100644 --- a/src/libs/services/database/include/services/database/Artist.hpp +++ b/src/libs/services/database/include/services/database/Artist.hpp @@ -40,96 +40,97 @@ namespace Database { -class Cluster; -class ClusterType; -class Release; -class Session; -class StarredArtist; -class Track; -class TrackArtistLink; -class User; + class Cluster; + class ClusterType; + class Release; + class Session; + class StarredArtist; + class Track; + class TrackArtistLink; + class User; -class Artist final : public Object -{ - public: - struct FindParameters - { - std::vector clusters; // if non empty, at least one artist that belongs to these clusters - std::vector keywords; // if non empty, name must match all of these keywords (on either name field OR sort name field) - std::optional linkType; // if set, only artists that have produced at least one track with this link type - ArtistSortMethod sortMethod {ArtistSortMethod::None}; - Range range; - Wt::WDateTime writtenAfter; - UserId starringUser; // only artists starred by this user - std::optional feedbackBackend; // and for this feedback backend - TrackId track; // artists involved in this track - ReleaseId release; // artists involved in this release + class Artist final : public Object + { + public: + struct FindParameters + { + std::vector clusters; // if non empty, at least one artist that belongs to these clusters + std::vector keywords; // if non empty, name must match all of these keywords (on either name field OR sort name field) + std::optional linkType; // if set, only artists that have produced at least one track with this link type + ArtistSortMethod sortMethod{ ArtistSortMethod::None }; + Range range; + Wt::WDateTime writtenAfter; + UserId starringUser; // only artists starred by this user + std::optional feedbackBackend; // and for this feedback backend + TrackId track; // artists involved in this track + ReleaseId release; // artists involved in this release - FindParameters& setClusters(const std::vector& _clusters) { clusters = _clusters; return *this; } - FindParameters& setKeywords(const std::vector& _keywords) { keywords = _keywords; return *this; } - FindParameters& setLinkType(std::optional _linkType) { linkType = _linkType; return *this; } - FindParameters& setSortMethod(ArtistSortMethod _sortMethod) {sortMethod = _sortMethod; return *this; } - FindParameters& setRange(Range _range) {range = _range; return *this; } - FindParameters& setWrittenAfter(const Wt::WDateTime& _after) { writtenAfter = _after; return *this; } - FindParameters& setStarringUser(UserId _user, FeedbackBackend _feedbackBackend) { starringUser = _user; feedbackBackend = _feedbackBackend; return *this; } - FindParameters& setTrack(TrackId _track) { track = _track; return *this; } - FindParameters& setRelease(ReleaseId _release) { release = _release; return *this; } - }; + FindParameters& setClusters(const std::vector& _clusters) { clusters = _clusters; return *this; } + FindParameters& setKeywords(const std::vector& _keywords) { keywords = _keywords; return *this; } + FindParameters& setLinkType(std::optional _linkType) { linkType = _linkType; return *this; } + FindParameters& setSortMethod(ArtistSortMethod _sortMethod) { sortMethod = _sortMethod; return *this; } + FindParameters& setRange(Range _range) { range = _range; return *this; } + FindParameters& setWrittenAfter(const Wt::WDateTime& _after) { writtenAfter = _after; return *this; } + FindParameters& setStarringUser(UserId _user, FeedbackBackend _feedbackBackend) { starringUser = _user; feedbackBackend = _feedbackBackend; return *this; } + FindParameters& setTrack(TrackId _track) { track = _track; return *this; } + FindParameters& setRelease(ReleaseId _release) { release = _release; return *this; } + }; - Artist() = default; + Artist() = default; - // Accessors - static std::size_t getCount(Session& session); - static pointer find(Session& session, const UUID& MBID); - static pointer find(Session& session, ArtistId id); - static std::vector find(Session& session, const std::string& name); // exact match on name field - static RangeResults find(Session& session, const FindParameters& parameters); - static RangeResults findAllOrphans(Session& session, Range range); // No track related - static bool exists(Session& session, ArtistId id); + // Accessors + static std::size_t getCount(Session& session); + static pointer find(Session& session, const UUID& MBID); + static pointer find(Session& session, ArtistId id); + static std::vector find(Session& session, const std::string& name); // exact match on name field + static RangeResults find(Session& session, const FindParameters& parameters); + static RangeResults findIds(Session& session, const FindParameters& parameters); + static RangeResults findOrphanIds(Session& session, Range range); // No track related + static bool exists(Session& session, ArtistId id); - // Accessors - const std::string& getName() const { return _name; } - const std::string& getSortName() const { return _sortName; } - std::optional getMBID() const { return UUID::fromString(_MBID); } + // Accessors + const std::string& getName() const { return _name; } + const std::string& getSortName() const { return _sortName; } + std::optional getMBID() const { return UUID::fromString(_MBID); } - // No artistLinkTypes means get them all - RangeResults findSimilarArtists(EnumSet artistLinkTypes = {}, Range range = {}) const; + // No artistLinkTypes means get them all + RangeResults findSimilarArtistIds(EnumSet artistLinkTypes = {}, Range range = {}) const; - // Get the cluster of the tracks made by this artist - // Each clusters are grouped by cluster type, sorted by the number of occurence - // size is the max number of cluster per cluster type - std::vector>> getClusterGroups(std::vector> clusterTypes, std::size_t size) const; + // Get the cluster of the tracks made by this artist + // Each clusters are grouped by cluster type, sorted by the number of occurence + // size is the max number of cluster per cluster type + std::vector>> getClusterGroups(std::vector> clusterTypes, std::size_t size) const; - void setName(std::string_view name) { _name = name; } - void setMBID(const std::optional& mbid) { _MBID = mbid ? mbid->getAsString() : ""; } - void setSortName(const std::string& sortName); + void setName(std::string_view name) { _name = name; } + void setMBID(const std::optional& mbid) { _MBID = mbid ? mbid->getAsString() : ""; } + void setSortName(const std::string& sortName); - template - void persist(Action& a) - { - Wt::Dbo::field(a, _name, "name"); - Wt::Dbo::field(a, _sortName, "sort_name"); - Wt::Dbo::field(a, _MBID, "mbid"); + template + void persist(Action& a) + { + Wt::Dbo::field(a, _name, "name"); + Wt::Dbo::field(a, _sortName, "sort_name"); + Wt::Dbo::field(a, _MBID, "mbid"); - Wt::Dbo::hasMany(a, _trackArtistLinks, Wt::Dbo::ManyToOne, "artist"); - Wt::Dbo::hasMany(a, _starredArtists, Wt::Dbo::ManyToMany, "user_starred_artists", "", Wt::Dbo::OnDeleteCascade); - } + Wt::Dbo::hasMany(a, _trackArtistLinks, Wt::Dbo::ManyToOne, "artist"); + Wt::Dbo::hasMany(a, _starredArtists, Wt::Dbo::ManyToMany, "user_starred_artists", "", Wt::Dbo::OnDeleteCascade); + } - private: - static constexpr std::size_t _maxNameLength {128}; + private: + static constexpr std::size_t _maxNameLength{ 128 }; - friend class Session; - // Create - Artist(const std::string& name, const std::optional& MBID = {}); - static pointer create(Session& session, const std::string& name, const std::optional& UUID = {}); + friend class Session; + // Create + Artist(const std::string& name, const std::optional& MBID = {}); + static pointer create(Session& session, const std::string& name, const std::optional& UUID = {}); - std::string _name; - std::string _sortName; - std::string _MBID; // Musicbrainz Identifier + std::string _name; + std::string _sortName; + std::string _MBID; // Musicbrainz Identifier - Wt::Dbo::collection> _trackArtistLinks; // Tracks involving this artist - Wt::Dbo::collection> _starredArtists; // starred entries for this artist -}; + Wt::Dbo::collection> _trackArtistLinks; // Tracks involving this artist + Wt::Dbo::collection> _starredArtists; // starred entries for this artist + }; } // namespace Database diff --git a/src/libs/services/database/test/Artist.cpp b/src/libs/services/database/test/Artist.cpp index 607235b3..6e9020fd 100644 --- a/src/libs/services/database/test/Artist.cpp +++ b/src/libs/services/database/test/Artist.cpp @@ -47,14 +47,23 @@ TEST_F(DatabaseFixture, Artist) { auto transaction{ session.createSharedTransaction() }; - auto artists{ Artist::find(session, Artist::FindParameters {}) }; + auto artists{ Artist::findIds(session, Artist::FindParameters {}) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); - artists = Artist::findAllOrphans(session, Range{}); + artists = Artist::findOrphanIds(session, Range{}); ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); } + + + { + auto transaction{ session.createSharedTransaction() }; + + auto artists{ Artist::find(session, Artist::FindParameters {}) }; + ASSERT_EQ(artists.results.size(), 1); + EXPECT_EQ(artists.results.front()->getId(), artist.getId()); + } } TEST_F(DatabaseFixture, Artist_singleTrack) @@ -71,7 +80,7 @@ TEST_F(DatabaseFixture, Artist_singleTrack) { auto transaction{ session.createSharedTransaction() }; - EXPECT_TRUE(Artist::findAllOrphans(session, Range{}).results.empty()); + EXPECT_TRUE(Artist::findOrphanIds(session, Range{}).results.empty()); } { @@ -121,7 +130,7 @@ TEST_F(DatabaseFixture, Artist_singleTrack) } { auto transaction{ session.createSharedTransaction() }; - auto artists{ Artist::find(session, Artist::FindParameters{}.setTrack(track->getId())) }; + auto artists{ Artist::findIds(session, Artist::FindParameters{}.setTrack(track->getId())) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); } @@ -141,16 +150,16 @@ TEST_F(DatabaseFixture, Artist_singleTracktMultiRoles) { auto transaction{ session.createSharedTransaction() }; - EXPECT_TRUE(Artist::findAllOrphans(session, Range{}).results.empty()); + EXPECT_TRUE(Artist::findOrphanIds(session, Range{}).results.empty()); } { auto transaction{ session.createSharedTransaction() }; - EXPECT_EQ(Artist::find(session, Artist::FindParameters{}).results.size(), 1); - EXPECT_EQ(Artist::find(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::Artist)).results.size(), 1); - EXPECT_EQ(Artist::find(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::ReleaseArtist)).results.size(), 1); - EXPECT_EQ(Artist::find(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::Writer)).results.size(), 1); - EXPECT_EQ(Artist::find(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::Composer)).results.size(), 0); + EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}).results.size(), 1); + EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::Artist)).results.size(), 1); + EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::ReleaseArtist)).results.size(), 1); + EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::Writer)).results.size(), 1); + EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::Composer)).results.size(), 0); } { @@ -206,7 +215,7 @@ TEST_F(DatabaseFixture, Artist_singleTrackMultiArtists) { auto transaction{ session.createSharedTransaction() }; - EXPECT_TRUE(Artist::findAllOrphans(session, Range{}).results.empty()); + EXPECT_TRUE(Artist::findOrphanIds(session, Range{}).results.empty()); } { @@ -220,8 +229,8 @@ TEST_F(DatabaseFixture, Artist_singleTrackMultiArtists) EXPECT_EQ(track->getArtists({}).size(), 2); EXPECT_EQ(track->getArtists({ TrackArtistLinkType::Artist }).size(), 2); EXPECT_TRUE(track->getArtists({ TrackArtistLinkType::ReleaseArtist }).empty()); - EXPECT_EQ(Artist::find(session, Artist::FindParameters{}).results.size(), 2); - EXPECT_EQ(Artist::find(session, Artist::FindParameters{}.setSortMethod(ArtistSortMethod::Random)).results.size(), 2); + EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}).results.size(), 2); + EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setSortMethod(ArtistSortMethod::Random)).results.size(), 2); } { @@ -263,13 +272,13 @@ TEST_F(DatabaseFixture, Artist_findByName) { auto transaction{ session.createSharedTransaction() }; - EXPECT_TRUE(Artist::find(session, Artist::FindParameters{}.setKeywords({ "N" })).results.empty()); + EXPECT_TRUE(Artist::findIds(session, Artist::FindParameters{}.setKeywords({ "N" })).results.empty()); - const auto artistsByAAA{ Artist::find(session, Artist::FindParameters {}.setKeywords({"A"})) }; + const auto artistsByAAA{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"A"})) }; ASSERT_EQ(artistsByAAA.results.size(), 1); EXPECT_EQ(artistsByAAA.results.front(), artist.getId()); - const auto artistsByZZZ{ Artist::Artist::find(session, Artist::FindParameters {}.setKeywords({"Z"})) }; + const auto artistsByZZZ{ Artist::Artist::findIds(session, Artist::FindParameters {}.setKeywords({"Z"})) }; ASSERT_EQ(artistsByZZZ.results.size(), 1); EXPECT_EQ(artistsByZZZ.results.front(), artist.getId()); @@ -280,59 +289,59 @@ TEST_F(DatabaseFixture, Artist_findByName) TEST_F(DatabaseFixture, Artist_findByNameEscaped) { - ScopedArtist artist1{ session, "MyArtist%" }; - ScopedArtist artist2{ session, "%MyArtist" }; - ScopedArtist artist3{ session, "%_MyArtist" }; + ScopedArtist artist1{ session, R"(MyArtist%)" }; + ScopedArtist artist2{ session, R"(%MyArtist)" }; + ScopedArtist artist3{ session, R"(%_MyArtist)" }; - ScopedArtist artist4{ session, "MyArtist%foo" }; - ScopedArtist artist5{ session, "foo%MyArtist" }; - ScopedArtist artist6{ session, "%AMyArtist" }; + ScopedArtist artist4{ session, R"(MyArtist%foo)" }; + ScopedArtist artist5{ session, R"(foo%MyArtist)" }; + ScopedArtist artist6{ session, R"(%AMyArtist)" }; { auto transaction{ session.createSharedTransaction() }; { - const auto artists{ Artist::find(session, "MyArtist%") }; + const auto artists{ Artist::find(session, R"(MyArtist%)") }; ASSERT_TRUE(artists.size() == 1); EXPECT_EQ(artists.front()->getId(), artist1.getId()); - EXPECT_TRUE(Artist::find(session, "MyArtistFoo").empty()); + EXPECT_TRUE(Artist::find(session, R"(MyArtistFoo)").empty()); } { - const auto artists{ Artist::find(session, "%MyArtist") }; + const auto artists{ Artist::find(session, R"(%MyArtist)") }; ASSERT_TRUE(artists.size() == 1); EXPECT_EQ(artists.front()->getId(), artist2.getId()); - EXPECT_TRUE(Artist::find(session, "FooMyArtist").empty()); + EXPECT_TRUE(Artist::find(session, R"(FooMyArtist)").empty()); } { - const auto artists{ Artist::find(session, "%_MyArtist") }; + const auto artists{ Artist::find(session, R"(%_MyArtist)") }; ASSERT_TRUE(artists.size() == 1); ASSERT_EQ(artists.front()->getId(), artist3.getId()); - EXPECT_TRUE(Artist::find(session, "%CMyArtist").empty()); + EXPECT_TRUE(Artist::find(session, R"(%CMyArtist)").empty()); } } { auto transaction{ session.createSharedTransaction() }; { - const auto artists{ Artist::find(session, Artist::FindParameters {}.setKeywords({"MyArtist"})) }; + const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"MyArtist"})) }; EXPECT_EQ(artists.results.size(), 6); } { - const auto artists{ Artist::find(session, Artist::FindParameters {}.setKeywords({"MyArtist%"}).setSortMethod(ArtistSortMethod::ByName)) }; + const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"MyArtist%"}).setSortMethod(ArtistSortMethod::ByName)) }; ASSERT_EQ(artists.results.size(), 2); EXPECT_EQ(artists.results[0], artist1.getId()); EXPECT_EQ(artists.results[1], artist4.getId()); } { - const auto artists{ Artist::find(session, Artist::FindParameters {}.setKeywords({"%MyArtist"}).setSortMethod(ArtistSortMethod::ByName)) }; + const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"%MyArtist"}).setSortMethod(ArtistSortMethod::ByName)) }; ASSERT_EQ(artists.results.size(), 2); EXPECT_EQ(artists.results[0], artist2.getId()); EXPECT_EQ(artists.results[1], artist5.getId()); } { - const auto artists{ Artist::find(session, Artist::FindParameters {}.setKeywords({"_MyArtist"}).setSortMethod(ArtistSortMethod::ByName)) }; + const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"_MyArtist"}).setSortMethod(ArtistSortMethod::ByName)) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results[0], artist3.getId()); } @@ -354,8 +363,8 @@ TEST_F(DatabaseFixture, Artist_sortMethod) { auto transaction{ session.createSharedTransaction() }; - auto allArtistsByName{ Artist::find(session, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::ByName)) }; - auto allArtistsBySortName{ Artist::find(session, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::BySortName)) }; + auto allArtistsByName{ Artist::findIds(session, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::ByName)) }; + auto allArtistsBySortName{ Artist::findIds(session, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::BySortName)) }; ASSERT_EQ(allArtistsByName.results.size(), 2); EXPECT_EQ(allArtistsByName.results.front(), artistA.getId()); @@ -407,7 +416,7 @@ TEST_F(DatabaseFixture, Artist_findByRelease) { auto transaction{ session.createSharedTransaction() }; - const auto artists{ Artist::find(session, Artist::FindParameters {}.setRelease(release.getId())) }; + const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setRelease(release.getId())) }; EXPECT_EQ(artists.results.size(), 0); } @@ -418,7 +427,7 @@ TEST_F(DatabaseFixture, Artist_findByRelease) { auto transaction{ session.createSharedTransaction() }; - const auto artists{ Artist::find(session, Artist::FindParameters {}.setRelease(release.getId())) }; + const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setRelease(release.getId())) }; EXPECT_EQ(artists.results.size(), 0); } @@ -429,7 +438,7 @@ TEST_F(DatabaseFixture, Artist_findByRelease) { auto transaction{ session.createSharedTransaction() }; - const auto artists{ Artist::find(session, Artist::FindParameters {}.setRelease(release.getId())) }; + const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setRelease(release.getId())) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); } diff --git a/src/libs/services/database/test/Cluster.cpp b/src/libs/services/database/test/Cluster.cpp index c914ecfa..8e0186b6 100644 --- a/src/libs/services/database/test/Cluster.cpp +++ b/src/libs/services/database/test/Cluster.cpp @@ -416,7 +416,7 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) EXPECT_TRUE(ClusterType::findOrphans(session, Range{}).results.empty()); EXPECT_EQ(Cluster::findOrphans(session, Range{}).results.size(), 2); EXPECT_TRUE(Release::findOrphanIds(session, Range{}).results.empty()); - EXPECT_TRUE(Artist::findAllOrphans(session, Range{}).results.empty()); + EXPECT_TRUE(Artist::findOrphanIds(session, Range{}).results.empty()); } { @@ -428,12 +428,12 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) { auto transaction{ session.createSharedTransaction() }; - auto artists{ Artist::find(session, Artist::FindParameters {}.setClusters({cluster1.getId()})) }; + auto artists{ Artist::findIds(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::findIds(session, Artist::FindParameters{}.setClusters({ cluster2.getId() })).results.empty()); + EXPECT_TRUE(Artist::findIds(session, Artist::FindParameters{}.setClusters({ cluster3.getId() })).results.empty()); cluster2.get().modify()->addTrack(track.get()); } @@ -441,19 +441,19 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) { auto transaction{ session.createSharedTransaction() }; - auto artists{ Artist::find(session, Artist::FindParameters {}.setClusters({cluster1.getId()})) }; + auto artists{ Artist::findIds(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() })); + artists = Artist::findIds(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() })); + artists = Artist::findIds(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::findIds(session, Artist::FindParameters{}.setClusters({ cluster3.getId() })).results.empty()); } } @@ -476,13 +476,13 @@ TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiRolesMultiClusters) auto transaction{ session.createSharedTransaction() }; EXPECT_TRUE(Cluster::findOrphans(session, Range{}).results.empty()); EXPECT_TRUE(Release::findOrphanIds(session, Range{}).results.empty()); - EXPECT_TRUE(Artist::findAllOrphans(session, Range{}).results.empty()); + EXPECT_TRUE(Artist::findOrphanIds(session, Range{}).results.empty()); } { auto transaction{ session.createSharedTransaction() }; - auto artists{ Artist::find(session, Artist::FindParameters {}.setClusters({cluster.getId()})) }; + auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster.getId()})) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); } @@ -515,7 +515,7 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistMultiClusters) { auto transaction{ session.createSharedTransaction() }; EXPECT_TRUE(Cluster::findOrphans(session, Range{}).results.empty()); - EXPECT_TRUE(Artist::findAllOrphans(session, Range{}).results.empty()); + EXPECT_TRUE(Artist::findOrphanIds(session, Range{}).results.empty()); } { @@ -524,7 +524,7 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistMultiClusters) 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)) }; + auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters(clusterIds)) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); } @@ -627,14 +627,14 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistSingleCluster) 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(Artist::findOrphanIds(session, Range{}).results.empty()); EXPECT_TRUE(Release::findOrphanIds(session, Range{}).results.empty()); } { auto transaction{ session.createSharedTransaction() }; - auto artists{ Artist::find(session, Artist::FindParameters {}.setClusters({cluster.getId()})) }; + auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster.getId()})) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); @@ -1027,9 +1027,9 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleArtistsMultiClusters) { auto transaction{ session.createSharedTransaction() }; - EXPECT_TRUE(artist1->findSimilarArtists().results.empty()); - EXPECT_TRUE(artist2->findSimilarArtists().results.empty()); - EXPECT_TRUE(artist3->findSimilarArtists().results.empty()); + EXPECT_TRUE(artist1->findSimilarArtistIds().results.empty()); + EXPECT_TRUE(artist2->findSimilarArtistIds().results.empty()); + EXPECT_TRUE(artist3->findSimilarArtistIds().results.empty()); } std::list tracks; @@ -1061,35 +1061,35 @@ TEST_F(DatabaseFixture, MultipleTracksMultipleArtistsMultiClusters) auto transaction{ session.createSharedTransaction() }; { - auto artists{ artist1->findSimilarArtists() }; + auto artists{ artist1->findSimilarArtistIds() }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist2.getId()); } { - auto artists{ artist1->findSimilarArtists({TrackArtistLinkType::Artist}) }; + auto artists{ artist1->findSimilarArtistIds({TrackArtistLinkType::Artist}) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist2.getId()); } { - auto artists{ artist1->findSimilarArtists({TrackArtistLinkType::ReleaseArtist}) }; + auto artists{ artist1->findSimilarArtistIds({TrackArtistLinkType::ReleaseArtist}) }; EXPECT_EQ(artists.results.empty(), 1); } { - auto artists{ artist1->findSimilarArtists({TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}) }; + auto artists{ artist1->findSimilarArtistIds({TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist2.getId()); } { - auto artists{ artist1->findSimilarArtists({TrackArtistLinkType::Composer}) }; + auto artists{ artist1->findSimilarArtistIds({TrackArtistLinkType::Composer}) }; EXPECT_TRUE(artists.results.empty()); } { - auto artists{ artist2->findSimilarArtists() }; + auto artists{ artist2->findSimilarArtistIds() }; ASSERT_EQ(artists.results.size(), 2); EXPECT_EQ(artists.results[0], artist1.getId()); EXPECT_EQ(artists.results[1], artist3.getId()); diff --git a/src/libs/services/database/test/DatabaseTest.cpp b/src/libs/services/database/test/DatabaseTest.cpp index 65844bf8..2b35929f 100644 --- a/src/libs/services/database/test/DatabaseTest.cpp +++ b/src/libs/services/database/test/DatabaseTest.cpp @@ -25,85 +25,85 @@ using namespace Database; TEST_F(DatabaseFixture, MultiTracksSingleArtistSingleRelease) { - constexpr std::size_t nbTracks {10}; - std::list tracks; - ScopedArtist artist {session, "MyArtst"}; - ScopedRelease release {session, "MyRelease"}; + constexpr std::size_t nbTracks{ 10 }; + std::list tracks; + ScopedArtist artist{ session, "MyArtst" }; + ScopedRelease release{ session, "MyRelease" }; - for (std::size_t i {}; i < nbTracks; ++i) - { - tracks.emplace_back(session, "MyTrack" + std::to_string(i)); + for (std::size_t i{}; i < nbTracks; ++i) + { + tracks.emplace_back(session, "MyTrack" + std::to_string(i)); - auto transaction {session.createUniqueTransaction()}; + auto transaction{ session.createUniqueTransaction() }; - TrackArtistLink::create(session, tracks.back().get(), artist.get(), TrackArtistLinkType::Artist); - tracks.back().get().modify()->setRelease(release.get()); - } + TrackArtistLink::create(session, tracks.back().get(), artist.get(), TrackArtistLinkType::Artist); + tracks.back().get().modify()->setRelease(release.get()); + } - { - auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(Release::findOrphanIds(session, Range {}).results.empty()); - EXPECT_TRUE(Artist::findAllOrphans(session, Range {}).results.empty()); - } + { + auto transaction{ session.createSharedTransaction() }; + EXPECT_TRUE(Release::findOrphanIds(session, Range{}).results.empty()); + EXPECT_TRUE(Artist::findOrphanIds(session, Range{}).results.empty()); + } - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - auto releases {Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId()))}; - ASSERT_EQ(releases.results.size(), 1); - EXPECT_EQ(releases.results.front(), release.getId()); + auto releases{ Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId())) }; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); - const auto releaseTracks {Track::find(session, Track::FindParameters {}.setRelease(release.getId()))}; - EXPECT_EQ(releaseTracks.results.size(), nbTracks); - } + const auto releaseTracks{ Track::find(session, Track::FindParameters {}.setRelease(release.getId())) }; + EXPECT_EQ(releaseTracks.results.size(), nbTracks); + } } TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtist) { - ScopedTrack track {session, "MyTrack"}; - ScopedRelease release {session, "MyRelease"}; - ScopedArtist artist {session, "MyArtist"}; + ScopedTrack track{ session, "MyTrack" }; + ScopedRelease release{ session, "MyRelease" }; + ScopedArtist artist{ session, "MyArtist" }; - { - 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()); - } + auto trackArtistLink{ TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist) }; + track.get().modify()->setRelease(release.get()); + } - { - auto transaction {session.createUniqueTransaction()}; + { + auto transaction{ session.createUniqueTransaction() }; - auto releases {Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId()))}; - ASSERT_EQ(releases.results.size(), 1); - EXPECT_EQ(releases.results.front(), release.getId()); + auto releases{ Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId())) }; + ASSERT_EQ(releases.results.size(), 1); + EXPECT_EQ(releases.results.front(), release.getId()); - auto artists {release->getArtists()}; - ASSERT_EQ(artists.size(), 1); - ASSERT_EQ(artists.front()->getId(), artist.getId()); - } + auto artists{ release->getArtists() }; + ASSERT_EQ(artists.size(), 1); + ASSERT_EQ(artists.front()->getId(), artist.getId()); + } } TEST_F(DatabaseFixture, SingleUser) { - { - auto transaction {session.createSharedTransaction()}; - EXPECT_TRUE(User::find(session, User::FindParameters {}).results.empty()); - EXPECT_EQ(User::getCount(session), 0); - } + { + auto transaction{ session.createSharedTransaction() }; + EXPECT_TRUE(User::find(session, User::FindParameters{}).results.empty()); + EXPECT_EQ(User::getCount(session), 0); + } - ScopedUser user {session, "MyUser"}; + ScopedUser user{ session, "MyUser" }; - { - auto transaction {session.createSharedTransaction()}; + { + auto transaction{ session.createSharedTransaction() }; - EXPECT_EQ(User::find(session, User::FindParameters {}).results.size(), 1); - EXPECT_EQ(User::getCount(session), 1); - } + EXPECT_EQ(User::find(session, User::FindParameters{}).results.size(), 1); + EXPECT_EQ(User::getCount(session), 1); + } } -int main(int argc, char **argv) +int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/src/libs/services/database/test/StarredArtist.cpp b/src/libs/services/database/test/StarredArtist.cpp index 905fc467..8be5df9b 100644 --- a/src/libs/services/database/test/StarredArtist.cpp +++ b/src/libs/services/database/test/StarredArtist.cpp @@ -37,7 +37,7 @@ TEST_F(DatabaseFixture, StarredArtist) EXPECT_FALSE(starredArtist); EXPECT_EQ(StarredArtist::getCount(session), 0); - auto artists{ Artist::find(session, Artist::FindParameters {}) }; + auto artists{ Artist::findIds(session, Artist::FindParameters {}) }; EXPECT_EQ(artists.results.size(), 1); } @@ -53,13 +53,13 @@ TEST_F(DatabaseFixture, StarredArtist) { auto transaction{ session.createSharedTransaction() }; - auto artists{ Artist::find(session, Artist::FindParameters {}) }; + auto artists{ Artist::findIds(session, Artist::FindParameters {}) }; EXPECT_EQ(artists.results.size(), 1); - artists = Artist::find(session, Artist::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal)); + artists = Artist::findIds(session, Artist::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal)); EXPECT_EQ(artists.results.size(), 1); - artists = Artist::find(session, Artist::FindParameters{}.setStarringUser(user2.getId(), FeedbackBackend::Internal)); + artists = Artist::findIds(session, Artist::FindParameters{}.setStarringUser(user2.getId(), FeedbackBackend::Internal)); EXPECT_EQ(artists.results.size(), 0); } } @@ -73,11 +73,11 @@ TEST_F(DatabaseFixture, StarredArtist_PendingDestroy) { auto transaction{ session.createUniqueTransaction() }; - auto artists{ Artist::find(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) }; + auto artists{ Artist::findIds(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) }; EXPECT_EQ(artists.results.size(), 1); starredArtist.get().modify()->setSyncState(SyncState::PendingRemove); - artists = Artist::find(session, Artist::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal)); + artists = Artist::findIds(session, Artist::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal)); EXPECT_EQ(artists.results.size(), 0); } } @@ -106,7 +106,7 @@ TEST_F(DatabaseFixture, StarredArtist_dateTime) starredArtist1.get().modify()->setDateTime(dateTime); starredArtist2.get().modify()->setDateTime(dateTime.addSecs(-1)); - auto artists{ Artist::find(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(ArtistSortMethod::StarredDateDesc)) }; + auto artists{ Artist::findIds(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(ArtistSortMethod::StarredDateDesc)) }; ASSERT_EQ(artists.results.size(), 2); EXPECT_EQ(artists.results[0], starredArtist1->getArtist()->getId()); EXPECT_EQ(artists.results[1], starredArtist2->getArtist()->getId()); @@ -117,7 +117,7 @@ TEST_F(DatabaseFixture, StarredArtist_dateTime) starredArtist1.get().modify()->setDateTime(dateTime); starredArtist2.get().modify()->setDateTime(dateTime.addSecs(1)); - auto artists{ Artist::find(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(ArtistSortMethod::StarredDateDesc)) }; + auto artists{ Artist::findIds(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(ArtistSortMethod::StarredDateDesc)) }; ASSERT_EQ(artists.results.size(), 2); EXPECT_EQ(artists.results[0], starredArtist2->getArtist()->getId()); EXPECT_EQ(artists.results[1], starredArtist1->getArtist()->getId()); diff --git a/src/libs/services/feedback/impl/FeedbackService.cpp b/src/libs/services/feedback/impl/FeedbackService.cpp index cf414819..2a9a60a9 100644 --- a/src/libs/services/feedback/impl/FeedbackService.cpp +++ b/src/libs/services/feedback/impl/FeedbackService.cpp @@ -103,7 +103,7 @@ namespace Feedback Session& session{ _db.getTLSSession() }; auto transaction{ session.createSharedTransaction() }; - return Artist::find(session, params); + return Artist::findIds(session, params); } void FeedbackService::star(UserId userId, ReleaseId releaseId) diff --git a/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp b/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp index dd11272f..d34fb6f6 100644 --- a/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp +++ b/src/libs/services/recommendation/impl/clusters/ClustersEngine.cpp @@ -104,7 +104,7 @@ namespace Recommendation { if (!artist) return {}; - const auto similarArtistIds{ artist->findSimilarArtists(artistLinkTypes, Range {0, maxCount}) }; + const auto similarArtistIds{ artist->findSimilarArtistIds(artistLinkTypes, Range {0, maxCount}) }; return std::move(similarArtistIds.results); } diff --git a/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp b/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp index f3731f2b..a9572fac 100644 --- a/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp +++ b/src/libs/services/scanner/impl/ScanStepRemoveOrphanDbFiles.cpp @@ -137,7 +137,7 @@ namespace Scanner Session& session {_db.getTLSSession()}; auto transaction {session.createUniqueTransaction()}; - auto artistIds {Artist::findAllOrphans(session, Range {})}; + auto artistIds {Artist::findOrphanIds(session, Range {})}; for (const ArtistId artistId : artistIds.results) { Artist::pointer artist {Artist::find(session, artistId)}; diff --git a/src/libs/subsonic/impl/entrypoints/Browsing.cpp b/src/libs/subsonic/impl/entrypoints/Browsing.cpp index 27abdb77..16b8ed8d 100644 --- a/src/libs/subsonic/impl/entrypoints/Browsing.cpp +++ b/src/libs/subsonic/impl/entrypoints/Browsing.cpp @@ -132,10 +132,9 @@ namespace API::Subsonic auto transaction{ context.dbSession.createSharedTransaction() }; parameters.setRange(Range{ currentArtistOffset, batchSize }); - const RangeResults artists{ Artist::find(context.dbSession, parameters) }; - for (const ArtistId artistId : artists.results) + const auto artists{ Artist::find(context.dbSession, parameters) }; + for (const Artist::pointer& artist : artists.results) { - const Artist::pointer artist{ Artist::find(context.dbSession, artistId) }; std::string_view sortName{ artist->getSortName() }; char sortChar; @@ -144,7 +143,7 @@ namespace API::Subsonic else sortChar = std::toupper(sortName[0]); - artistsSortedByFirstChar[sortChar].push_back(artistId); + artistsSortedByFirstChar[sortChar].push_back(artist->getId()); } hasMoreArtists = artists.moreResults; @@ -317,12 +316,9 @@ namespace API::Subsonic directoryNode.setAttribute("id", idToString(RootId{})); directoryNode.setAttribute("name", "Music"); - auto rootArtistIds{ Artist::find(context.dbSession, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::BySortName)) }; - for (const ArtistId rootArtistId : rootArtistIds.results) - { - const Artist::pointer artist{ Artist::find(context.dbSession, rootArtistId) }; + const auto rootArtistIds{ Artist::find(context.dbSession, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::BySortName)) }; + for (const Artist::pointer& artist : rootArtistIds.results) directoryNode.addArrayChild("child", createArtistNode(context, artist, user, false /* no id3 */)); - } } else if (artistId) { diff --git a/src/libs/subsonic/impl/entrypoints/Searching.cpp b/src/libs/subsonic/impl/entrypoints/Searching.cpp index 59c33fae..a3ed2968 100644 --- a/src/libs/subsonic/impl/entrypoints/Searching.cpp +++ b/src/libs/subsonic/impl/entrypoints/Searching.cpp @@ -72,12 +72,8 @@ namespace API::Subsonic params.setKeywords(keywords); params.setRange({ artistOffset, artistCount }); - RangeResults artistIds{ Artist::find(context.dbSession, params) }; - for (const ArtistId artistId : artistIds.results) - { - const auto artist{ Artist::find(context.dbSession, artistId) }; + for (const Artist::pointer& artist : Artist::find(context.dbSession, params).results) searchResult2Node.addArrayChild("artist", createArtistNode(context, artist, user, id3)); - } } if (albumCount > 0) diff --git a/src/lms/ui/Utils.cpp b/src/lms/ui/Utils.cpp index 0ea27ebf..12c5fb93 100644 --- a/src/lms/ui/Utils.cpp +++ b/src/lms/ui/Utils.cpp @@ -191,7 +191,7 @@ namespace UserInterface::Utils params.setRelease(release->getId()); params.setLinkType(TrackArtistLinkType::ReleaseArtist); - if (const auto releaseArtists{ Artist::find(LmsApp->getDbSession(), params) }; !releaseArtists.results.empty()) + if (const auto releaseArtists{ Artist::findIds(LmsApp->getDbSession(), params) }; !releaseArtists.results.empty()) { if (releaseArtists.results.size() == 1 && releaseArtists.results.front() == omitIfMatchThisArtist) return {}; @@ -200,7 +200,7 @@ namespace UserInterface::Utils } params.setLinkType(TrackArtistLinkType::Artist); - const auto artists{ Artist::find(LmsApp->getDbSession(), params) }; + const auto artists{ Artist::findIds(LmsApp->getDbSession(), params) }; if (artists.results.size() == 1) { if (artists.results.front() == omitIfMatchThisArtist) diff --git a/src/lms/ui/explore/ArtistCollector.cpp b/src/lms/ui/explore/ArtistCollector.cpp index fe3a8a95..67d08238 100644 --- a/src/lms/ui/explore/ArtistCollector.cpp +++ b/src/lms/ui/explore/ArtistCollector.cpp @@ -72,7 +72,7 @@ namespace UserInterface { auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; - artists = Artist::find(LmsApp->getDbSession(), params); + artists = Artist::findIds(LmsApp->getDbSession(), params); } break; } @@ -87,7 +87,7 @@ namespace UserInterface { auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; - artists = Artist::find(LmsApp->getDbSession(), params); + artists = Artist::findIds(LmsApp->getDbSession(), params); } break; } @@ -102,7 +102,7 @@ namespace UserInterface { auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; - artists = Artist::find(LmsApp->getDbSession(), params); + artists = Artist::findIds(LmsApp->getDbSession(), params); } break; } @@ -128,7 +128,7 @@ namespace UserInterface { auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; - _randomArtists = Artist::find(LmsApp->getDbSession(), params); + _randomArtists = Artist::findIds(LmsApp->getDbSession(), params); } } diff --git a/src/lms/ui/explore/ArtistView.cpp b/src/lms/ui/explore/ArtistView.cpp index 540f3f7b..8410ee68 100644 --- a/src/lms/ui/explore/ArtistView.cpp +++ b/src/lms/ui/explore/ArtistView.cpp @@ -46,342 +46,334 @@ using namespace Database; -namespace UserInterface { - -bool -Artist::ReleaseType::operator<(const ReleaseType& other) const +namespace UserInterface { - if (!primaryType && other.primaryType) - return false; - else if (primaryType && !other.primaryType) - return true; - else if (*primaryType == *other.primaryType) - return secondaryTypes.getBitfield() < other.secondaryTypes.getBitfield(); - else - return static_cast(*primaryType) < static_cast(*other.primaryType); -} + namespace + { + std::optional extractArtistIdFromInternalPath() + { + if (wApp->internalPathMatches("/artist/mbid/")) + { + const auto mbid{ UUID::fromString(wApp->internalPathNextPart("/artist/mbid/")) }; + if (mbid) + { + auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; + if (const Database::Artist::pointer artist{ Database::Artist::find(LmsApp->getDbSession(), *mbid) }) + return artist->getId(); + } -Artist::Artist(Filters& filters, PlayQueueController& controller) -: Template {Wt::WString::tr("Lms.Explore.Artist.template")} -, _filters {filters} -, _playQueueController {controller} -{ - addFunction("tr", &Wt::WTemplate::Functions::tr); - addFunction("id", &Wt::WTemplate::Functions::id); + return std::nullopt; + } - LmsApp->internalPathChanged().connect(this, [this] - { - refreshView(); - }); + return StringUtils::readAs(wApp->internalPathNextPart("/artist/")); + } + } - filters.updated().connect([this] - { - _needForceRefresh = true; - refreshView(); - }); + bool Artist::ReleaseType::operator<(const ReleaseType& other) const + { + if (!primaryType && other.primaryType) + return false; + else if (primaryType && !other.primaryType) + return true; + else if (*primaryType == *other.primaryType) + return secondaryTypes.getBitfield() < other.secondaryTypes.getBitfield(); + else + return static_cast(*primaryType) < static_cast(*other.primaryType); + } - refreshView(); -} + Artist::Artist(Filters& filters, PlayQueueController& controller) + : Template{ Wt::WString::tr("Lms.Explore.Artist.template") } + , _filters{ filters } + , _playQueueController{ controller } + { + addFunction("tr", &Wt::WTemplate::Functions::tr); + addFunction("id", &Wt::WTemplate::Functions::id); -static -std::optional -extractArtistIdFromInternalPath() -{ - if (wApp->internalPathMatches("/artist/mbid/")) - { - const auto mbid {UUID::fromString(wApp->internalPathNextPart("/artist/mbid/"))}; - if (mbid) - { - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; - if (const Database::Artist::pointer artist {Database::Artist::find(LmsApp->getDbSession(), *mbid)}) - return artist->getId(); - } + LmsApp->internalPathChanged().connect(this, [this] + { + refreshView(); + }); - return std::nullopt; - } + filters.updated().connect([this] + { + _needForceRefresh = true; + refreshView(); + }); - return StringUtils::readAs(wApp->internalPathNextPart("/artist/")); -} + refreshView(); + } -void -Artist::refreshView() -{ - if (!wApp->internalPathMatches("/artist/")) - return; + void Artist::refreshView() + { + if (!wApp->internalPathMatches("/artist/")) + return; - const auto artistId {extractArtistIdFromInternalPath()}; + const auto artistId{ extractArtistIdFromInternalPath() }; - // consider everything is up to date is the same artist is being rendered - if (!_needForceRefresh && artistId && *artistId == _artistId) - return; + // consider everything is up to date is the same artist is being rendered + if (!_needForceRefresh && artistId && *artistId == _artistId) + return; - clear(); - _artistId = {}; - _trackContainer = nullptr; - _needForceRefresh = false; + clear(); + _artistId = {}; + _trackContainer = nullptr; + _needForceRefresh = false; - if (!artistId) - throw ArtistNotFoundException {}; + if (!artistId) + throw ArtistNotFoundException{}; - const auto similarArtistIds {Service::get()->getSimilarArtists(*artistId, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, 5)}; + const auto similarArtistIds{ Service::get()->getSimilarArtists(*artistId, {TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}, 5) }; - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; - const Database::Artist::pointer artist {Database::Artist::find(LmsApp->getDbSession(), *artistId)}; - if (!artist) - throw ArtistNotFoundException {}; + const Database::Artist::pointer artist{ Database::Artist::find(LmsApp->getDbSession(), *artistId) }; + if (!artist) + throw ArtistNotFoundException{}; - LmsApp->setTitle(artist->getName()); - _artistId = *artistId; + LmsApp->setTitle(artist->getName()); + _artistId = *artistId; - refreshReleases(); - refreshAppearsOnReleases(); - refreshNonReleaseTracks(); - refreshLinks(artist); - refreshSimilarArtists(similarArtistIds); + refreshReleases(); + refreshAppearsOnReleases(); + refreshNonReleaseTracks(); + refreshLinks(artist); + refreshSimilarArtists(similarArtistIds); - Wt::WContainerWidget* clusterContainers {bindNew("clusters")}; + Wt::WContainerWidget* clusterContainers{ bindNew("clusters") }; - { - auto clusterTypes = ScanSettings::get(LmsApp->getDbSession())->getClusterTypes(); - auto clusterGroups = artist->getClusterGroups(clusterTypes, 3); + { + auto clusterTypes = ScanSettings::get(LmsApp->getDbSession())->getClusterTypes(); + auto clusterGroups = artist->getClusterGroups(clusterTypes, 3); - for (auto clusters : clusterGroups) - { - for (const Database::Cluster::pointer& cluster : clusters) - { - const Database::ClusterId clusterId = cluster->getId(); - Wt::WInteractWidget* entry {clusterContainers->addWidget(Utils::createCluster(clusterId))}; - entry->clicked().connect([=] - { - _filters.add(clusterId); - }); - } - } - } + for (auto clusters : clusterGroups) + { + for (const Database::Cluster::pointer& cluster : clusters) + { + const Database::ClusterId clusterId = cluster->getId(); + Wt::WInteractWidget* entry{ clusterContainers->addWidget(Utils::createCluster(clusterId)) }; + entry->clicked().connect([=] + { + _filters.add(clusterId); + }); + } + } + } - bindString("name", Wt::WString::fromUTF8(artist->getName()), Wt::TextFormat::Plain); + bindString("name", Wt::WString::fromUTF8(artist->getName()), Wt::TextFormat::Plain); - bindNew("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML) - ->clicked().connect([=] - { - _playQueueController.processCommand(PlayQueueController::Command::Play, {_artistId}); - }); + bindNew("play-btn", Wt::WString::tr("Lms.Explore.play"), Wt::TextFormat::XHTML) + ->clicked().connect([=] + { + _playQueueController.processCommand(PlayQueueController::Command::Play, { _artistId }); + }); - bindNew("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain) - ->clicked().connect([=] - { - _playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, {_artistId}); - }); - bindNew("play-next", Wt::WString::tr("Lms.Explore.play-next"), Wt::TextFormat::Plain) - ->clicked().connect([=] - { - _playQueueController.processCommand(PlayQueueController::Command::PlayNext, {_artistId}); - }); - bindNew("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain) - ->clicked().connect([=] - { - _playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, {_artistId}); - }); - bindNew("download", Wt::WString::tr("Lms.Explore.download")) - ->setLink(Wt::WLink {std::make_unique(_artistId)}); + bindNew("play-shuffled", Wt::WString::tr("Lms.Explore.play-shuffled"), Wt::TextFormat::Plain) + ->clicked().connect([=] + { + _playQueueController.processCommand(PlayQueueController::Command::PlayShuffled, { _artistId }); + }); + bindNew("play-next", Wt::WString::tr("Lms.Explore.play-next"), Wt::TextFormat::Plain) + ->clicked().connect([=] + { + _playQueueController.processCommand(PlayQueueController::Command::PlayNext, { _artistId }); + }); + bindNew("play-last", Wt::WString::tr("Lms.Explore.play-last"), Wt::TextFormat::Plain) + ->clicked().connect([=] + { + _playQueueController.processCommand(PlayQueueController::Command::PlayOrAddLast, { _artistId }); + }); + bindNew("download", Wt::WString::tr("Lms.Explore.download")) + ->setLink(Wt::WLink{ std::make_unique(_artistId) }); - { - auto isStarred {[=] { return Service::get()->isStarred(LmsApp->getUserId(), _artistId); }}; + { + auto isStarred{ [=] { return Service::get()->isStarred(LmsApp->getUserId(), _artistId); } }; - Wt::WPushButton* starBtn {bindNew("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star"))}; - starBtn->clicked().connect([=] - { - if (isStarred()) - { - Service::get()->unstar(LmsApp->getUserId(), _artistId); - starBtn->setText(Wt::WString::tr("Lms.Explore.star")); - } - else - { - Service::get()->star(LmsApp->getUserId(), _artistId); - starBtn->setText(Wt::WString::tr("Lms.Explore.unstar")); - } - }); - } -} + Wt::WPushButton* starBtn{ bindNew("star", Wt::WString::tr(isStarred() ? "Lms.Explore.unstar" : "Lms.Explore.star")) }; + starBtn->clicked().connect([=] + { + if (isStarred()) + { + Service::get()->unstar(LmsApp->getUserId(), _artistId); + starBtn->setText(Wt::WString::tr("Lms.Explore.star")); + } + else + { + Service::get()->star(LmsApp->getUserId(), _artistId); + starBtn->setText(Wt::WString::tr("Lms.Explore.unstar")); + } + }); + } + } -void -Artist::refreshReleases() -{ - _releaseContainers.clear(); + void Artist::refreshReleases() + { + _releaseContainers.clear(); - Release::FindParameters params; - params.setClusters(_filters.getClusterIds()); - params.setArtist(_artistId, {TrackArtistLinkType::ReleaseArtist}, {}); - params.setSortMethod(ReleaseSortMethod::OriginalDateDesc); + Release::FindParameters params; + params.setClusters(_filters.getClusterIds()); + params.setArtist(_artistId, { TrackArtistLinkType::ReleaseArtist }, {}); + params.setSortMethod(ReleaseSortMethod::OriginalDateDesc); - const auto releases {Release::findIds(LmsApp->getDbSession(), params)}; - if (!releases.results.empty()) - { - // first pass: gather all ids and sort by type - for (const ReleaseId releaseId : releases.results) - { - const Database::Release::pointer release {Database::Release::find(LmsApp->getDbSession(), releaseId)}; + const auto releases{ Release::findIds(LmsApp->getDbSession(), params) }; + if (!releases.results.empty()) + { + // first pass: gather all ids and sort by type + for (const ReleaseId releaseId : releases.results) + { + const Database::Release::pointer release{ Database::Release::find(LmsApp->getDbSession(), releaseId) }; - ReleaseType releaseType {release->getPrimaryType(), release->getSecondaryTypes()}; - _releaseContainers[releaseType].releases.push_back(releaseId); - } + ReleaseType releaseType{ release->getPrimaryType(), release->getSecondaryTypes() }; + _releaseContainers[releaseType].releases.push_back(releaseId); + } - // second pass: construct widgets - Wt::WContainerWidget* releaseContainers {bindNew("release-containers")}; - for (auto& [releaseType, releases] : _releaseContainers) - { - Wt::WTemplate* releaseContainer {releaseContainers->addNew(Wt::WString::tr("Lms.Explore.Artist.template.release-container"))}; + // second pass: construct widgets + Wt::WContainerWidget* releaseContainers{ bindNew("release-containers") }; + for (auto& [releaseType, releases] : _releaseContainers) + { + Wt::WTemplate* releaseContainer{ releaseContainers->addNew(Wt::WString::tr("Lms.Explore.Artist.template.release-container")) }; - if (releaseType.primaryType) - releaseContainer->bindString("release-type", ReleaseHelpers::buildReleaseTypeString(*releaseType.primaryType, releaseType.secondaryTypes)); - else - releaseContainer->bindString("release-type", Wt::WString::tr("Lms.Explore.releases")); // fallback when not tagged with MB + if (releaseType.primaryType) + releaseContainer->bindString("release-type", ReleaseHelpers::buildReleaseTypeString(*releaseType.primaryType, releaseType.secondaryTypes)); + else + releaseContainer->bindString("release-type", Wt::WString::tr("Lms.Explore.releases")); // fallback when not tagged with MB - releases.container = releaseContainer->bindNew("releases", Wt::WString::tr("Lms.Explore.Releases.template.container")); - releases.container->onRequestElements.connect(this, [this, &releases = releases] - { - addSomeReleases(releases); - }); - } - } - else - { - bindEmpty("release-containers"); - } -} + releases.container = releaseContainer->bindNew("releases", Wt::WString::tr("Lms.Explore.Releases.template.container")); + releases.container->onRequestElements.connect(this, [this, &releases = releases] + { + addSomeReleases(releases); + }); + } + } + else + { + bindEmpty("release-containers"); + } + } -void -Artist::refreshAppearsOnReleases() -{ - constexpr EnumSet types - { - TrackArtistLinkType::Artist, - TrackArtistLinkType::Arranger, - TrackArtistLinkType::Composer, - TrackArtistLinkType::Conductor, - TrackArtistLinkType::Lyricist, - TrackArtistLinkType::Mixer, - TrackArtistLinkType::Performer, - TrackArtistLinkType::Producer, - TrackArtistLinkType::Remixer, - TrackArtistLinkType::Writer, - }; + void Artist::refreshAppearsOnReleases() + { + constexpr EnumSet types + { + TrackArtistLinkType::Artist, + TrackArtistLinkType::Arranger, + TrackArtistLinkType::Composer, + TrackArtistLinkType::Conductor, + TrackArtistLinkType::Lyricist, + TrackArtistLinkType::Mixer, + TrackArtistLinkType::Performer, + TrackArtistLinkType::Producer, + TrackArtistLinkType::Remixer, + TrackArtistLinkType::Writer, + }; - _appearsOnReleaseContainer = {}; + _appearsOnReleaseContainer = {}; - Release::FindParameters params; - params.setClusters(_filters.getClusterIds()); - params.setArtist(_artistId, types, {TrackArtistLinkType::ReleaseArtist}); - params.setSortMethod(ReleaseSortMethod::OriginalDateDesc); + Release::FindParameters params; + params.setClusters(_filters.getClusterIds()); + params.setArtist(_artistId, types, { TrackArtistLinkType::ReleaseArtist }); + params.setSortMethod(ReleaseSortMethod::OriginalDateDesc); - const auto releases {Release::findIds(LmsApp->getDbSession(), params)}; - if (!releases.results.empty()) - { - Wt::WTemplate* releaseContainer {bindNew("appears-on-releases", Wt::WString::tr("Lms.Explore.Artist.template.release-container"))}; - releaseContainer->bindString("release-type", Wt::WString::tr("Lms.Explore.Artist.appears-on")); - _appearsOnReleaseContainer.releases = releases.results; - _appearsOnReleaseContainer.container = releaseContainer->bindNew("releases", Wt::WString::tr("Lms.Explore.Releases.template.container")); - _appearsOnReleaseContainer.container->onRequestElements.connect(this, [this] - { - addSomeReleases(_appearsOnReleaseContainer); - }); - } - else - { - bindEmpty("appears-on-releases"); - } -} + const auto releases{ Release::findIds(LmsApp->getDbSession(), params) }; + if (!releases.results.empty()) + { + Wt::WTemplate* releaseContainer{ bindNew("appears-on-releases", Wt::WString::tr("Lms.Explore.Artist.template.release-container")) }; + releaseContainer->bindString("release-type", Wt::WString::tr("Lms.Explore.Artist.appears-on")); + _appearsOnReleaseContainer.releases = releases.results; + _appearsOnReleaseContainer.container = releaseContainer->bindNew("releases", Wt::WString::tr("Lms.Explore.Releases.template.container")); + _appearsOnReleaseContainer.container->onRequestElements.connect(this, [this] + { + addSomeReleases(_appearsOnReleaseContainer); + }); + } + else + { + bindEmpty("appears-on-releases"); + } + } -void -Artist::refreshNonReleaseTracks() -{ - setCondition("if-has-non-release-tracks", true); - _trackContainer = bindNew("tracks"); - _trackContainer->onRequestElements.connect(this, [this] - { - addSomeNonReleaseTracks(); - }); + void Artist::refreshNonReleaseTracks() + { + setCondition("if-has-non-release-tracks", true); + _trackContainer = bindNew("tracks"); + _trackContainer->onRequestElements.connect(this, [this] + { + addSomeNonReleaseTracks(); + }); - const bool added {addSomeNonReleaseTracks()}; - setCondition("if-has-non-release-tracks", added); -} + const bool added{ addSomeNonReleaseTracks() }; + setCondition("if-has-non-release-tracks", added); + } -void -Artist::refreshSimilarArtists(const std::vector& similarArtistsId) -{ - if (similarArtistsId.empty()) - return; + void Artist::refreshSimilarArtists(const std::vector& similarArtistsId) + { + if (similarArtistsId.empty()) + return; - setCondition("if-has-similar-artists", true); - Wt::WContainerWidget* similarArtistsContainer {bindNew("similar-artists")}; + setCondition("if-has-similar-artists", true); + Wt::WContainerWidget* similarArtistsContainer{ bindNew("similar-artists") }; - for (const ArtistId artistId : similarArtistsId) - { - const Database::Artist::pointer similarArtist {Database::Artist::find(LmsApp->getDbSession(), artistId)}; - if (!similarArtist) - continue; + for (const ArtistId artistId : similarArtistsId) + { + const Database::Artist::pointer similarArtist{ Database::Artist::find(LmsApp->getDbSession(), artistId) }; + if (!similarArtist) + continue; - similarArtistsContainer->addWidget(ArtistListHelpers::createEntry(similarArtist)); - } -} + similarArtistsContainer->addWidget(ArtistListHelpers::createEntry(similarArtist)); + } + } -void -Artist::refreshLinks(const Database::Artist::pointer& artist) -{ - const auto mbid {artist->getMBID()}; - if (mbid) - { - setCondition("if-has-mbid", true); - bindString("mbid-link", std::string {"https://musicbrainz.org/artist/"} + std::string {mbid->getAsString()}); - } -} + void Artist::refreshLinks(const Database::Artist::pointer& artist) + { + const auto mbid{ artist->getMBID() }; + if (mbid) + { + setCondition("if-has-mbid", true); + bindString("mbid-link", std::string{ "https://musicbrainz.org/artist/" } + std::string{ mbid->getAsString() }); + } + } -void -Artist::addSomeReleases(ReleaseContainer& releaseContainer) -{ - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + void Artist::addSomeReleases(ReleaseContainer& releaseContainer) + { + auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; - if (const Database::Artist::pointer artist {Database::Artist::find(LmsApp->getDbSession(), _artistId)}) - { - for (std::size_t i {static_cast(releaseContainer.container->getCount())}; i < releaseContainer.releases.size(); ++i) - { - const Database::Release::pointer release {Database::Release::find(LmsApp->getDbSession(), releaseContainer.releases[i])}; - releaseContainer.container->add(ReleaseListHelpers::createEntryForArtist(release, artist)); - } - } -} + if (const Database::Artist::pointer artist{ Database::Artist::find(LmsApp->getDbSession(), _artistId) }) + { + for (std::size_t i{ static_cast(releaseContainer.container->getCount()) }; i < releaseContainer.releases.size(); ++i) + { + const Database::Release::pointer release{ Database::Release::find(LmsApp->getDbSession(), releaseContainer.releases[i]) }; + releaseContainer.container->add(ReleaseListHelpers::createEntryForArtist(release, artist)); + } + } + } -bool -Artist::addSomeNonReleaseTracks() -{ - bool areTracksAdded{}; - auto transaction {LmsApp->getDbSession().createSharedTransaction()}; + bool Artist::addSomeNonReleaseTracks() + { + bool areTracksAdded{}; + auto transaction{ LmsApp->getDbSession().createSharedTransaction() }; - const Range range {static_cast(_trackContainer->getCount()), _tracksBatchSize}; + const Range range{ static_cast(_trackContainer->getCount()), _tracksBatchSize }; - Track::FindParameters params; - params.setClusters(_filters.getClusterIds()); - params.setArtist(_artistId); - params.setRange(range); - params.setSortMethod(TrackSortMethod::Name); - params.setNonRelease(true); + Track::FindParameters params; + params.setClusters(_filters.getClusterIds()); + params.setArtist(_artistId); + params.setRange(range); + params.setSortMethod(TrackSortMethod::Name); + params.setNonRelease(true); - const auto tracks {Track::find(LmsApp->getDbSession(), params)}; - for (const Track::pointer& track : tracks.results) - { - // TODO handle this with range - if (_trackContainer->getCount() == _tracksMaxCount) - break; + const auto tracks{ Track::find(LmsApp->getDbSession(), params) }; + for (const Track::pointer& track : tracks.results) + { + // TODO handle this with range + if (_trackContainer->getCount() == _tracksMaxCount) + break; - _trackContainer->add(TrackListHelpers::createEntry(track, _playQueueController, _filters)); + _trackContainer->add(TrackListHelpers::createEntry(track, _playQueueController, _filters)); - areTracksAdded = true; - } + areTracksAdded = true; + } - return areTracksAdded; -} + return areTracksAdded; + } } // namespace UserInterface diff --git a/src/lms/ui/explore/ReleaseView.cpp b/src/lms/ui/explore/ReleaseView.cpp index 40f7ebd8..40754fc2 100644 --- a/src/lms/ui/explore/ReleaseView.cpp +++ b/src/lms/ui/explore/ReleaseView.cpp @@ -79,7 +79,7 @@ namespace UserInterface Artist::FindParameters params; params.setRelease(releaseId); params.setLinkType(linkType); - const auto artistIds{ Artist::find(LmsApp->getDbSession(), params) }; + const auto artistIds{ Artist::findIds(LmsApp->getDbSession(), params) }; if (artistIds.results.empty()) return; diff --git a/src/lms/ui/explore/TrackListHelpers.cpp b/src/lms/ui/explore/TrackListHelpers.cpp index fdd064d3..75cc4f68 100644 --- a/src/lms/ui/explore/TrackListHelpers.cpp +++ b/src/lms/ui/explore/TrackListHelpers.cpp @@ -67,7 +67,7 @@ namespace UserInterface::TrackListHelpers Artist::FindParameters params; params.setTrack(trackId); params.setLinkType(linkType); - const auto artistIds {Artist::find(LmsApp->getDbSession(), params)}; + const auto artistIds {Artist::findIds(LmsApp->getDbSession(), params)}; if (artistIds.results.empty()) return; diff --git a/src/tools/recommendation/LmsRecommendation.cpp b/src/tools/recommendation/LmsRecommendation.cpp index 64e722a1..33d73d6c 100644 --- a/src/tools/recommendation/LmsRecommendation.cpp +++ b/src/tools/recommendation/LmsRecommendation.cpp @@ -102,7 +102,7 @@ static void dumpArtistsRecommendation(Session session, Recommendation::IRecommen const RangeResults artistIds = std::invoke([&]() { auto transaction{ session.createSharedTransaction() }; - return Artist::find(session, Artist::FindParameters{}); + return Artist::findIds(session, Artist::FindParameters{}); }); std::cout << "*** Artists ***" << std::endl;