diff --git a/src/libs/database/impl/Artist.cpp b/src/libs/database/impl/Artist.cpp index 1b8a2cca..b602ae0b 100644 --- a/src/libs/database/impl/Artist.cpp +++ b/src/libs/database/impl/Artist.cpp @@ -42,9 +42,9 @@ namespace lms::db { session.checkReadTransaction(); - // TODO remove distinct and use group by auto query{ session.getDboSession()->query("SELECT " + std::string{ itemToSelect } + " FROM artist a") }; - if (params.sortMethod == ArtistSortMethod::LastWritten + if (params.sortMethod == ArtistSortMethod::LastWrittenDesc + || params.sortMethod == ArtistSortMethod::AddedDesc || params.writtenAfter.isValid() || params.linkType || params.track.isValid() @@ -55,7 +55,8 @@ namespace lms::db query.join("track_artist_link t_a_l ON t_a_l.artist_id = a.id"); } - if (params.sortMethod == ArtistSortMethod::LastWritten + if (params.sortMethod == ArtistSortMethod::LastWrittenDesc + || params.sortMethod == ArtistSortMethod::AddedDesc || params.writtenAfter.isValid() || params.release.isValid() || params.mediaLibrary.isValid()) @@ -151,8 +152,11 @@ namespace lms::db case ArtistSortMethod::Random: query.orderBy("RANDOM()"); break; - case ArtistSortMethod::LastWritten: - query.orderBy("t.file_last_write DESC"); + case ArtistSortMethod::LastWrittenDesc: + query.orderBy("MAX(t.file_last_write) DESC"); + break; + case ArtistSortMethod::AddedDesc: + query.orderBy("MAX(t.file_added) DESC"); break; case ArtistSortMethod::StarredDateDesc: assert(params.starringUser.isValid()); diff --git a/src/libs/database/impl/Db.cpp b/src/libs/database/impl/Db.cpp index 91b27346..ff696b7a 100644 --- a/src/libs/database/impl/Db.cpp +++ b/src/libs/database/impl/Db.cpp @@ -48,9 +48,12 @@ namespace lms::db { prepare(); } + ~Connection() override = default; private: Connection& operator=(const Connection&) = delete; + Connection(Connection&&) = delete; + Connection&& operator=(Connection&&) = delete; std::unique_ptr clone() const override { diff --git a/src/libs/database/impl/Release.cpp b/src/libs/database/impl/Release.cpp index 9695c16a..9cfb04e9 100644 --- a/src/libs/database/impl/Release.cpp +++ b/src/libs/database/impl/Release.cpp @@ -50,7 +50,8 @@ namespace lms::db auto query{ session.getDboSession()->query("SELECT " + std::string{ itemToSelect } + " from release r") }; if (params.sortMethod == ReleaseSortMethod::ArtistNameThenName - || params.sortMethod == ReleaseSortMethod::LastWritten + || params.sortMethod == ReleaseSortMethod::LastWrittenDesc + || params.sortMethod == ReleaseSortMethod::AddedDesc || params.sortMethod == ReleaseSortMethod::DateAsc || params.sortMethod == ReleaseSortMethod::DateDesc || params.sortMethod == ReleaseSortMethod::OriginalDate @@ -207,9 +208,12 @@ namespace lms::db case ReleaseSortMethod::Random: query.orderBy("RANDOM()"); break; - case ReleaseSortMethod::LastWritten: + case ReleaseSortMethod::LastWrittenDesc: query.orderBy("t.file_last_write DESC"); break; + case ReleaseSortMethod::AddedDesc: + query.orderBy("t.file_added DESC"); + break; case ReleaseSortMethod::DateAsc: query.orderBy("t.date ASC, r.name COLLATE NOCASE"); break; diff --git a/src/libs/database/impl/Session.cpp b/src/libs/database/impl/Session.cpp index 4f0a4092..25af8d32 100644 --- a/src/libs/database/impl/Session.cpp +++ b/src/libs/database/impl/Session.cpp @@ -240,7 +240,10 @@ namespace lms::db utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_date_idx ON track(date)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_directory_release_idx ON track(directory_id, release_id);"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_directory_file_stem_idx ON track(directory_id, file_stem);"); + utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_file_added_idx ON track(file_added)"); + utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_file_added_desc_idx ON track(file_added DESC)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_file_last_write_idx ON track(file_last_write)"); + utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_file_last_write_desc_idx ON track(file_last_write DESC)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_file_name_idx ON track(file_name COLLATE NOCASE)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_media_library_idx ON track(media_library_id)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_media_library_release_idx ON track(media_library_id, release_id)"); @@ -250,8 +253,12 @@ namespace lms::db utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_original_date_idx ON track(original_date)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_recording_mbid_idx ON track(recording_mbid)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_idx ON track(release_id)"); - utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_file_last_write_idx ON track(release_id, file_last_write)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_date_idx ON track(release_id, date)"); + utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_date_desc_idx ON track(release_id, date DESC)"); + utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_file_last_write_idx ON track(release_id, file_last_write)"); + utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_file_last_write_desc_idx ON track(release_id, file_last_write DESC)"); + utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_file_added_idx ON track(release_id, file_added)"); + utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS track_release_file_added_desc_idx ON track(release_id, file_added DESC)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS tracklist_name_idx ON tracklist(name)"); utils::executeCommand(_session, "CREATE INDEX IF NOT EXISTS tracklist_user_type_idx ON tracklist(user_id, type)"); diff --git a/src/libs/database/impl/Track.cpp b/src/libs/database/impl/Track.cpp index 3dd53fcd..058ff7eb 100644 --- a/src/libs/database/impl/Track.cpp +++ b/src/libs/database/impl/Track.cpp @@ -175,9 +175,12 @@ namespace lms::db case TrackSortMethod::Id: query.orderBy("t.id"); break; - case TrackSortMethod::LastWritten: + case TrackSortMethod::LastWrittenDesc: query.orderBy("t.file_last_write DESC"); break; + case TrackSortMethod::AddedDesc: + query.orderBy("t.file_added DESC"); + break; case TrackSortMethod::Random: query.orderBy("RANDOM()"); break; diff --git a/src/libs/database/include/database/Types.hpp b/src/libs/database/include/database/Types.hpp index 5fb4a3b4..7d8d955b 100644 --- a/src/libs/database/include/database/Types.hpp +++ b/src/libs/database/include/database/Types.hpp @@ -119,7 +119,8 @@ namespace lms::db Name, SortName, Random, - LastWritten, + LastWrittenDesc, + AddedDesc, StarredDateDesc, }; @@ -146,7 +147,8 @@ namespace lms::db OriginalDate, OriginalDateDesc, Random, - LastWritten, + LastWrittenDesc, + AddedDesc, StarredDateDesc, }; @@ -162,7 +164,8 @@ namespace lms::db None, Id, Random, - LastWritten, + LastWrittenDesc, + AddedDesc, StarredDateDesc, FileName, Name, diff --git a/src/libs/database/test/Artist.cpp b/src/libs/database/test/Artist.cpp index 85f6ece5..0ee1c8da 100644 --- a/src/libs/database/test/Artist.cpp +++ b/src/libs/database/test/Artist.cpp @@ -669,7 +669,8 @@ namespace lms::db::tests TEST_F(DatabaseFixture, Artist_findByRelease) { ScopedArtist artist{ session, "artist" }; - ScopedTrack track{ session }; + ScopedTrack track1{ session }; + ScopedTrack track2{ session }; ScopedRelease release{ session, "MyRelease" }; { @@ -680,7 +681,8 @@ namespace lms::db::tests { auto transaction{ session.createWriteTransaction() }; - TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); + TrackArtistLink::create(session, track1.get(), artist.get(), TrackArtistLinkType::Artist); + TrackArtistLink::create(session, track2.get(), artist.get(), TrackArtistLinkType::Artist); } { @@ -691,7 +693,8 @@ namespace lms::db::tests { auto transaction{ session.createWriteTransaction() }; - track.get().modify()->setRelease(release.get()); + track1.get().modify()->setRelease(release.get()); + track2.get().modify()->setRelease(release.get()); } { @@ -700,6 +703,12 @@ namespace lms::db::tests ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); } + + { + auto transaction{ session.createReadTransaction() }; + const std::size_t count{ Release::getCount(session, Release::FindParameters{}.setArtist(artist.getId())) }; + EXPECT_EQ(count, 1); + } } TEST_F(DatabaseFixture, Artist_image) @@ -725,4 +734,84 @@ namespace lms::db::tests EXPECT_EQ(artistImage->getId(), image.getId()); } } + + TEST_F(DatabaseFixture, Artist_sortDateAdded) + { + ScopedArtist artistA{ session, "artistA" }; + ScopedArtist artistB{ session, "artistB" }; + ScopedArtist artistC{ session, "artistC" }; + ScopedArtist artistD{ session, "artistD" }; + + ScopedTrack trackA1{ session }; + ScopedTrack trackB1{ session }; + ScopedTrack trackC1{ session }; + ScopedTrack trackD1{ session }; + + ScopedTrack trackA2{ session }; + + { + auto transaction{ session.createWriteTransaction() }; + trackA1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2 }); + trackB1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 1 }); + trackD1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2, 15, 36, 24 }); + trackD1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 3 }); + trackA2.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 4 }); + + TrackArtistLink::create(session, trackA1.get(), artistA.get(), TrackArtistLinkType::Artist); + TrackArtistLink::create(session, trackA2.get(), artistA.get(), TrackArtistLinkType::Producer); + TrackArtistLink::create(session, trackB1.get(), artistB.get(), TrackArtistLinkType::Artist); + TrackArtistLink::create(session, trackC1.get(), artistC.get(), TrackArtistLinkType::Artist); + TrackArtistLink::create(session, trackD1.get(), artistD.get(), TrackArtistLinkType::Artist); + } + + { + auto transaction{ session.createReadTransaction() }; + const auto artists{ Artist::findIds(session, Artist::FindParameters{}.setSortMethod(ArtistSortMethod::AddedDesc)) }; + ASSERT_EQ(artists.results.size(), 4); + EXPECT_EQ(artists.results[0], artistA.getId()); + EXPECT_EQ(artists.results[1], artistD.getId()); + EXPECT_EQ(artists.results[2], artistB.getId()); + EXPECT_EQ(artists.results[3], artistC.getId()); + } + } + + TEST_F(DatabaseFixture, Artist_sortLastWritten) + { + ScopedArtist artistA{ session, "artistA" }; + ScopedArtist artistB{ session, "artistB" }; + ScopedArtist artistC{ session, "artistC" }; + ScopedArtist artistD{ session, "artistD" }; + + ScopedTrack trackA1{ session }; + ScopedTrack trackB1{ session }; + ScopedTrack trackC1{ session }; + ScopedTrack trackD1{ session }; + + ScopedTrack trackA2{ session }; + + { + auto transaction{ session.createWriteTransaction() }; + trackA1.get().modify()->setLastWriteTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 } }); + trackB1.get().modify()->setLastWriteTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 1 } }); + trackD1.get().modify()->setLastWriteTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 }, Wt::WTime{ 15, 36, 24 } }); + trackD1.get().modify()->setLastWriteTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 3 } }); + trackA2.get().modify()->setLastWriteTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 4 } }); + + TrackArtistLink::create(session, trackA1.get(), artistA.get(), TrackArtistLinkType::Artist); + TrackArtistLink::create(session, trackA2.get(), artistA.get(), TrackArtistLinkType::Producer); + TrackArtistLink::create(session, trackB1.get(), artistB.get(), TrackArtistLinkType::Artist); + TrackArtistLink::create(session, trackC1.get(), artistC.get(), TrackArtistLinkType::Artist); + TrackArtistLink::create(session, trackD1.get(), artistD.get(), TrackArtistLinkType::Artist); + } + + { + auto transaction{ session.createReadTransaction() }; + const auto artists{ Artist::findIds(session, Artist::FindParameters{}.setSortMethod(ArtistSortMethod::LastWrittenDesc)) }; + ASSERT_EQ(artists.results.size(), 4); + EXPECT_EQ(artists.results[0], artistA.getId()); + EXPECT_EQ(artists.results[1], artistD.getId()); + EXPECT_EQ(artists.results[2], artistB.getId()); + EXPECT_EQ(artists.results[3], artistC.getId()); + } + } } // namespace lms::db::tests diff --git a/src/libs/database/test/Release.cpp b/src/libs/database/test/Release.cpp index 39e31611..21416541 100644 --- a/src/libs/database/test/Release.cpp +++ b/src/libs/database/test/Release.cpp @@ -1121,4 +1121,84 @@ namespace lms::db::tests EXPECT_EQ(releaseImage->getId(), image.getId()); } } + + TEST_F(DatabaseFixture, Release_sortDateAdded) + { + ScopedRelease releaseA{ session, "relA" }; + ScopedRelease releaseB{ session, "relB" }; + ScopedRelease releaseC{ session, "relC" }; + ScopedRelease releaseD{ session, "relD" }; + + ScopedTrack trackA1{ session }; + ScopedTrack trackB1{ session }; + ScopedTrack trackC1{ session }; + ScopedTrack trackD1{ session }; + + ScopedTrack trackA2{ session }; + + { + auto transaction{ session.createWriteTransaction() }; + trackA1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2 }); + trackB1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 1 }); + trackD1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2, 15, 36, 24 }); + trackD1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 3 }); + trackA2.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 4 }); + + trackA1.get().modify()->setRelease(releaseA.get()); + trackA2.get().modify()->setRelease(releaseA.get()); + trackB1.get().modify()->setRelease(releaseB.get()); + trackC1.get().modify()->setRelease(releaseC.get()); + trackD1.get().modify()->setRelease(releaseD.get()); + } + + { + auto transaction{ session.createReadTransaction() }; + const auto releases{ Release::findIds(session, Release::FindParameters{}.setSortMethod(ReleaseSortMethod::AddedDesc)) }; + ASSERT_EQ(releases.results.size(), 4); + EXPECT_EQ(releases.results[0], releaseA.getId()); + EXPECT_EQ(releases.results[1], releaseD.getId()); + EXPECT_EQ(releases.results[2], releaseB.getId()); + EXPECT_EQ(releases.results[3], releaseC.getId()); + } + } + + TEST_F(DatabaseFixture, Release_sortLastWritten) + { + ScopedRelease releaseA{ session, "relA" }; + ScopedRelease releaseB{ session, "relB" }; + ScopedRelease releaseC{ session, "relC" }; + ScopedRelease releaseD{ session, "relD" }; + + ScopedTrack trackA1{ session }; + ScopedTrack trackB1{ session }; + ScopedTrack trackC1{ session }; + ScopedTrack trackD1{ session }; + + ScopedTrack trackA2{ session }; + + { + auto transaction{ session.createWriteTransaction() }; + trackA1.get().modify()->setLastWriteTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 } }); + trackB1.get().modify()->setLastWriteTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 1 } }); + trackD1.get().modify()->setLastWriteTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 }, Wt::WTime{ 15, 36, 24 } }); + trackD1.get().modify()->setLastWriteTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 3 } }); + trackA2.get().modify()->setLastWriteTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 4 } }); + + trackA1.get().modify()->setRelease(releaseA.get()); + trackA2.get().modify()->setRelease(releaseA.get()); + trackB1.get().modify()->setRelease(releaseB.get()); + trackC1.get().modify()->setRelease(releaseC.get()); + trackD1.get().modify()->setRelease(releaseD.get()); + } + + { + auto transaction{ session.createReadTransaction() }; + const auto releases{ Release::findIds(session, Release::FindParameters{}.setSortMethod(ReleaseSortMethod::LastWrittenDesc)) }; + ASSERT_EQ(releases.results.size(), 4); + EXPECT_EQ(releases.results[0], releaseA.getId()); + EXPECT_EQ(releases.results[1], releaseD.getId()); + EXPECT_EQ(releases.results[2], releaseB.getId()); + EXPECT_EQ(releases.results[3], releaseC.getId()); + } + } } // namespace lms::db::tests \ No newline at end of file diff --git a/src/libs/database/test/Track.cpp b/src/libs/database/test/Track.cpp index 8d9edc88..f5ab80fc 100644 --- a/src/libs/database/test/Track.cpp +++ b/src/libs/database/test/Track.cpp @@ -377,4 +377,54 @@ namespace lms::db::tests EXPECT_EQ(track->getComment(), "MyComment"); } } + + TEST_F(DatabaseFixture, Track_sortDateAdded) + { + ScopedTrack track1{ session }; + ScopedTrack track2{ session }; + ScopedTrack track3{ session }; + ScopedTrack track4{ session }; + + { + auto transaction{ session.createWriteTransaction() }; + track1.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2 }); + track2.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 1 }); + track4.get().modify()->setAddedTime(core::PartialDateTime{ 2021, 1, 2, 15, 36, 24 }); + } + + { + auto transaction{ session.createReadTransaction() }; + const auto tracks{ Track::findIds(session, Track::FindParameters{}.setSortMethod(TrackSortMethod::AddedDesc)) }; + ASSERT_EQ(tracks.results.size(), 4); + EXPECT_EQ(tracks.results[0], track4.getId()); + EXPECT_EQ(tracks.results[1], track1.getId()); + EXPECT_EQ(tracks.results[2], track2.getId()); + EXPECT_EQ(tracks.results[3], track3.getId()); + } + } + + TEST_F(DatabaseFixture, Track_sortLastWritten) + { + ScopedTrack track1{ session }; + ScopedTrack track2{ session }; + ScopedTrack track3{ session }; + ScopedTrack track4{ session }; + + { + auto transaction{ session.createWriteTransaction() }; + track1.get().modify()->setLastWriteTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 } }); + track2.get().modify()->setLastWriteTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 1 } }); + track4.get().modify()->setLastWriteTime(Wt::WDateTime{ Wt::WDate{ 2021, 1, 2 }, Wt::WTime{ 15, 36, 24 } }); + } + + { + auto transaction{ session.createReadTransaction() }; + const auto tracks{ Track::findIds(session, Track::FindParameters{}.setSortMethod(TrackSortMethod::LastWrittenDesc)) }; + ASSERT_EQ(tracks.results.size(), 4); + EXPECT_EQ(tracks.results[0], track4.getId()); + EXPECT_EQ(tracks.results[1], track1.getId()); + EXPECT_EQ(tracks.results[2], track2.getId()); + EXPECT_EQ(tracks.results[3], track3.getId()); + } + } } // namespace lms::db::tests \ No newline at end of file diff --git a/src/libs/subsonic/impl/endpoints/AlbumSongLists.cpp b/src/libs/subsonic/impl/endpoints/AlbumSongLists.cpp index 49dc8344..8c04c158 100644 --- a/src/libs/subsonic/impl/endpoints/AlbumSongLists.cpp +++ b/src/libs/subsonic/impl/endpoints/AlbumSongLists.cpp @@ -124,7 +124,7 @@ namespace lms::api::subsonic else if (type == "newest") { Release::FindParameters params; - params.setSortMethod(ReleaseSortMethod::LastWritten); + params.setSortMethod(ReleaseSortMethod::AddedDesc); params.setRange(range); params.setMediaLibrary(mediaLibraryId); diff --git a/src/lms/ui/explore/ArtistCollector.cpp b/src/lms/ui/explore/ArtistCollector.cpp index eaf08daa..dd2ff253 100644 --- a/src/lms/ui/explore/ArtistCollector.cpp +++ b/src/lms/ui/explore/ArtistCollector.cpp @@ -99,7 +99,7 @@ namespace lms::ui params.setKeywords(getSearchKeywords()); params.setMediaLibrary(filters.getMediaLibrary()); params.setLinkType(_linkType); - params.setSortMethod(ArtistSortMethod::LastWritten); + params.setSortMethod(ArtistSortMethod::AddedDesc); params.setRange(range); { diff --git a/src/lms/ui/explore/ReleaseCollector.cpp b/src/lms/ui/explore/ReleaseCollector.cpp index 8d60907c..8ff416c5 100644 --- a/src/lms/ui/explore/ReleaseCollector.cpp +++ b/src/lms/ui/explore/ReleaseCollector.cpp @@ -92,7 +92,7 @@ namespace lms::ui params.setClusters(getFilters().getClusters()); params.setMediaLibrary(getFilters().getMediaLibrary()); params.setKeywords(getSearchKeywords()); - params.setSortMethod(ReleaseSortMethod::LastWritten); + params.setSortMethod(ReleaseSortMethod::AddedDesc); params.setRange(range); { diff --git a/src/lms/ui/explore/TrackCollector.cpp b/src/lms/ui/explore/TrackCollector.cpp index 4bb2a338..66b321bd 100644 --- a/src/lms/ui/explore/TrackCollector.cpp +++ b/src/lms/ui/explore/TrackCollector.cpp @@ -93,7 +93,7 @@ namespace lms::ui params.setClusters(getFilters().getClusters()); params.setMediaLibrary(getFilters().getMediaLibrary()); params.setKeywords(getSearchKeywords()); - params.setSortMethod(TrackSortMethod::LastWritten); + params.setSortMethod(TrackSortMethod::AddedDesc); params.setRange(range); {