Tried to factorize some db code

This commit is contained in:
emeric
2022-06-10 21:43:28 +02:00
parent a5c5185640
commit 27dfaf999a
15 changed files with 167 additions and 208 deletions
-108
View File
@@ -204,114 +204,6 @@ Artist::find(Session& session, const FindParameters& params)
return execQuery(query, params.range);
}
RangeResults<ReleaseId>
Artist::getReleases(Range range, const std::vector<ClusterId>& clusterIds) const
{
assert(session());
WhereClause where;
std::ostringstream oss;
oss << "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";
if (!clusterIds.empty())
{
oss << " INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id";
WhereClause clusterClause;
for (auto id : clusterIds)
clusterClause.Or(WhereClause("c.id = ?")).bind(id.toString());
where.And(clusterClause);
}
where.And(WhereClause("a.id = ?")).bind(getId().toString());
oss << " " << where.get();
if (!clusterIds.empty())
oss << " GROUP BY t.id HAVING COUNT(DISTINCT c.id) = " << clusterIds.size();
oss << " ORDER BY t.date DESC, r.name COLLATE NOCASE";
auto query {session()->query<ReleaseId>(oss.str())};
for (const std::string& bindArg : where.getBindArgs())
query.bind(bindArg);
return execQuery(query, range);
}
std::size_t
Artist::getReleaseCount() const
{
assert(session());
int res = session()->query<int>("SELECT COUNT(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 = ?").bind(getId());
return res;
}
std::vector<Track::pointer>
Artist::getTracks(std::optional<TrackArtistLinkType> linkType) const
{
assert(session());
auto query {session()->query<Wt::Dbo::ptr<Track>>("SELECT DISTINCT t FROM track t 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 = ?").bind(getId())
.orderBy("t.date DESC,t.release_id,t.disc_number,t.track_number")};
if (linkType)
query.where("t_a_l.type = ?").bind(*linkType);
auto tracks {query.resultList()};
return std::vector<Track::pointer>(tracks.begin(), tracks.end());
}
RangeResults<Track::pointer>
Artist::getNonReleaseTracks(std::optional<TrackArtistLinkType> linkType, Range range) const
{
assert(session());
auto query {session()->query<Wt::Dbo::ptr<Track>>("SELECT t FROM track t 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 = ?").bind(getId())
.where("t.release_id is NULL")
.orderBy("t.name")};
if (linkType)
query.where("t_a_l.type = ?").bind(*linkType);
return execQuery(query, range);
}
bool
Artist::hasNonReleaseTracks(std::optional<TrackArtistLinkType> linkType) const
{
auto query {session()->query<Wt::Dbo::ptr<Track>>("SELECT t FROM track t 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 = ?").bind(getId())
.where("t.release_id is NULL")
.orderBy("t.name")};
if (linkType)
query.where("t_a_l.type = ?").bind(*linkType);
return !query.resultList().empty();
}
std::vector<Track::pointer>
Artist::getRandomTracks(std::optional<std::size_t> count) const
{
assert(session());
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> tracks {session()->query<Wt::Dbo::ptr<Track>>("SELECT t from track t 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 = ?").bind(getId())
.orderBy("RANDOM()")
.limit(count ? static_cast<int>(*count) : -1)};
return std::vector<Track::pointer>(tracks.begin(), tracks.end());
}
RangeResults<ArtistId>
Artist::findSimilarArtists(EnumSet<TrackArtistLinkType> artistLinkTypes, Range range) const
{
@@ -549,6 +549,28 @@ CREATE TABLE "listen" (
ScanSettings::get(session).modify()->addAudioFileExtension(".wv");
}
static
void
migrateFromV33(Session& session)
{
// remove name from track_artist_link
// Drop Auth mode
session.getDboSession().execute(R"(
CREATE TABLE IF NOT EXISTS "track_artist_link_backup" (
"id" integer primary key autoincrement,
"version" integer not null,
"type" integer not null,
"track_id" bigint,
"artist_id" bigint,
constraint "fk_track_artist_link_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred,
constraint "fk_track_artist_link_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred
);
))");
session.getDboSession().execute("INSERT INTO track_artist_link_backup SELECT id, version, type, track_id, artist_id FROM track_artist_link");
session.getDboSession().execute("DROP TABLE track_artist_link");
session.getDboSession().execute("ALTER TABLE track_artist_link_backup RENAME TO track_artist_link");
}
void
doDbMigration(Session& session)
{
@@ -588,6 +610,7 @@ CREATE TABLE "listen" (
{30, migrateFromV30},
{31, migrateFromV31},
{32, migrateFromV32},
{33, migrateFromV33},
};
while (1)
@@ -26,7 +26,7 @@ namespace Database
class Session;
using Version = std::size_t;
static constexpr Version LMS_DATABASE_VERSION {33};
static constexpr Version LMS_DATABASE_VERSION {34};
class VersionInfo
{
public:
+34 -1
View File
@@ -43,7 +43,7 @@ createQuery(Session& session, const Track::FindParameters& params)
{
session.checkSharedLocked();
auto query {session.getDboSession().query<TrackId>("SELECT t.id from track t")};
auto query {session.getDboSession().query<TrackId>("SELECT DISTINCT t.id from track t")};
for (std::string_view keyword : params.keywords)
query.where("t.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + escapeLikeKeyword(keyword) + "%");
@@ -79,6 +79,33 @@ createQuery(Session& session, const Track::FindParameters& params)
query.where(oss.str());
}
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.trackArtistLinkTypes.empty())
{
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());
}
}
if (params.nonRelease)
query.where("t.release_id IS NULL");
switch (params.sortMethod)
{
case TrackSortMethod::None:
@@ -93,6 +120,12 @@ createQuery(Session& session, const Track::FindParameters& params)
assert(params.starringUser.isValid());
query.orderBy("s_t.date_time DESC");
break;
case TrackSortMethod::Name:
query.orderBy("t.name COLLATE NOCASE");
break;
case TrackSortMethod::DateDescAndRelease:
query.orderBy("t.date DESC,t.release_id,t.disc_number,t.track_number");
break;
}
return query;
@@ -89,13 +89,6 @@ class Artist : public Object<Artist, ArtistId>
const std::string& getSortName() const { return _sortName; }
std::optional<UUID> getMBID() const { return UUID::fromString(_MBID); }
RangeResults<ReleaseId> getReleases(Range range, const std::vector<ClusterId>& clusterIds = {}) const; // if non empty, get the releases that match all these clusters
std::size_t getReleaseCount() const;
std::vector<ObjectPtr<Track>> getTracks(std::optional<TrackArtistLinkType> linkType = {}) const;
bool hasNonReleaseTracks(std::optional<TrackArtistLinkType> linkType = std::nullopt) const;
RangeResults<ObjectPtr<Track>> getNonReleaseTracks(std::optional<TrackArtistLinkType> linkType, Range range) const;
std::vector<ObjectPtr<Track>> getRandomTracks(std::optional<std::size_t> count) const;
// No artistLinkTypes means get them all
RangeResults<ArtistId> findSimilarArtists(EnumSet<TrackArtistLinkType> artistLinkTypes = {}, Range range = {}) const;
@@ -63,7 +63,10 @@ class Track : public Object<Track, TrackId>
Range range;
Wt::WDateTime writtenAfter;
UserId starringUser; // only tracks starred by this user
std::optional<Scrobbler> scrobbler; // and for this scrobbler
std::optional<Scrobbler> scrobbler; // and for this scrobbler
ArtistId artist; // only tracks that involve this user
EnumSet<TrackArtistLinkType> trackArtistLinkTypes; // and for these link types
bool nonRelease {}; // only tracks that do not belong to a 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; }
@@ -71,7 +74,10 @@ class Track : public Object<Track, TrackId>
FindParameters& setRange(Range _range) { range = _range; return *this; }
FindParameters& setWrittenAfter(const Wt::WDateTime& _after) { writtenAfter = _after; return *this; }
FindParameters& setStarringUser(UserId _user, Scrobbler _scrobbler) { starringUser = _user; scrobbler = _scrobbler; return *this; }
FindParameters& setArtist(ArtistId _artist, EnumSet<TrackArtistLinkType> _trackArtistLinkTypes = {}) { artist = _artist; trackArtistLinkTypes = _trackArtistLinkTypes; return *this; }
FindParameters& setNonRelease(bool _nonRelease) { nonRelease = _nonRelease; return *this; }
};
struct PathResult
{
TrackId trackId;
@@ -110,6 +110,8 @@ namespace Database
Random,
LastWritten,
StarredDateDesc,
Name,
DateDescAndRelease,
};
enum class TrackArtistLinkType
+33 -29
View File
@@ -80,8 +80,6 @@ TEST_F(DatabaseFixture, Artist_singleTrack)
ASSERT_EQ(artists.size(), 1);
EXPECT_EQ(artists.front()->getId(), artist.getId());
EXPECT_EQ(artist->getReleaseCount(), 0);
ASSERT_EQ(track->getArtistLinks().size(), 1);
auto artistLink {track->getArtistLinks().front()};
EXPECT_EQ(artistLink->getTrack()->getId(), track.getId());
@@ -91,17 +89,6 @@ TEST_F(DatabaseFixture, Artist_singleTrack)
EXPECT_TRUE(track->getArtists({TrackArtistLinkType::ReleaseArtist}).empty());
EXPECT_EQ(track->getArtists({}).size(), 1);
}
{
auto transaction {session.createUniqueTransaction()};
auto tracks {artist->getTracks()};
ASSERT_EQ(tracks.size(), 1);
EXPECT_EQ(tracks.front()->getId(), track.getId());
EXPECT_TRUE(artist->getTracks(TrackArtistLinkType::ReleaseArtist).empty());
EXPECT_EQ(artist->getTracks(TrackArtistLinkType::Artist).size(), 1);
}
}
TEST_F(DatabaseFixture, Artist_singleTracktMultiRoles)
@@ -143,10 +130,18 @@ TEST_F(DatabaseFixture, Artist_singleTracktMultiRoles)
EXPECT_EQ(track->getArtistLinks().size(), 3);
EXPECT_EQ(artist->getTracks().size(), 1);
EXPECT_EQ(artist->getTracks({TrackArtistLinkType::ReleaseArtist}).size(), 1);
EXPECT_EQ(artist->getTracks({TrackArtistLinkType::Artist}).size(), 1);
EXPECT_EQ(artist->getTracks({TrackArtistLinkType::Writer}).size(), 1);
auto tracks {Track::find(session, Track::FindParameters {}.setArtist(artist.getId()))};
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist.getId(), {TrackArtistLinkType::ReleaseArtist}));
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist.getId(), {TrackArtistLinkType::Artist}));
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist.getId(), {TrackArtistLinkType::Writer}));
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist.getId(), {TrackArtistLinkType::Composer}));
EXPECT_EQ(tracks.results.size(), 0);
}
}
@@ -187,13 +182,25 @@ TEST_F(DatabaseFixture, Artist_singleTrackMultiArtists)
{
auto transaction {session.createUniqueTransaction()};
EXPECT_EQ(artist1->getTracks().front(), track.get());
EXPECT_EQ(artist2->getTracks().front(), track.get());
auto tracks {Track::find(session, Track::FindParameters {}.setArtist(artist1->getId()))};
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track->getId());
EXPECT_TRUE(artist1->getTracks(TrackArtistLinkType::ReleaseArtist).empty());
EXPECT_EQ(artist1->getTracks(TrackArtistLinkType::Artist).size(), 1);
EXPECT_TRUE(artist2->getTracks(TrackArtistLinkType::ReleaseArtist).empty());
EXPECT_EQ(artist2->getTracks(TrackArtistLinkType::Artist).size(), 1);
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist2->getId()));
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track->getId());
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist1->getId(), {TrackArtistLinkType::ReleaseArtist}));
EXPECT_EQ(tracks.results.size(), 0);
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist1->getId(), {TrackArtistLinkType::Artist}));
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist2->getId(), {TrackArtistLinkType::ReleaseArtist}));
EXPECT_EQ(tracks.results.size(), 0);
tracks = Track::find(session, Track::FindParameters {}.setArtist(artist2->getId(), {TrackArtistLinkType::Artist}));
EXPECT_EQ(tracks.results.size(), 1);
}
}
@@ -324,9 +331,8 @@ TEST_F(DatabaseFixture, Artist_nonReleaseTracks)
{
auto transaction {session.createSharedTransaction()};
EXPECT_FALSE(artist->hasNonReleaseTracks(std::nullopt));
const auto tracks {artist->getNonReleaseTracks(std::nullopt, Range {})};
auto tracks {Track::find(session, Track::FindParameters {}.setNonRelease(true).setArtist(artist->getId()))};
EXPECT_EQ(tracks.results.size(), 0);
}
@@ -343,11 +349,9 @@ TEST_F(DatabaseFixture, Artist_nonReleaseTracks)
{
auto transaction {session.createSharedTransaction()};
const auto tracks {artist->getNonReleaseTracks(std::nullopt, Range {})};
EXPECT_TRUE(artist->hasNonReleaseTracks(std::nullopt));
EXPECT_FALSE(tracks.moreResults);
const auto tracks {Track::find(session, Track::FindParameters {}.setArtist(artist->getId()).setNonRelease(true))};
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front()->getId(), track2.getId());
EXPECT_EQ(tracks.results.front(), track2.getId());
}
}
+4 -4
View File
@@ -574,11 +574,11 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistSingleCluster)
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
auto releases {artist->getReleases(Range {})};
auto releases {Release::find(session, Release::FindParameters {}.setArtist(artist.getId()))};
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
releases = artist->getReleases(Range {}, {cluster.getId()});
releases = Release::find(session, Release::FindParameters {}.setArtist(artist.getId()).setClusters({cluster.getId()}));
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
}
@@ -605,11 +605,11 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistMultiClusters)
{
auto transaction {session.createSharedTransaction()};
auto releases {artist->getReleases(Range {})};
auto releases {Release::find(session, Release::FindParameters {}.setArtist(artist.getId()))};
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
releases = artist->getReleases(Range {}, {cluster1.getId(), cluster2.getId()});
releases = Release::find(session, Release::FindParameters {}.setArtist(artist.getId()).setClusters({cluster1.getId(), cluster2.getId()}));
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
}
@@ -49,8 +49,7 @@ TEST_F(DatabaseFixture, MultiTracksSingleArtistSingleRelease)
{
auto transaction {session.createSharedTransaction()};
EXPECT_EQ(artist->getReleaseCount(), 1);
auto releases {artist->getReleases(Range {})};
auto releases {Release::find(session, Release::FindParameters {}.setArtist(artist.getId()))};
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
@@ -74,12 +73,10 @@ TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtist)
{
auto transaction {session.createUniqueTransaction()};
auto releases {artist->getReleases(Range {})};
auto releases {Release::find(session, Release::FindParameters {}.setArtist(artist.getId()))};
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
EXPECT_EQ(artist->getReleaseCount(), 1);
auto artists {release->getArtists()};
ASSERT_EQ(artists.size(), 1);
ASSERT_EQ(artists.front()->getId(), artist.getId());