Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Small database batch refactoring to speed up some subsonic API queries #361

Merged
merged 3 commits into from
Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
844 changes: 412 additions & 432 deletions src/libs/services/cover/impl/CoverService.cpp

Large diffs are not rendered by default.

572 changes: 293 additions & 279 deletions src/libs/services/database/impl/Artist.cpp

Large diffs are not rendered by default.

263 changes: 144 additions & 119 deletions src/libs/services/database/impl/Release.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,152 +34,170 @@

namespace Database
{

Wt::Dbo::Query<ReleaseId> createQuery(Session& session, const Release::FindParameters& params)
namespace
{
auto query{ session.getDboSession().query<ReleaseId>("SELECT DISTINCT r.id from release r") };

if (params.sortMethod == ReleaseSortMethod::LastWritten
|| params.sortMethod == ReleaseSortMethod::Date
|| params.sortMethod == ReleaseSortMethod::OriginalDate
|| params.sortMethod == ReleaseSortMethod::OriginalDateDesc
|| params.writtenAfter.isValid()
|| params.dateRange
|| params.artist.isValid()
|| params.clusters.size() == 1)
template <typename ResultType>
Wt::Dbo::Query<ResultType> createQuery(Session& session, std::string_view itemToSelect, const Release::FindParameters& params)

Check warning

Code scanning / CodeQL

Poorly documented large function Warning

Poorly documented function: fewer than 2% comments for a function of 147 lines.
{
query.join("track t ON t.release_id = r.id");
}
auto query{ session.getDboSession().query<ResultType>("SELECT DISTINCT " + std::string{ itemToSelect } + " from release r") };

if (params.sortMethod == ReleaseSortMethod::LastWritten
|| params.sortMethod == ReleaseSortMethod::Date
|| params.sortMethod == ReleaseSortMethod::OriginalDate
|| params.sortMethod == ReleaseSortMethod::OriginalDateDesc
|| params.writtenAfter.isValid()
|| params.dateRange
|| params.artist.isValid()
|| params.clusters.size() == 1)
{
query.join("track t ON t.release_id = r.id");
}

if (params.writtenAfter.isValid())
query.where("t.file_last_write > ?").bind(params.writtenAfter);
if (params.writtenAfter.isValid())
query.where("t.file_last_write > ?").bind(params.writtenAfter);

if (params.dateRange)
{
query.where("t.date >= ?").bind(params.dateRange->begin);
query.where("t.date <= ?").bind(params.dateRange->end);
}
if (params.dateRange)
{
query.where("t.date >= ?").bind(params.dateRange->begin);
query.where("t.date <= ?").bind(params.dateRange->end);
}

for (std::string_view keyword : params.keywords)
query.where("r.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + Utils::escapeLikeKeyword(keyword) + "%");
for (std::string_view keyword : params.keywords)
query.where("r.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + Utils::escapeLikeKeyword(keyword) + "%");

if (params.starringUser.isValid())
{
assert(params.feedbackBackend);
query.join("starred_release s_r ON s_r.release_id = r.id")
.where("s_r.user_id = ?").bind(params.starringUser)
.where("s_r.backend = ?").bind(*params.feedbackBackend)
.where("s_r.sync_state <> ?").bind(SyncState::PendingRemove);
}

if (params.artist.isValid())
{
query.join("artist a ON a.id = t_a_l.artist_id")
.join("track_artist_link t_a_l ON t_a_l.track_id = t.id")
.where("a.id = ?").bind(params.artist);
if (params.starringUser.isValid())
{
assert(params.feedbackBackend);
query.join("starred_release s_r ON s_r.release_id = r.id")
.where("s_r.user_id = ?").bind(params.starringUser)
.where("s_r.backend = ?").bind(*params.feedbackBackend)
.where("s_r.sync_state <> ?").bind(SyncState::PendingRemove);
}

if (!params.trackArtistLinkTypes.empty())
if (params.artist.isValid())
{
std::ostringstream oss;
query.join("artist a ON a.id = t_a_l.artist_id")
.join("track_artist_link t_a_l ON t_a_l.track_id = t.id")
.where("a.id = ?").bind(params.artist);

bool first{ true };
for (TrackArtistLinkType linkType : params.trackArtistLinkTypes)
if (!params.trackArtistLinkTypes.empty())
{
if (!first)
oss << " OR ";
oss << "t_a_l.type = ?";
query.bind(linkType);
std::ostringstream oss;

bool first{ true };
for (TrackArtistLinkType linkType : params.trackArtistLinkTypes)
{
if (!first)
oss << " OR ";
oss << "t_a_l.type = ?";
query.bind(linkType);

first = false;
}
query.where(oss.str());
}

first = false;
if (!params.excludedTrackArtistLinkTypes.empty())
{
std::ostringstream oss;
oss << "r.id NOT IN (SELECT DISTINCT r.id FROM release r"
" 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"
" INNER JOIN track t ON t.release_id = r.id"
" WHERE (a.id = ? AND (";

query.bind(params.artist);

bool first{ true };
for (const TrackArtistLinkType linkType : params.excludedTrackArtistLinkTypes)
{
if (!first)
oss << " OR ";
oss << "t_a_l.type = ?";
query.bind(linkType);

first = false;
}
oss << ")))";
query.where(oss.str());
}
query.where(oss.str());
}

if (!params.excludedTrackArtistLinkTypes.empty())
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 << "r.id NOT IN (SELECT DISTINCT r.id FROM release r"
" 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"
oss << "r.id IN (SELECT DISTINCT r.id FROM release r"
" INNER JOIN track t ON t.release_id = r.id"
" WHERE (a.id = ? AND (";

query.bind(params.artist);
" INNER JOIN track_cluster t_c ON t_c.track_id = t.id";

bool first{ true };
for (const TrackArtistLinkType linkType : params.excludedTrackArtistLinkTypes)
WhereClause clusterClause;
for (const ClusterId clusterId : params.clusters)
{
if (!first)
oss << " OR ";
oss << "t_a_l.type = ?";
query.bind(linkType);

first = false;
clusterClause.Or(WhereClause("t_c.cluster_id = ?"));
query.bind(clusterId);
}
oss << ")))";

oss << " " << clusterClause.get();
oss << " GROUP BY t.id HAVING COUNT(*) = " << params.clusters.size() << ")";

query.where(oss.str());
}
}

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 << "r.id IN (SELECT DISTINCT r.id FROM release r"
" INNER JOIN track t ON t.release_id = r.id"
" INNER JOIN track_cluster t_c ON t_c.track_id = t.id";
if (params.primaryType)
query.where("primary_type = ?").bind(*params.primaryType);
if (!params.secondaryTypes.empty())
query.where("secondary_type = ?").bind(params.secondaryTypes);

WhereClause clusterClause;
for (const ClusterId clusterId : params.clusters)
switch (params.sortMethod)
{
clusterClause.Or(WhereClause("t_c.cluster_id = ?"));
query.bind(clusterId);
case ReleaseSortMethod::None:
break;
case ReleaseSortMethod::Name:
query.orderBy("r.name COLLATE NOCASE");
break;
case ReleaseSortMethod::Random:
query.orderBy("RANDOM()");
break;
case ReleaseSortMethod::LastWritten:
query.orderBy("t.file_last_write DESC");
break;
case ReleaseSortMethod::Date:
query.orderBy("t.date, r.name COLLATE NOCASE");
break;
case ReleaseSortMethod::OriginalDate:
query.orderBy("CASE WHEN t.original_date IS NULL THEN t.date ELSE t.original_date END, t.date, r.name COLLATE NOCASE");
break;
case ReleaseSortMethod::OriginalDateDesc:
query.orderBy("CASE WHEN t.original_date IS NULL THEN t.date ELSE t.original_date END DESC, t.date, r.name COLLATE NOCASE");
break;
case ReleaseSortMethod::StarredDateDesc:
assert(params.starringUser.isValid());
query.orderBy("s_r.date_time DESC");
break;
}

oss << " " << clusterClause.get();
oss << " GROUP BY t.id HAVING COUNT(*) = " << params.clusters.size() << ")";

query.where(oss.str());
return query;
}

if (params.primaryType)
query.where("primary_type = ?").bind(*params.primaryType);
if (!params.secondaryTypes.empty())
query.where("secondary_type = ?").bind(params.secondaryTypes);

switch (params.sortMethod)
template <typename ResultType>
Wt::Dbo::Query<ResultType> createQuery(Session& session, const Release::FindParameters& params)
{
case ReleaseSortMethod::None:
break;
case ReleaseSortMethod::Name:
query.orderBy("r.name COLLATE NOCASE");
break;
case ReleaseSortMethod::Random:
query.orderBy("RANDOM()");
break;
case ReleaseSortMethod::LastWritten:
query.orderBy("t.file_last_write DESC");
break;
case ReleaseSortMethod::Date:
query.orderBy("t.date, r.name COLLATE NOCASE");
break;
case ReleaseSortMethod::OriginalDate:
query.orderBy("CASE WHEN t.original_date IS NULL THEN t.date ELSE t.original_date END, t.date, r.name COLLATE NOCASE");
break;
case ReleaseSortMethod::OriginalDateDesc:
query.orderBy("CASE WHEN t.original_date IS NULL THEN t.date ELSE t.original_date END DESC, t.date, r.name COLLATE NOCASE");
break;
case ReleaseSortMethod::StarredDateDesc:
assert(params.starringUser.isValid());
query.orderBy("s_r.date_time DESC");
break;
std::string_view itemToSelect;

if constexpr (std::is_same_v<ResultType, ReleaseId>)
itemToSelect = "r.id";
else if constexpr (std::is_same_v<ResultType, Wt::Dbo::ptr<Release>>)
itemToSelect = "r";
else
static_assert("Unhandled type");

return createQuery<ResultType>(session, itemToSelect, params);
}

return query;
}

Release::Release(const std::string& name, const std::optional<UUID>& MBID)
Expand Down Expand Up @@ -238,7 +256,7 @@
return session.getDboSession().query<int>("SELECT COUNT(*) FROM release");
}

RangeResults<ReleaseId> Release::findOrderedByArtist(Session& session, Range range)
RangeResults<ReleaseId> Release::findIdsOrderedByArtist(Session& session, Range range)
{
session.checkSharedLocked();

Expand All @@ -253,20 +271,27 @@
return Utils::execQuery(query, range);
}

RangeResults<ReleaseId> Release::findOrphans(Session& session, Range range)
RangeResults<ReleaseId> Release::findOrphanIds(Session& session, Range range)
{
session.checkSharedLocked();

auto query{ session.getDboSession().query<ReleaseId>("select r.id from release r LEFT OUTER JOIN Track t ON r.id = t.release_id WHERE t.id IS NULL") };
return Utils::execQuery(query, range);
}

RangeResults<ReleaseId> Release::find(Session& session, const FindParameters& params)
RangeResults<Release::pointer> Release::find(Session& session, const FindParameters& params)
{
session.checkSharedLocked();

auto query{ createQuery(session, params) };
auto query{ createQuery<Wt::Dbo::ptr<Release>>(session, params) };
return Utils::execQuery(query, params.range);
}

RangeResults<ReleaseId> Release::findIds(Session& session, const FindParameters& params)
{
session.checkSharedLocked();

auto query{ createQuery<ReleaseId>(session, params) };
return Utils::execQuery(query, params.range);
}

Expand Down
Loading