SubsonicAPI: optimized search for artists
This commit is contained in:
@@ -33,284 +33,298 @@
|
||||
|
||||
namespace Database
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template <typename ResultType>
|
||||
Wt::Dbo::Query<ResultType> createQuery(Session& session, std::string_view itemToSelect, const Artist::FindParameters& params)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
|
||||
Artist::Artist(const std::string& name, const std::optional<UUID>& MBID)
|
||||
: _name {std::string(name, 0 , _maxNameLength)},
|
||||
_sortName {_name},
|
||||
_MBID {MBID ? MBID->getAsString() : ""}
|
||||
{
|
||||
}
|
||||
auto query{ session.getDboSession().query<ResultType>("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<UUID>& MBID)
|
||||
{
|
||||
return session.getDboSession().add(std::unique_ptr<Artist> {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<int>("SELECT COUNT(*) FROM artist");
|
||||
}
|
||||
if (!params.keywords.empty())
|
||||
{
|
||||
std::vector<std::string> clauses;
|
||||
std::vector<std::string> sortClauses;
|
||||
|
||||
std::vector<Artist::pointer>
|
||||
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<Wt::Dbo::ptr<Artist>> res = session.getDboSession().find<Artist>()
|
||||
.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<Artist::pointer>(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<Artist>().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<Artist>().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<int>("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<ArtistId>
|
||||
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<ArtistId>("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<std::string> clauses;
|
||||
std::vector<std::string> 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 <typename ResultType>
|
||||
Wt::Dbo::Query<ResultType> createQuery(Session& session, const Artist::FindParameters& params)
|
||||
{
|
||||
std::string_view itemToSelect;
|
||||
|
||||
if constexpr (std::is_same_v<ResultType, ArtistId>)
|
||||
itemToSelect = "a.id";
|
||||
else if constexpr (std::is_same_v<ResultType, Wt::Dbo::ptr<Artist>>)
|
||||
itemToSelect = "a";
|
||||
else
|
||||
static_assert("Unhandled type");
|
||||
|
||||
query.where("(" + StringUtils::joinStrings(clauses, " AND ") + ") OR (" + StringUtils::joinStrings(sortClauses, " AND ") + ")");
|
||||
}
|
||||
return createQuery<ResultType>(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<UUID>& 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<UUID>& MBID)
|
||||
{
|
||||
return session.getDboSession().add(std::unique_ptr<Artist> {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<int>("SELECT COUNT(*) FROM artist");
|
||||
}
|
||||
|
||||
query.where(oss.str());
|
||||
}
|
||||
std::vector<Artist::pointer> Artist::find(Session& session, const std::string& name)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
|
||||
if (params.track.isValid())
|
||||
query.where("t.id = ?").bind(params.track);
|
||||
Wt::Dbo::collection<Wt::Dbo::ptr<Artist>> res = session.getDboSession().find<Artist>()
|
||||
.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<Artist::pointer>(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<Artist>().where("mbid = ?").bind(std::string{ mbid.getAsString() }).resultValue();
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
Artist::pointer Artist::find(Session& session, ArtistId id)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
return session.getDboSession().find<Artist>().where("id = ?").bind(id).resultValue();
|
||||
}
|
||||
|
||||
RangeResults<ArtistId>
|
||||
Artist::findAllOrphans(Session& session, Range range)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
auto query {session.getDboSession().query<ArtistId>("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<int>("SELECT 1 FROM artist").where("id = ?").bind(id).resultValue() == 1;
|
||||
}
|
||||
|
||||
return Utils::execQuery(query, range);
|
||||
}
|
||||
|
||||
RangeResults<ArtistId>
|
||||
Artist::find(Session& session, const FindParameters& params)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
RangeResults<ArtistId> Artist::findOrphanIds(Session& session, Range range)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
auto query{ session.getDboSession().query<ArtistId>("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<ArtistId>
|
||||
Artist::findSimilarArtists(EnumSet<TrackArtistLinkType> artistLinkTypes, Range range) const
|
||||
{
|
||||
assert(session());
|
||||
RangeResults<ArtistId> 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<ArtistId>(session, params) };
|
||||
return Utils::execQuery(query, params.range);
|
||||
}
|
||||
|
||||
if (!artistLinkTypes.empty())
|
||||
{
|
||||
oss << " AND t_a_l.type IN (";
|
||||
RangeResults<Artist::pointer> 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<Wt::Dbo::ptr<Artist>>(session, params) };
|
||||
return Utils::execQuery(query, params.range);
|
||||
}
|
||||
|
||||
auto query {session()->query<ArtistId>(oss.str())
|
||||
.bind(getId())
|
||||
.bind(getId())
|
||||
.groupBy("a.id")
|
||||
.orderBy("COUNT(*) DESC, RANDOM()")};
|
||||
RangeResults<ArtistId> Artist::findSimilarArtistIds(EnumSet<TrackArtistLinkType> 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<std::vector<Cluster::pointer>>
|
||||
Artist::getClusterGroups(std::vector<ClusterType::pointer> 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<ArtistId>(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<std::vector<Cluster::pointer>> Artist::getClusterGroups(std::vector<ClusterType::pointer> clusterTypes, std::size_t size) const
|
||||
{
|
||||
assert(session());
|
||||
|
||||
Wt::Dbo::Query<Wt::Dbo::ptr<Cluster>> query = session()->query<Wt::Dbo::ptr<Cluster>>( 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<Wt::Dbo::ptr<Cluster>> 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<ClusterTypeId, std::vector<Cluster::pointer>> 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<std::vector<Cluster::pointer>> res;
|
||||
for (const auto& [clusterTypeId, clusters] : clustersByType)
|
||||
res.push_back(clusters);
|
||||
Wt::Dbo::Query<Wt::Dbo::ptr<Cluster>> query = session()->query<Wt::Dbo::ptr<Cluster>>(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<Wt::Dbo::ptr<Cluster>> queryRes = query;
|
||||
|
||||
std::map<ClusterTypeId, std::vector<Cluster::pointer>> clustersByType;
|
||||
for (Cluster::pointer cluster : queryRes)
|
||||
{
|
||||
if (clustersByType[cluster->getType()->getId()].size() < size)
|
||||
clustersByType[cluster->getType()->getId()].push_back(cluster);
|
||||
}
|
||||
|
||||
std::vector<std::vector<Cluster::pointer>> 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
|
||||
|
||||
@@ -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<Artist, ArtistId>
|
||||
{
|
||||
public:
|
||||
struct FindParameters
|
||||
{
|
||||
std::vector<ClusterId> clusters; // if non empty, at least one artist that belongs to these clusters
|
||||
std::vector<std::string_view> keywords; // if non empty, name must match all of these keywords (on either name field OR sort name field)
|
||||
std::optional<TrackArtistLinkType> 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> 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<Artist, ArtistId>
|
||||
{
|
||||
public:
|
||||
struct FindParameters
|
||||
{
|
||||
std::vector<ClusterId> clusters; // if non empty, at least one artist that belongs to these clusters
|
||||
std::vector<std::string_view> keywords; // if non empty, name must match all of these keywords (on either name field OR sort name field)
|
||||
std::optional<TrackArtistLinkType> 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> 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<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
||||
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
||||
FindParameters& setLinkType(std::optional<TrackArtistLinkType> _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<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
||||
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
||||
FindParameters& setLinkType(std::optional<TrackArtistLinkType> _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<pointer> find(Session& session, const std::string& name); // exact match on name field
|
||||
static RangeResults<ArtistId> find(Session& session, const FindParameters& parameters);
|
||||
static RangeResults<ArtistId> 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<pointer> find(Session& session, const std::string& name); // exact match on name field
|
||||
static RangeResults<pointer> find(Session& session, const FindParameters& parameters);
|
||||
static RangeResults<ArtistId> findIds(Session& session, const FindParameters& parameters);
|
||||
static RangeResults<ArtistId> 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<UUID> getMBID() const { return UUID::fromString(_MBID); }
|
||||
// Accessors
|
||||
const std::string& getName() const { return _name; }
|
||||
const std::string& getSortName() const { return _sortName; }
|
||||
std::optional<UUID> getMBID() const { return UUID::fromString(_MBID); }
|
||||
|
||||
// No artistLinkTypes means get them all
|
||||
RangeResults<ArtistId> findSimilarArtists(EnumSet<TrackArtistLinkType> artistLinkTypes = {}, Range range = {}) const;
|
||||
// No artistLinkTypes means get them all
|
||||
RangeResults<ArtistId> findSimilarArtistIds(EnumSet<TrackArtistLinkType> 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<std::vector<ObjectPtr<Cluster>>> getClusterGroups(std::vector<ObjectPtr<ClusterType>> 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<std::vector<ObjectPtr<Cluster>>> getClusterGroups(std::vector<ObjectPtr<ClusterType>> clusterTypes, std::size_t size) const;
|
||||
|
||||
void setName(std::string_view name) { _name = name; }
|
||||
void setMBID(const std::optional<UUID>& mbid) { _MBID = mbid ? mbid->getAsString() : ""; }
|
||||
void setSortName(const std::string& sortName);
|
||||
void setName(std::string_view name) { _name = name; }
|
||||
void setMBID(const std::optional<UUID>& mbid) { _MBID = mbid ? mbid->getAsString() : ""; }
|
||||
void setSortName(const std::string& sortName);
|
||||
|
||||
template<class Action>
|
||||
void persist(Action& a)
|
||||
{
|
||||
Wt::Dbo::field(a, _name, "name");
|
||||
Wt::Dbo::field(a, _sortName, "sort_name");
|
||||
Wt::Dbo::field(a, _MBID, "mbid");
|
||||
template<class Action>
|
||||
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<UUID>& MBID = {});
|
||||
static pointer create(Session& session, const std::string& name, const std::optional<UUID>& UUID = {});
|
||||
friend class Session;
|
||||
// Create
|
||||
Artist(const std::string& name, const std::optional<UUID>& MBID = {});
|
||||
static pointer create(Session& session, const std::string& name, const std::optional<UUID>& 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<Wt::Dbo::ptr<TrackArtistLink>> _trackArtistLinks; // Tracks involving this artist
|
||||
Wt::Dbo::collection<Wt::Dbo::ptr<StarredArtist>> _starredArtists; // starred entries for this artist
|
||||
};
|
||||
Wt::Dbo::collection<Wt::Dbo::ptr<TrackArtistLink>> _trackArtistLinks; // Tracks involving this artist
|
||||
Wt::Dbo::collection<Wt::Dbo::ptr<StarredArtist>> _starredArtists; // starred entries for this artist
|
||||
};
|
||||
|
||||
} // namespace Database
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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<ClusterId> 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<ScopedTrack> 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());
|
||||
|
||||
@@ -25,85 +25,85 @@ using namespace Database;
|
||||
|
||||
TEST_F(DatabaseFixture, MultiTracksSingleArtistSingleRelease)
|
||||
{
|
||||
constexpr std::size_t nbTracks {10};
|
||||
std::list<ScopedTrack> tracks;
|
||||
ScopedArtist artist {session, "MyArtst"};
|
||||
ScopedRelease release {session, "MyRelease"};
|
||||
constexpr std::size_t nbTracks{ 10 };
|
||||
std::list<ScopedTrack> 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();
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user