From eb5f8ea9e337bba37842fbe0f9190f50c992f571 Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 10 Oct 2014 23:11:49 +0200 Subject: [PATCH] [Remote] Added genre filter on artist searches --- src/database/Artist.cpp | 39 ++++++++++ src/database/AudioTypes.hpp | 78 ++++++++++--------- .../server/AudioCollectionRequestHandler.cpp | 7 +- test/RemoteClientServer.cpp | 39 +++++++--- 4 files changed, 116 insertions(+), 47 deletions(-) diff --git a/src/database/Artist.cpp b/src/database/Artist.cpp index ae24109b..c171ae0a 100644 --- a/src/database/Artist.cpp +++ b/src/database/Artist.cpp @@ -17,8 +17,12 @@ * along with LMS. If not, see . */ +#include + #include "AudioTypes.hpp" +#include "SqlQuery.hpp" + namespace Database { @@ -63,5 +67,40 @@ Artist::getAllOrphans(Wt::Dbo::Session& session) return session.query< Wt::Dbo::ptr >("select a from artist a LEFT OUTER JOIN Track t ON a.id = t.artist_id WHERE t.id IS NULL"); } +Wt::Dbo::collection +Artist::getAll(Wt::Dbo::Session& session, + const std::vector& genreIds, + int offset, int size) +{ + std::string sqlQuery = "SELECT a FROM artist a"; + + if (!genreIds.empty()) + { + sqlQuery += " INNER JOIN genre g ON g.id = t_g.genre_id"; + sqlQuery += " INNER JOIN track t ON t.id = t_g.track_id AND t.artist_id = a.id"; + sqlQuery += " INNER JOIN track_genre t_g ON t_g.track_id = t.id AND t_g.genre_id = g.id"; + } + + WhereClause where; + { + WhereClause genreWhere; + + for (std::size_t i = 0; i < genreIds.size(); ++i) + genreWhere.Or( WhereClause("g.id = ?") ); + + where.And(genreWhere); + } + + Wt::Dbo::Query query = session.query( sqlQuery + " " + where.get() ).offset(offset).limit(size); + + BOOST_FOREACH(const Genre::id_type genreId, genreIds) + query.bind(genreId); + + query.groupBy("a"); + + return query; +} + + } // namespace Database diff --git a/src/database/AudioTypes.hpp b/src/database/AudioTypes.hpp index a3c9b37c..89b01fbb 100644 --- a/src/database/AudioTypes.hpp +++ b/src/database/AudioTypes.hpp @@ -36,6 +36,44 @@ class Track; class Release; class Artist; +class Genre +{ + public: + + typedef Wt::Dbo::ptr pointer; + typedef Wt::Dbo::dbo_traits::IdType id_type; + + Genre(); + Genre(const std::string& name); + + // Find utility + static pointer getByName(Wt::Dbo::Session& session, const std::string& name); + static pointer getNone(Wt::Dbo::Session& session); + static Wt::Dbo::collection getAll(Wt::Dbo::Session& session, std::size_t offset = -1, std::size_t size = -1); + + // Create utility + static pointer create(Wt::Dbo::Session& session, const std::string& name); + + // Accessors + const std::string& getName(void) const { return _name; } + bool isNone(void) const; + const Wt::Dbo::collection< Wt::Dbo::ptr >& getTracks() const { return _tracks;} + + template + void persist(Action& a) + { + Wt::Dbo::field(a, _name, "name"); + Wt::Dbo::hasMany(a, _tracks, Wt::Dbo::ManyToMany, "track_genre", "", Wt::Dbo::OnDeleteCascade); + } + + private: + static const std::size_t _maxNameLength = 128; + std::string _name; + + Wt::Dbo::collection< Wt::Dbo::ptr > _tracks; +}; + + class Artist { public: @@ -50,6 +88,9 @@ class Artist static pointer getByName(Wt::Dbo::Session& session, const std::string& name); static pointer getNone(Wt::Dbo::Session& session); static Wt::Dbo::collection getAll(Wt::Dbo::Session& session, int offset = -1, int size = -1); + static Wt::Dbo::collection getAll(Wt::Dbo::Session& session, + const std::vector& genreIds, + int offset = -1, int size = -1); static Wt::Dbo::collection getAllOrphans(Wt::Dbo::Session& session); const std::string& getName(void) const { return _name; } @@ -122,43 +163,6 @@ class Release }; -class Genre -{ - public: - - typedef Wt::Dbo::ptr pointer; - typedef Wt::Dbo::dbo_traits::IdType id_type; - - Genre(); - Genre(const std::string& name); - - // Find utility - static pointer getByName(Wt::Dbo::Session& session, const std::string& name); - static pointer getNone(Wt::Dbo::Session& session); - static Wt::Dbo::collection getAll(Wt::Dbo::Session& session, std::size_t offset = -1, std::size_t size = -1); - - // Create utility - static pointer create(Wt::Dbo::Session& session, const std::string& name); - - // Accessors - const std::string& getName(void) const { return _name; } - bool isNone(void) const; - const Wt::Dbo::collection< Wt::Dbo::ptr >& getTracks() const { return _tracks;} - - template - void persist(Action& a) - { - Wt::Dbo::field(a, _name, "name"); - Wt::Dbo::hasMany(a, _tracks, Wt::Dbo::ManyToMany, "track_genre", "", Wt::Dbo::OnDeleteCascade); - } - - private: - static const std::size_t _maxNameLength = 128; - std::string _name; - - Wt::Dbo::collection< Wt::Dbo::ptr > _tracks; -}; - class Track { public: diff --git a/src/remote/server/AudioCollectionRequestHandler.cpp b/src/remote/server/AudioCollectionRequestHandler.cpp index e882d78e..f986e6c0 100644 --- a/src/remote/server/AudioCollectionRequestHandler.cpp +++ b/src/remote/server/AudioCollectionRequestHandler.cpp @@ -164,11 +164,16 @@ AudioCollectionRequestHandler::processGetArtists(const AudioCollectionRequest::G size = _maxListArtists; size = std::min(size, _maxListArtists); + // Get filters + std::vector genreIds; + for (int id = 0; id < request.genre_id_size(); ++id) + genreIds.push_back( request.genre_id(id) ); + // Now fetch requested data... Wt::Dbo::Transaction transaction( _db.getSession() ); - Wt::Dbo::collection artists = Database::Artist::getAll( _db.getSession(), request.batch_parameter().offset(), static_cast(size) ); + Wt::Dbo::collection artists = Database::Artist::getAll( _db.getSession(), genreIds, request.batch_parameter().offset(), static_cast(size) ); typedef Wt::Dbo::collection< Database::Artist::pointer > Artists; diff --git a/test/RemoteClientServer.cpp b/test/RemoteClientServer.cpp index afd4628f..58ec4a75 100644 --- a/test/RemoteClientServer.cpp +++ b/test/RemoteClientServer.cpp @@ -117,7 +117,7 @@ class TestClient _socket.handshake(boost::asio::ssl::stream_base::client); } - void getArtists(std::vector& artists) + void getArtists(std::vector& artists, const std::vector& genreIds) { const std::size_t requestedBatchSize = 128; @@ -125,12 +125,12 @@ class TestClient std::size_t res = 0; - while ((res = getArtists(artists, offset, requestedBatchSize) ) > 0) + while ((res = getArtists(artists, genreIds, offset, requestedBatchSize) ) > 0) offset += res; } - std::size_t getArtists(std::vector& artists, std::size_t offset, std::size_t size) + std::size_t getArtists(std::vector& artists, const std::vector& genreIds, std::size_t offset, std::size_t size) { std::size_t nbArtists = 0; @@ -142,6 +142,8 @@ class TestClient request.mutable_audio_collection_request()->set_type( Remote::AudioCollectionRequest_Type_TypeGetArtistList); request.mutable_audio_collection_request()->mutable_get_artists()->mutable_batch_parameter()->set_size(size); request.mutable_audio_collection_request()->mutable_get_artists()->mutable_batch_parameter()->set_offset(offset); + BOOST_FOREACH(uint64_t genreId, genreIds) + request.mutable_audio_collection_request()->mutable_get_artists()->add_genre_id(genreId); sendMsg(request); @@ -719,7 +721,7 @@ class TestClient int main() { try { - bool extendedTests = false; + bool extendedTests = true; bool writeCovers = false; std::cout << "Running test... extendedTests = " << std::boolalpha << extendedTests << std::endl; @@ -733,18 +735,21 @@ int main() throw std::runtime_error("login failed!"); // **** REVISION *** + std::cout << "Getting revision..." << std::endl; std::string rev = client.getRevision(); std::cout << "Revision '" << rev << "'" << std::endl; // ****** Artists ********* + std::cout << "Getting artists..." << std::endl; std::vector artists; - client.getArtists(artists); + client.getArtists(artists, std::vector()); std::cout << "Got " << artists.size() << " artists!" << std::endl; BOOST_FOREACH(const ArtistInfo& artist, artists) std::cout << "Artist: '" << artist << "'" << std::endl; // ***** Genres ********* + std::cout << "Getting genres..." << std::endl; std::vector genres; client.getGenres(genres); @@ -753,31 +758,47 @@ int main() std::cout << "Genre: '" << genre << "'" << std::endl; // **** Releases ****** + std::cout << "Getting releases..." << std::endl; std::vector releases; client.getReleases(releases, std::vector()); BOOST_FOREACH(const ReleaseInfo& release, releases) std::cout << "Release: '" << release << "'" << std::endl; // **** Tracks ****** + std::cout << "Getting tracks..." << std::endl; std::vector tracks; client.getTracks(tracks, std::vector(), std::vector(), std::vector()); BOOST_FOREACH(const TrackInfo& track, tracks) std::cout << "Track: '" << track << "'" << std::endl; // Caution: long test! -/* if (extendedTests) + if (extendedTests) { + + std::cout << "Getting artist for each genre..." << std::endl; + // Get the artists for each genre + BOOST_FOREACH(const GenreInfo& genre, genres) + { + std::cout << "Getting artists genre '" << genre.name << "'" << std::endl; + std::vector artists; + client.getArtists(artists, std::vector(1, genre.id)); + + BOOST_FOREACH(const ArtistInfo& artist, artists) + std::cout << "-> Artist: " << artist << std::endl; + } + + std::cout << "Getting tracks for each artist..." << std::endl; + // Get the tracks for each artist BOOST_FOREACH(const ArtistInfo& artist, artists) { - // Get the tracks for each artist std::vector tracks; client.getTracks(tracks, std::vector(1, artist.id), std::vector(), std::vector()); std::cout << "Artist '" << artist.name << "', nb tracks = " << tracks.size() << std::endl; BOOST_FOREACH(const TrackInfo& track, tracks) - std::cout << "Track: '" << track << "'" << std::endl; + std::cout << "Artist '" << artist.name << "', track: '" << track << "'" << std::endl; } - }*/ + } // ***** Covers ******* if (extendedTests)