From 30c5858fa24504c44a2d54f4b1f35e6168ef1c4e Mon Sep 17 00:00:00 2001 From: emeric Date: Fri, 19 Dec 2014 22:56:03 +0100 Subject: [PATCH] [DB] Corrected searching by genre and added searching by multi keywords --- TODO | 1 - src/Makefile.am | 2 - src/cover/CoverArtGrabber.cpp | 2 +- src/database/AudioTypes.hpp | 54 ++- src/database/DatabaseHandler.cpp | 1 + src/database/Genre.cpp | 68 --- src/database/SqlQuery.cpp | 2 +- src/database/Track.cpp | 388 +++++++++++++----- .../server/AudioCollectionRequestHandler.cpp | 44 +- src/ui/audio/Audio.cpp | 8 +- src/ui/audio/Filter.hpp | 11 +- src/ui/audio/FilterChain.cpp | 6 +- src/ui/audio/KeywordSearchFilter.cpp | 25 +- src/ui/audio/KeywordSearchFilter.hpp | 4 +- src/ui/audio/TableFilter.cpp | 82 ++-- src/ui/audio/TableFilter.hpp | 11 +- src/ui/audio/TableFilterGenre.cpp | 133 ------ src/ui/audio/TableFilterGenre.hpp | 65 --- src/ui/audio/TrackView.cpp | 63 +-- src/ui/audio/TrackView.hpp | 6 +- 20 files changed, 446 insertions(+), 530 deletions(-) delete mode 100644 src/database/Genre.cpp delete mode 100644 src/ui/audio/TableFilterGenre.cpp delete mode 100644 src/ui/audio/TableFilterGenre.hpp diff --git a/TODO b/TODO index 45d48e95..e956e043 100644 --- a/TODO +++ b/TODO @@ -39,7 +39,6 @@ [Audio] - Style eveything nicely... - - Search patterns in the genre list - ReleaseView/ArtistView/GenreView: -> Track count for this special category. Easier: add a special entry '' that contains everything? - MediaPlayer: move slider using js (http://redmine.webtoolkit.eu/boards/2/topics/7924?r=8478) - TrackView : handle duration > 1 hour diff --git a/src/Makefile.am b/src/Makefile.am index 6912e71a..b8480855 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,7 +12,6 @@ lms_SOURCES = \ $(srcdir)/cover/CoverArt.cpp \ $(srcdir)/cover/CoverArtGrabber.cpp \ $(srcdir)/database/DatabaseHandler.cpp \ - $(srcdir)/database/Genre.cpp \ $(srcdir)/database/MediaDirectory.cpp \ $(srcdir)/database/SqlQuery.cpp \ $(srcdir)/database/Track.cpp \ @@ -46,7 +45,6 @@ lms_SOURCES = \ $(srcdir)/ui/audio/KeywordSearchFilter.cpp \ $(srcdir)/ui/audio/PlayQueue.cpp \ $(srcdir)/ui/audio/TableFilter.cpp \ - $(srcdir)/ui/audio/TableFilterGenre.cpp \ $(srcdir)/ui/audio/TrackView.cpp \ $(srcdir)/ui/common/DirectoryValidator.cpp \ $(srcdir)/ui/common/SessionData.cpp \ diff --git a/src/cover/CoverArtGrabber.cpp b/src/cover/CoverArtGrabber.cpp index dccfaf77..8ca350ff 100644 --- a/src/cover/CoverArtGrabber.cpp +++ b/src/cover/CoverArtGrabber.cpp @@ -79,7 +79,7 @@ Grabber::getFromTrack(Database::Track::pointer track) { std::vector res; - if (!track) + if (!track || !track->hasCover()) return std::vector(); try diff --git a/src/database/AudioTypes.hpp b/src/database/AudioTypes.hpp index 08ccc1d5..eeacb22c 100644 --- a/src/database/AudioTypes.hpp +++ b/src/database/AudioTypes.hpp @@ -32,6 +32,25 @@ namespace Database { +// Find utilities +struct SearchFilter +{ + enum class Field { + Artist, // artist name + Release, // release name + Genre, // genre name + Track, // track name + }; + + typedef std::map > FieldValues; + + // Formulas : + // ((like1-1 LIKE ? OR like1-2 LIKE = ? ...) AND (like2-1 LIKE ? OR like2-2 LIKE ? ...)) AND exact1 = ? AND exact2 = ? ... + + std::vector likeMatches; + FieldValues exactMatch; +}; + class Track; class Genre @@ -47,7 +66,9 @@ class Genre // 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); + static Wt::Dbo::collection getAll(Wt::Dbo::Session& session, int offset = -1, int size = -1); + static Wt::Dbo::Query > getAllQuery(Wt::Dbo::Session& session, SearchFilter& filter); + static void updateGenreQueryModel(Wt::Dbo::Session& session, Wt::Dbo::QueryModel >& model, SearchFilter filter, const std::vector& columnNames = std::vector()); // Create utility static pointer create(Wt::Dbo::Session& session, const std::string& name); @@ -81,25 +102,21 @@ class Track Track() {} Track(const boost::filesystem::path& p); - // Find utilities + + // Find utility functions static pointer getByPath(Wt::Dbo::Session& session, const boost::filesystem::path& p); static pointer getById(Wt::Dbo::Session& session, id_type id); static Wt::Dbo::collection< pointer > getAll(Wt::Dbo::Session& session); - static Wt::Dbo::collection< pointer > getAll(Wt::Dbo::Session& session, - const std::vector& artists, // OR filter if many - const std::vector& releases, // OR filter if many - const std::vector& genres, // OR filter if many - int offset = -1, int size = -1); - - static std::vector getReleases(Wt::Dbo::Session& session, - const std::vector& artists, // OR filter if many - const std::vector& genres, // OR filter if many - int offset = -1, int size = -1); - - static std::vector getArtists(Wt::Dbo::Session& session, - const std::vector& genres, // OR filter - int offset = -1, int size = -1); + // Used for remote + static Wt::Dbo::collection< pointer > getAll(Wt::Dbo::Session& session, SearchFilter filter, int offset = -1, int size = -1); + static std::vector getReleases(Wt::Dbo::Session& session, SearchFilter filter, int offset = -1, int size = -1); + static std::vector getArtists(Wt::Dbo::Session& session, SearchFilter filter, int offset = -1, int size = -1); + // Utility fonctions + // MVC models for the user interface + static void updateTracksQueryModel(Wt::Dbo::Session& session, Wt::Dbo::QueryModel< pointer >& model, SearchFilter filter, const std::vector& columnNames = std::vector()); + static void updateReleaseQueryModel(Wt::Dbo::Session& session, Wt::Dbo::QueryModel >& model, SearchFilter filter, const std::vector& columnNames = std::vector()); + static void updateArtistQueryModel(Wt::Dbo::Session& session, Wt::Dbo::QueryModel >& model, SearchFilter filter, const std::vector& columnNames = std::vector()); // Create utility static pointer create(Wt::Dbo::Session& session, const boost::filesystem::path& p); @@ -156,6 +173,10 @@ class Track private: + static Wt::Dbo::Query< pointer > getAllQuery(Wt::Dbo::Session& session, SearchFilter filter); + static Wt::Dbo::Query > getReleasesQuery(Wt::Dbo::Session& session, SearchFilter filter); + static Wt::Dbo::Query > getArtistsQuery(Wt::Dbo::Session& session, SearchFilter filter); + static const std::size_t _maxNameLength = 128; int _trackNumber; @@ -175,6 +196,7 @@ class Track Wt::Dbo::collection< Genre::pointer > _genres; // Tracks that belong to this genre }; + } // namespace database #endif diff --git a/src/database/DatabaseHandler.cpp b/src/database/DatabaseHandler.cpp index c0f7ddeb..23dd7431 100644 --- a/src/database/DatabaseHandler.cpp +++ b/src/database/DatabaseHandler.cpp @@ -98,6 +98,7 @@ _dbBackend( db.string() ) _session.createTables(); _dbBackend.executeSql("CREATE INDEX artist_name_idx ON track(artist_name)"); _dbBackend.executeSql("CREATE INDEX release_name_idx ON track(release_name)"); + _dbBackend.executeSql("CREATE INDEX genre_name_idx ON genre(name)"); } catch(std::exception& e) { LMS_LOG(MOD_DB, SEV_ERROR) << "Cannot create tables: " << e.what(); diff --git a/src/database/Genre.cpp b/src/database/Genre.cpp deleted file mode 100644 index c0d1f5e7..00000000 --- a/src/database/Genre.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2013 Emeric Poupon - * - * This file is part of LMS. - * - * LMS is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LMS is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with LMS. If not, see . - */ - -#include "AudioTypes.hpp" - -namespace Database { - -Genre::Genre() -{ -} - -Genre::Genre(const std::string& name) -: _name( std::string(name, 0, _maxNameLength) ) -{ -} - - -Genre::pointer -Genre::getByName(Wt::Dbo::Session& session, const std::string& name) -{ - // TODO use like search - return session.find().where("name = ?").bind( std::string(name, 0, _maxNameLength) ); -} - -Genre::pointer -Genre::getNone(Wt::Dbo::Session& session) -{ - pointer res = getByName(session, ""); - if (!res) - res = create(session, ""); - return res; -} - -bool -Genre::isNone(void) const -{ - return (_name == ""); -} - -Genre::pointer -Genre::create(Wt::Dbo::Session& session, const std::string& name) -{ - return session.add(new Genre(name)); -} - -Wt::Dbo::collection -Genre::getAll(Wt::Dbo::Session& session, std::size_t offset, std::size_t size) -{ - return session.find().offset(offset).limit(size); -} - -} // namespace Database diff --git a/src/database/SqlQuery.cpp b/src/database/SqlQuery.cpp index 5dc6a12c..611e1c9e 100644 --- a/src/database/SqlQuery.cpp +++ b/src/database/SqlQuery.cpp @@ -89,7 +89,7 @@ InnerJoinClause::And(const InnerJoinClause& clause) if (!_clause.empty()) _clause += " "; - _clause += clause._clause; + _clause += "INNER JOIN " + clause._clause; return *this; } diff --git a/src/database/Track.cpp b/src/database/Track.cpp index f2df60e2..3312d5c4 100644 --- a/src/database/Track.cpp +++ b/src/database/Track.cpp @@ -19,12 +19,114 @@ #include +#include + +#include "logger/Logger.hpp" + #include "SqlQuery.hpp" #include "AudioTypes.hpp" namespace Database { +static SqlQuery generatePartialQuery(SearchFilter& filter, bool forceGenreInnerJoin = false) +{ + bool genreJoin = forceGenreInnerJoin; + SqlQuery sqlQuery; + + + // Process like searches + BOOST_FOREACH(SearchFilter::FieldValues& likeMatch, filter.likeMatches) + { + WhereClause likeWhereClause; + + // Artist + { + WhereClause whereClause; + + BOOST_FOREACH(const std::string& name, likeMatch[SearchFilter::Field::Artist]) + whereClause.Or( WhereClause("t.artist_name LIKE ?") ).bind("%%" + name + "%%"); + + likeWhereClause.Or( whereClause ); + } + // Release + { + WhereClause whereClause; + + BOOST_FOREACH(const std::string& name, likeMatch[SearchFilter::Field::Release]) + whereClause.Or( WhereClause("t.release_name LIKE ?") ).bind("%%" + name + "%%"); + + likeWhereClause.Or( whereClause ); + } + // Genre + { + WhereClause whereClause; + + BOOST_FOREACH(const std::string& name, likeMatch[SearchFilter::Field::Genre]) + { + whereClause.Or( WhereClause("g.name LIKE ?") ).bind("%%" + name + "%%"); + genreJoin = true; + } + + likeWhereClause.Or( whereClause ); + } + // Track + { + WhereClause whereClause; + + BOOST_FOREACH(const std::string& name, likeMatch[SearchFilter::Field::Track]) + whereClause.Or( WhereClause("t.name LIKE ?") ).bind("%%" + name + "%%"); + + likeWhereClause.Or( whereClause ); + } + + sqlQuery.where().And( likeWhereClause ); + } + + // Add exact search constraints + // Artist + { + WhereClause whereClause; + + BOOST_FOREACH(const std::string& name, filter.exactMatch[SearchFilter::Field::Artist]) + whereClause.Or( WhereClause("t.artist_name = ?") ).bind(name); + + sqlQuery.where().And( whereClause ); + } + + // Release + { + WhereClause whereClause; + + BOOST_FOREACH(const std::string& name, filter.exactMatch[SearchFilter::Field::Release]) + whereClause.Or( WhereClause("t.release_name = ?") ).bind(name); + + sqlQuery.where().And( whereClause ); + } + + // Genre + { + WhereClause whereClause; + + BOOST_FOREACH(const std::string& name, filter.exactMatch[SearchFilter::Field::Genre]) + { + whereClause.Or( WhereClause("g.name = ?") ).bind(name); + genreJoin = true; + } + + sqlQuery.where().And( whereClause ); + } + + if (genreJoin) + { + sqlQuery.innerJoin().And( InnerJoinClause("genre g ON g.id = t_g.genre_id")); + sqlQuery.innerJoin().And( InnerJoinClause("track_genre t_g ON t_g.track_id = t.id")); + } + + return sqlQuery; +} + + Track::Track(const boost::filesystem::path& p) : _trackNumber(0), @@ -34,6 +136,11 @@ _hasCover(false) { } +Wt::Dbo::collection< Track::pointer > +Track::getAll(Wt::Dbo::Session& session) +{ + return session.find(); +} void Track::setGenres(std::vector genres) @@ -65,12 +172,6 @@ Track::create(Wt::Dbo::Session& session, const boost::filesystem::path& p) return session.add(new Track(p) ); } -Wt::Dbo::collection< Track::pointer > -Track::getAll(Wt::Dbo::Session& session) -{ - return session.find(); -} - std::vector< Genre::pointer > Track::getGenres(void) const { @@ -79,89 +180,116 @@ Track::getGenres(void) const return genres; } -Wt::Dbo::collection< Track::pointer > -Track::getAll(Wt::Dbo::Session& session, - const std::vector& artists, - const std::vector& releases, - const std::vector& genres, - int offset, int size) +Wt::Dbo::Query< Track::pointer > +Track::getAllQuery(Wt::Dbo::Session& session, SearchFilter filter) { - std::string sqlQuery = "SELECT t FROM track t"; + SqlQuery sqlQuery = generatePartialQuery(filter); - if (!genres.empty()) - { - sqlQuery += " INNER JOIN genre g ON g.id = t_g.genre_id"; - sqlQuery += " INNER JOIN track_genre t_g ON t_g.track_id = t.id AND t_g.genre_id = g.id"; - } + Wt::Dbo::Query query + = session.query( "SELECT t FROM track t " + sqlQuery.innerJoin().get() + " " + sqlQuery.where().get()).groupBy("t.id"); - WhereClause where; - - { - WhereClause artistWhere; - - for (std::size_t i = 0; i < artists.size(); ++i) - artistWhere.Or( WhereClause("t.artist_name = ?") ); - - where.And(artistWhere); - } - - { - WhereClause releaseWhere; - - for (std::size_t i = 0; i < releases.size(); ++i) - releaseWhere.Or( WhereClause("t.release_name = ?") ); - - where.And(releaseWhere); - } - - { - WhereClause genreWhere; - - for (std::size_t i = 0; i < genres.size(); ++i) - genreWhere.Or( WhereClause("g.name = ?") ); - - where.And(genreWhere); - } - - - Wt::Dbo::Query query = session.query( sqlQuery + " " + where.get() ).offset(offset).limit(size); - - BOOST_FOREACH(const std::string& artist, artists) - query.bind(artist); - - BOOST_FOREACH(const std::string& release, releases) - query.bind(release); - - BOOST_FOREACH(const std::string& genre, genres) - query.bind(genre); - - query.groupBy("t"); + BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) + query.bind(bindArg); return query; } -std::vector -Track::getArtists(Wt::Dbo::Session& session, - const std::vector& genres, - int offset, int size) +Wt::Dbo::collection< Track::pointer > +Track::getAll(Wt::Dbo::Session& session, SearchFilter filter, int offset, int size) { - std::string sqlQuery = "SELECT t.artist_name FROM track t"; + return getAllQuery(session, filter).limit(size).offset(offset); +} - if (!genres.empty()) +Wt::Dbo::Query< boost::tuple > +Track::getReleasesQuery(Wt::Dbo::Session& session, SearchFilter filter) +{ + SqlQuery sqlQuery = generatePartialQuery(filter); + + Wt::Dbo::Query > query + = session.query >("SELECT t.release_name, COUNT(DISTINCT t.id) FROM track t " + sqlQuery.innerJoin().get() + " " + sqlQuery.where().get()).groupBy("t.release_name").orderBy("t.release_name"); + + BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) + query.bind(bindArg); + + return query; +} + +Wt::Dbo::Query< boost::tuple > +Track::getArtistsQuery(Wt::Dbo::Session& session, SearchFilter filter) +{ + SqlQuery sqlQuery = generatePartialQuery(filter); + + Wt::Dbo::Query > query + = session.query >( "SELECT t.artist_name, COUNT(DISTINCT t.id) FROM track t " + sqlQuery.innerJoin().get() + " " + sqlQuery.where().get()).groupBy("t.artist_name").orderBy("t.artist_name"); + + BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) + query.bind(bindArg); + + return query; +} + +void +Track::updateTracksQueryModel(Wt::Dbo::Session& session, Wt::Dbo::QueryModel< pointer >& model, SearchFilter filter, const std::vector& columnNames) +{ + Wt::Dbo::Query< pointer > query = getAllQuery(session, filter).orderBy("t.artist_name,t.date,t.release_name,t.disc_number,t.track_number"); + model.setQuery(query, columnNames.empty() ? true : false); + + // TODO do something better + if (columnNames.size() == 9) { - sqlQuery += " INNER JOIN genre g ON g.id = t_g.genre_id"; - sqlQuery += " INNER JOIN track_genre t_g ON t_g.track_id = t.id AND t_g.genre_id = g.id"; + model.addColumn( "t.artist_name", columnNames[0] ); + model.addColumn( "t.release_name", columnNames[1] ); + model.addColumn( "t.disc_number", columnNames[2] ); + model.addColumn( "t.track_number", columnNames[3] ); + model.addColumn( "t.name", columnNames[4] ); + model.addColumn( "t.duration", columnNames[5] ); + model.addColumn( "t.date", columnNames[6] ); + model.addColumn( "t.original_date", columnNames[7] ); + model.addColumn( "t.genre_list", columnNames[8] ); } - WhereClause whereClause; +} - for (std::size_t i = 0; i < genres.size(); ++i) - whereClause.Or( WhereClause("g.name = ?") ); +void +Track::updateReleaseQueryModel(Wt::Dbo::Session& session, Wt::Dbo::QueryModel >& model, SearchFilter filter, const std::vector& columnNames) +{ + Wt::Dbo::Query< boost::tuple > query = getReleasesQuery(session, filter); - Wt::Dbo::Query query = session.query( sqlQuery + " " + whereClause.get() ).offset(offset).limit(size).groupBy("t.artist_name"); + model.setQuery(query, columnNames.empty() ? true : false); - BOOST_FOREACH(const std::string& genre, genres) - query.bind(genre); + // TODO do something better + if (columnNames.size() == 2) + { + model.addColumn( "t.release_name", columnNames[0]); + model.addColumn( "COUNT(DISTINCT t.id)" ); + } +} + +void +Track::updateArtistQueryModel(Wt::Dbo::Session& session, Wt::Dbo::QueryModel >& model, SearchFilter filter, const std::vector& columnNames) +{ + Wt::Dbo::Query< boost::tuple > query = getArtistsQuery(session, filter); + + model.setQuery(query, columnNames.empty() ? true : false); + + // TODO do something better + if (columnNames.size() == 2) + { + model.addColumn( "t.artist_name", columnNames[0]); + model.addColumn( "COUNT(DISTINCT t.id)" ); + } +} + +std::vector +Track::getArtists(Wt::Dbo::Session& session, SearchFilter filter, int offset, int size) +{ + SqlQuery sqlQuery = generatePartialQuery(filter); + + Wt::Dbo::Query query + = session.query( "SELECT DISTINCT t.artist_name FROM track t " + sqlQuery.innerJoin().get() + " " + sqlQuery.where().get()).offset(offset).limit(size).orderBy("t.artist_name"); + + BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) + query.bind(bindArg); typedef Wt::Dbo::collection< std::string > ArtistNames; ArtistNames artistNames(query); @@ -171,46 +299,17 @@ Track::getArtists(Wt::Dbo::Session& session, res.push_back((*it)); return res; - } std::vector -Track::getReleases(Wt::Dbo::Session& session, - const std::vector& artists, - const std::vector& genres, - int offset, int size) +Track::getReleases(Wt::Dbo::Session& session, SearchFilter filter, int offset, int size) { + SqlQuery sqlQuery = generatePartialQuery(filter); - std::string sqlQuery = "SELECT t.release_name FROM track t"; + Wt::Dbo::Query query + = session.query( "SELECT DISTINCT t.release_name FROM track t " + sqlQuery.innerJoin().get() + " " + sqlQuery.where().get()).offset(offset).limit(size).orderBy("t.release_name"); - if (!genres.empty()) - { - sqlQuery += " INNER JOIN genre g ON g.id = t_g.genre_id"; - sqlQuery += " INNER JOIN track_genre t_g ON t_g.track_id = t.id AND t_g.genre_id = g.id"; - } - - WhereClause whereClause; - - { - WhereClause clause; - - BOOST_FOREACH(const std::string& artist, artists) - clause.Or( WhereClause("t.artist_name = ?") ).bind(artist); - - whereClause.And(clause); - } - { - WhereClause clause; - - BOOST_FOREACH(const std::string& genre, genres) - clause.Or( WhereClause("g.name = ?") ).bind(genre); - - whereClause.And(clause); - } - - Wt::Dbo::Query query = session.query( sqlQuery + " " + whereClause.get() ).offset(offset).limit(size).groupBy("t.release_name"); - - BOOST_FOREACH(const std::string& bindArg, whereClause.getBindArgs()) + BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) query.bind(bindArg); typedef Wt::Dbo::collection< std::string > ReleaseNames; @@ -223,8 +322,77 @@ Track::getReleases(Wt::Dbo::Session& session, return res; } +Genre::Genre() +{ +} + +Genre::Genre(const std::string& name) +: _name( std::string(name, 0, _maxNameLength) ) +{ +} + + +Genre::pointer +Genre::getByName(Wt::Dbo::Session& session, const std::string& name) +{ + // TODO use like search + return session.find().where("name = ?").bind( std::string(name, 0, _maxNameLength) ); +} + +Genre::pointer +Genre::getNone(Wt::Dbo::Session& session) +{ + pointer res = getByName(session, ""); + if (!res) + res = create(session, ""); + return res; +} + +bool +Genre::isNone(void) const +{ + return (_name == ""); +} + +Genre::pointer +Genre::create(Wt::Dbo::Session& session, const std::string& name) +{ + return session.add(new Genre(name)); +} + +Wt::Dbo::collection +Genre::getAll(Wt::Dbo::Session& session, int offset, int size) +{ + return session.find().offset(offset).limit(size).orderBy("name"); +} + +Wt::Dbo::Query > +Genre::getAllQuery(Wt::Dbo::Session& session, SearchFilter& filter) +{ + SqlQuery sqlQuery = generatePartialQuery(filter, true); + + Wt::Dbo::Query > query + = session.query >( "SELECT g.name, COUNT(DISTINCT t.id) FROM track t " + sqlQuery.innerJoin().get() + " " + sqlQuery.where().get()).groupBy("g.name").orderBy("g.name"); + + BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) + query.bind(bindArg); + + return query; +} + +void +Genre::updateGenreQueryModel(Wt::Dbo::Session& session, Wt::Dbo::QueryModel< boost::tuple >& model, SearchFilter filter, const std::vector& columnNames) +{ + Wt::Dbo::Query< boost::tuple > query = getAllQuery(session, filter); + model.setQuery(query, columnNames.empty() ? true : false); + + // TODO do something better + if (columnNames.size() == 2) + { + model.addColumn( "g.name", columnNames[0] ); + model.addColumn( "COUNT(DISTINCT t.id)", columnNames[1] ); + } +} } // namespace Database - - diff --git a/src/remote/server/AudioCollectionRequestHandler.cpp b/src/remote/server/AudioCollectionRequestHandler.cpp index 17719257..98d9e37f 100644 --- a/src/remote/server/AudioCollectionRequestHandler.cpp +++ b/src/remote/server/AudioCollectionRequestHandler.cpp @@ -34,6 +34,8 @@ namespace Remote { namespace Server { +using namespace Database; + AudioCollectionRequestHandler::AudioCollectionRequestHandler(Database::Handler& db) : _db(db) {} @@ -163,24 +165,23 @@ AudioCollectionRequestHandler::processGetArtists(const AudioCollectionRequest::G size = _maxListArtists; size = std::min(size, _maxListArtists); + SearchFilter filter; + // Get filters std::vector genres; for (int id = 0; id < request.genre_size(); ++id) - genres.push_back( request.genre(id) ); + filter.exactMatch[SearchFilter::Field::Genre].push_back( request.genre(id) ); // Now fetch requested data... Wt::Dbo::Transaction transaction( _db.getSession() ); std::vector artists - = Database::Track::getArtists(_db.getSession(), - genres, + = Database::Track::getArtists(_db.getSession(), filter, request.batch_parameter().offset(), static_cast(size) ); BOOST_FOREACH(const std::string& artist, artists) - { response.add_artists()->set_name( std::string( boost::locale::conv::to_utf(artist, "UTF-8") ) ); - } return true; } @@ -200,20 +201,20 @@ AudioCollectionRequestHandler::processGetReleases(const AudioCollectionRequest:: size = _maxListReleases; size = std::min(size, _maxListReleases); + SearchFilter filter; + std::vector artists; for (int id = 0; id < request.artist_size(); ++id) - artists.push_back( request.artist(id) ); + filter.exactMatch[SearchFilter::Field::Artist].push_back( request.artist(id) ); std::vector genres; for (int id = 0; id < request.genre_size(); ++id) - genres.push_back( request.genre(id) ); + filter.exactMatch[SearchFilter::Field::Genre].push_back( request.genre(id) ); Wt::Dbo::Transaction transaction( _db.getSession() ); std::vector releases - = Database::Track::getReleases( _db.getSession(), - artists, - genres, + = Database::Track::getReleases( _db.getSession(), filter, request.batch_parameter().offset(), static_cast(size)); BOOST_FOREACH(const std::string& release, releases) @@ -240,25 +241,24 @@ AudioCollectionRequestHandler::processGetTracks(const AudioCollectionRequest::Ge size = std::min(size, _maxListTracks); // Get filters + SearchFilter filter; + std::vector artists; for (int id = 0; id < request.artist_size(); ++id) - artists.push_back( request.artist(id) ); + filter.exactMatch[SearchFilter::Field::Artist].push_back( request.artist(id) ); std::vector releases; for (int id = 0; id < request.release_size(); ++id) - releases.push_back( request.release(id) ); + filter.exactMatch[SearchFilter::Field::Release].push_back( request.release(id) ); std::vector genres; for (int id = 0; id < request.genre_size(); ++id) - genres.push_back( request.genre(id) ); + filter.exactMatch[SearchFilter::Field::Genre].push_back( request.genre(id) ); Wt::Dbo::Transaction transaction( _db.getSession() ); Wt::Dbo::collection tracks - = Database::Track::getAll( _db.getSession(), - artists, - releases, - genres, + = Database::Track::getAll( _db.getSession(), filter, request.batch_parameter().offset(), static_cast(size)); typedef Wt::Dbo::collection< Database::Track::pointer > Tracks; @@ -305,11 +305,11 @@ AudioCollectionRequestHandler::processGetCoverArt(const AudioCollectionRequest:: case AudioCollectionRequest::GetCoverArt::TypeGetCoverArtRelease: if (request.has_release()) { - Wt::Dbo::collection tracks = Database::Track::getAll(_db.getSession(), - std::vector(), // artist - std::vector(1, request.release()), // release - std::vector(), // genre - -1, 1); + SearchFilter filter; + filter.exactMatch[SearchFilter::Field::Release].push_back(request.release()); + + Wt::Dbo::collection tracks + = Database::Track::getAll(_db.getSession(), filter, -1, 1 /* limit reuslt size */); Wt::Dbo::collection::iterator it = tracks.begin(); diff --git a/src/ui/audio/Audio.cpp b/src/ui/audio/Audio.cpp index 766f880b..ec8f8412 100644 --- a/src/ui/audio/Audio.cpp +++ b/src/ui/audio/Audio.cpp @@ -29,7 +29,6 @@ #include "logger/Logger.hpp" #include "TableFilter.hpp" -#include "TableFilterGenre.hpp" #include "KeywordSearchFilter.hpp" #include "Audio.hpp" @@ -49,15 +48,15 @@ _playQueue(nullptr) // Filters Wt::WHBoxLayout *filterLayout = new Wt::WHBoxLayout(); - TableFilterGenre *filterGenre = new TableFilterGenre(_db); + TableFilter *filterGenre = new TableFilter(_db, Database::SearchFilter::Field::Genre, { "Genre", "Tracks"} ); filterLayout->addWidget(filterGenre); _filterChain.addFilter(filterGenre); - TableFilter *filterArtist = new TableFilter(_db, "track", "artist_name", "Artist"); + TableFilter *filterArtist = new TableFilter(_db, Database::SearchFilter::Field::Artist, {"Artist", "Tracks"} ); filterLayout->addWidget(filterArtist); _filterChain.addFilter(filterArtist); - TableFilter *filterRelease = new TableFilter(_db, "track", "release_name", "Release"); + TableFilter *filterRelease = new TableFilter(_db, Database::SearchFilter::Field::Release, {"Release", "Tracks"}); filterLayout->addWidget(filterRelease); _filterChain.addFilter(filterRelease); @@ -95,6 +94,7 @@ _playQueue(nullptr) Database::User::pointer user = _db.getCurrentUser(); Wt::WVBoxLayout* playQueueLayout = new Wt::WVBoxLayout(); + playQueueLayout->setContentsMargins(5,5,5,5); _mediaPlayer = new AudioMediaPlayer(); playQueueLayout->addWidget(_mediaPlayer); diff --git a/src/ui/audio/Filter.hpp b/src/ui/audio/Filter.hpp index 162cca41..17afe7d2 100644 --- a/src/ui/audio/Filter.hpp +++ b/src/ui/audio/Filter.hpp @@ -22,7 +22,7 @@ #include -#include "database/SqlQuery.hpp" +#include "database/AudioTypes.hpp" namespace UserInterface { @@ -31,17 +31,20 @@ class Filter public: struct Constraint { - WhereClause where; // WHERE SQL clause + std::vector search; + + typedef std::map > ColumnValues; + ColumnValues columnValues; }; Filter() {} virtual ~Filter() {} // Refresh filter using constraints created by parent filters - virtual void refresh(const Constraint& constraint) = 0; + virtual void refresh(Database::SearchFilter& filter) = 0; // Update constraints for next filters - virtual void getConstraint(Constraint& constraint) = 0; + virtual void getConstraint(Database::SearchFilter& filter) = 0; // Emitted when a constraint has changed Wt::Signal& update() { return _update; }; diff --git a/src/ui/audio/FilterChain.cpp b/src/ui/audio/FilterChain.cpp index 403a6d2e..b3b0ab96 100644 --- a/src/ui/audio/FilterChain.cpp +++ b/src/ui/audio/FilterChain.cpp @@ -50,7 +50,7 @@ FilterChain::updateFilters(std::size_t startIdx) _refreshingFilters = true; - Filter::Constraint currentConstraint; + Database::SearchFilter searchFilter; for (std::size_t idFilter = 0; idFilter < _filters.size(); ++idFilter) { @@ -58,12 +58,12 @@ FilterChain::updateFilters(std::size_t startIdx) // Apply contraints created by previous filters if (idFilter > startIdx) { - filter->refresh(currentConstraint); + filter->refresh(searchFilter); } // Get constraints generated by this filter // (Note: adding accross successive calls) - filter->getConstraint(currentConstraint); + filter->getConstraint(searchFilter); } _refreshingFilters = false; diff --git a/src/ui/audio/KeywordSearchFilter.cpp b/src/ui/audio/KeywordSearchFilter.cpp index 9782a66a..8f896939 100644 --- a/src/ui/audio/KeywordSearchFilter.cpp +++ b/src/ui/audio/KeywordSearchFilter.cpp @@ -17,6 +17,10 @@ * along with LMS. If not, see . */ +#include +#include +#include + #include "KeywordSearchFilter.hpp" namespace UserInterface { @@ -34,15 +38,26 @@ KeywordSearchFilter::setText(const std::string& text) // Get constraints created by this filter void -KeywordSearchFilter::getConstraint(Constraint& constraint) +KeywordSearchFilter::getConstraint(Database::SearchFilter& filter) { // No active search means no constaint! if (!_lastEmittedText.empty()) { - const std::string bindText ("%%" + _lastEmittedText + "%%"); - constraint.where.And( WhereClause("(track.name like ? or track.release_name like ? or track.artist_name like ? or track.genre_list like ?)").bind(bindText).bind(bindText).bind(bindText).bind(bindText)); - } + std::vector values; + boost::algorithm::split(values, _lastEmittedText, boost::is_any_of(" "), boost::token_compress_on); - // else no constraint! + // For each part, do a global search on all searchable fields + BOOST_FOREACH(std::string value, values) + { + Database::SearchFilter::FieldValues likeMatch; + + likeMatch[Database::SearchFilter::Field::Artist].push_back(value); + likeMatch[Database::SearchFilter::Field::Release].push_back(value); + likeMatch[Database::SearchFilter::Field::Genre].push_back(value); + likeMatch[Database::SearchFilter::Field::Track].push_back(value); + + filter.likeMatches.push_back(likeMatch); + } + } } } // namespace UserInterface diff --git a/src/ui/audio/KeywordSearchFilter.hpp b/src/ui/audio/KeywordSearchFilter.hpp index a38f6382..5ff2df87 100644 --- a/src/ui/audio/KeywordSearchFilter.hpp +++ b/src/ui/audio/KeywordSearchFilter.hpp @@ -34,10 +34,10 @@ class KeywordSearchFilter : public Filter void setText(const std::string& text); // Set constraint on this filter - void refresh(const Constraint& constraint) {} + void refresh(Database::SearchFilter& filter) {} // Get constraints created by this filter - void getConstraint(Constraint& constraint); + void getConstraint(Database::SearchFilter& filter); private: diff --git a/src/ui/audio/TableFilter.cpp b/src/ui/audio/TableFilter.cpp index b2c156db..f062ebac 100644 --- a/src/ui/audio/TableFilter.cpp +++ b/src/ui/audio/TableFilter.cpp @@ -19,25 +19,41 @@ #include +#include "database/AudioTypes.hpp" + #include "logger/Logger.hpp" #include "TableFilter.hpp" namespace UserInterface { -TableFilter::TableFilter(Database::Handler& db, std::string table, std::string field, const Wt::WString& displayName, Wt::WContainerWidget* parent) +using namespace Database; + +TableFilter::TableFilter(Database::Handler& db, Database::SearchFilter::Field field, std::vector columnNames, Wt::WContainerWidget* parent) : Wt::WTableView( parent ), Filter(), _db(db), -_table(table), _field(field) { - _queryModel.setQuery( _db.getSession().query< ResultType >("select track." + _field + ", COUNT(DISTINCT track.id) from track GROUP BY track." + _field).orderBy("track." + _field)); - _queryModel.addColumn( "track." + _field, displayName); - _queryModel.addColumn( "COUNT(DISTINCT track.id)", "Tracks"); + SearchFilter filter; + + switch (field) + { + case Database::SearchFilter::Field::Artist: + Track::updateArtistQueryModel(_db.getSession(), _queryModel, filter, columnNames); + break; + case Database::SearchFilter::Field::Release: + Track::updateReleaseQueryModel(_db.getSession(), _queryModel, filter, columnNames); + break; + case Database::SearchFilter::Field::Genre: + Genre::updateGenreQueryModel(_db.getSession(), _queryModel, filter, columnNames); + break; + default: + break; + } this->setSelectionMode(Wt::ExtendedSelection); - this->setSortingEnabled(false); + this->setSortingEnabled(true); this->setAlternatingRowColors(true); this->setModel(&_queryModel); @@ -68,53 +84,37 @@ _field(field) void TableFilter::layoutSizeChanged (int width, int height) { - LMS_LOG(MOD_UI, SEV_DEBUG) << "LAYOUT CHANGED, new width = " << width; - - LMS_LOG(MOD_UI, SEV_DEBUG) << "Before: sizes = " << this->columnWidth(0).toPixels() << ", " << this->columnWidth(1).toPixels(); - std::size_t trackColumnSize = this->columnWidth(1).toPixels(); // Set the remaining size for the name column this->setColumnWidth(0, width - trackColumnSize - (7 * 2) - 2); - - LMS_LOG(MOD_UI, SEV_DEBUG) << "After: sizes = " << this->columnWidth(0).toPixels() << ", " << this->columnWidth(1).toPixels(); } // Set constraints on this filter void -TableFilter::refresh(const Constraint& constraint) +TableFilter::refresh(SearchFilter& filter) { - SqlQuery sqlQuery; - - sqlQuery.select("track." + _field + ", COUNT(DISTINCT track.id)"); - sqlQuery.from().And( FromClause("track")) ; - sqlQuery.where().And(constraint.where); // Add constraint made by other filters - sqlQuery.groupBy().And( "track." + _field); // Add constraint made by other filters - - LMS_LOG(MOD_UI, SEV_DEBUG) << _table << ", generated query = '" << sqlQuery.get() << "'"; - - Wt::Dbo::Query query = _db.getSession().query( sqlQuery.get() ); - - query.orderBy(_table + "." + _field); - - BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) { - LMS_LOG(MOD_UI, SEV_DEBUG) << "Binding value '" << bindArg << "'"; - query.bind(bindArg); + switch (_field) + { + case Database::SearchFilter::Field::Artist: + Track::updateArtistQueryModel(_db.getSession(), _queryModel, filter); + break; + case Database::SearchFilter::Field::Release: + Track::updateReleaseQueryModel(_db.getSession(), _queryModel, filter); + break; + case Database::SearchFilter::Field::Genre: + Genre::updateGenreQueryModel(_db.getSession(), _queryModel, filter); + break; + default: + break; } - - _queryModel.setQuery( query, true ); - - LMS_LOG(MOD_UI, SEV_DEBUG) << "Finish !"; } // Get constraint created by this filter void -TableFilter::getConstraint(Constraint& constraint) +TableFilter::getConstraint(SearchFilter& filter) { Wt::WModelIndexSet indexSet = this->selectedIndexes(); - // WHERE statement - WhereClause clause; - BOOST_FOREACH(Wt::WModelIndex index, indexSet) { if (!index.isValid()) @@ -122,14 +122,10 @@ TableFilter::getConstraint(Constraint& constraint) const ResultType& result = _queryModel.resultRow( index.row() ); - // Get the track part - std::string name(result.get<0>()); + std::string name = result.get<0>(); - clause.Or(_table + "." + _field + " = ?").bind(name); + filter.exactMatch[_field].push_back(name); } - - // Adding our WHERE clause - constraint.where.And( clause ); } } // namespace UserInterface diff --git a/src/ui/audio/TableFilter.hpp b/src/ui/audio/TableFilter.hpp index 2b5b3ca0..4f74dcd2 100644 --- a/src/ui/audio/TableFilter.hpp +++ b/src/ui/audio/TableFilter.hpp @@ -33,13 +33,13 @@ class TableFilter : public Wt::WTableView, public Filter { public: - TableFilter(Database::Handler& db, std::string table, std::string field, const Wt::WString& displayName, Wt::WContainerWidget* parent = 0); + TableFilter(Database::Handler& db, Database::SearchFilter::Field field, std::vector displayName, Wt::WContainerWidget* parent = 0); // Set constraints on this filter - void refresh(const Constraint& constraint); + void refresh(Database::SearchFilter& filter); // Get constraints created by this filter - void getConstraint(Constraint& constraint); + void getConstraint(Database::SearchFilter& filter); void layoutSizeChanged (int width, int height); @@ -52,10 +52,9 @@ class TableFilter : public Wt::WTableView, public Filter SigDoubleClicked _sigDoubleClicked; Database::Handler& _db; - const std::string _table; - const std::string _field; + Database::SearchFilter::Field _field; - // Name, track count, special value that means 'all' if set to 1 + // Name, track count typedef boost::tuple ResultType; Wt::Dbo::QueryModel< ResultType > _queryModel; diff --git a/src/ui/audio/TableFilterGenre.cpp b/src/ui/audio/TableFilterGenre.cpp deleted file mode 100644 index a90a6dee..00000000 --- a/src/ui/audio/TableFilterGenre.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2013 Emeric Poupon - * - * This file is part of LMS. - * - * LMS is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LMS is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with LMS. If not, see . - */ - -#include - -#include "logger/Logger.hpp" - -#include "TableFilterGenre.hpp" - -namespace UserInterface { - -TableFilterGenre::TableFilterGenre(Database::Handler& db, Wt::WContainerWidget* parent) -: Wt::WTableView( parent ), -Filter(), -_db(db) -{ - _queryModel.setQuery( _db.getSession().query< ResultType >("select genre.name, COUNT(DISTINCT track.id) from genre INNER JOIN track_genre ON track_genre.genre_id = genre.id INNER JOIN track ON track.id = track_genre.track_id").orderBy("genre.name").groupBy("genre.name").orderBy("genre.name")); - _queryModel.addColumn("genre.name", "Genre"); - _queryModel.addColumn("COUNT(DISTINCT track.id)", "tracks"); - - this->setSelectionMode(Wt::ExtendedSelection); - this->setSortingEnabled(false); - this->setAlternatingRowColors(true); - this->setModel(&_queryModel); - - this->selectionChanged().connect(this, &TableFilterGenre::emitUpdate); - - setLayoutSizeAware(true); - - _queryModel.setBatchSize(100); - - // If an item is double clicked, select and emit signal - this->doubleClicked().connect( std::bind([=] (Wt::WModelIndex idx, Wt::WMouseEvent evt) - { - if (!idx.isValid()) - return; - - Wt::WModelIndexSet indexSet; - indexSet.insert(idx); - - this->setSelectedIndexes( indexSet ); - _sigDoubleClicked.emit( ); - }, std::placeholders::_1, std::placeholders::_2)); - -} - -void -TableFilterGenre::layoutSizeChanged (int width, int height) -{ - LMS_LOG(MOD_UI, SEV_DEBUG) << "LAYOUT CHANGED!"; - - /* TODO - std::size_t trackColumnSize = this->columnWidth(1).toPixels() + 30 ; - // Set the remaining size for the name column - this->setColumnWidth(0, width - 7 - trackColumnSize); - */ - -} - -// Set constraints on this filter -void -TableFilterGenre::refresh(const Constraint& constraint) -{ - SqlQuery sqlQuery; - - sqlQuery.select("genre.name, COUNT(DISTINCT track.id)"); - sqlQuery.from().And( std::string("genre INNER JOIN track_genre ON track_genre.genre_id = genre.id INNER JOIN track ON track.id = track_genre.track_id") ); - sqlQuery.where().And(constraint.where); // Add constraint made by other filters - sqlQuery.groupBy().And( std::string("genre.name") ); - - LMS_LOG(MOD_UI, SEV_DEBUG) << "genre, generated query = '" << sqlQuery.get() << "'"; - - Wt::Dbo::Query query = _db.getSession().query( sqlQuery.get() ); - - query.orderBy("genre.name"); - - BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) { - LMS_LOG(MOD_UI, SEV_DEBUG) << "Binding value '" << bindArg << "'"; - query.bind(bindArg); - } - - _queryModel.setQuery( query, true ); - -} - -// Get constraint created by this filter -void -TableFilterGenre::getConstraint(Constraint& constraint) -{ - Wt::WModelIndexSet indexSet = this->selectedIndexes(); - - // WHERE statement - WhereClause clause; - - BOOST_FOREACH(Wt::WModelIndex index, indexSet) { - - if (!index.isValid()) - continue; - - const ResultType& result = _queryModel.resultRow( index.row() ); - - // Get the track part - std::string name(result.get<0>()); - - if (name == "") - clause.Or( std::string("track.genre_list = ?") ).bind(""); - else - clause.Or( std::string("track.genre_list LIKE ?") ).bind("%%" + name + "%%"); - } - - // Adding our WHERE clause - constraint.where.And( clause ); - -} - -} // namespace UserInterface - diff --git a/src/ui/audio/TableFilterGenre.hpp b/src/ui/audio/TableFilterGenre.hpp deleted file mode 100644 index 97ef5948..00000000 --- a/src/ui/audio/TableFilterGenre.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2013 Emeric Poupon - * - * This file is part of LMS. - * - * LMS is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LMS is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with LMS. If not, see . - */ - -#ifndef TABLE_FILTER_GENRE_HPP -#define TABLE_FILTER_GENRE_HPP - - -#include -#include - -#include "Filter.hpp" -#include "database/DatabaseHandler.hpp" - -namespace UserInterface { - -class TableFilterGenre : public Wt::WTableView, public Filter -{ - - public: - TableFilterGenre(Database::Handler& db, Wt::WContainerWidget* parent = 0); - - // Set constraints on this filter - void refresh(const Constraint& constraint); - - // Get constraints created by this filter - void getConstraint(Constraint& constraint); - - void layoutSizeChanged (int width, int height); - - typedef Wt::Signal SigDoubleClicked; - - SigDoubleClicked& sigDoubleClicked() { return _sigDoubleClicked; } - - protected: - - SigDoubleClicked _sigDoubleClicked; - - Database::Handler& _db; - - // genre_name, count - typedef boost::tuple ResultType; - Wt::Dbo::QueryModel< ResultType > _queryModel; - -}; - -} // namespace UserInterface - -#endif - diff --git a/src/ui/audio/TrackView.cpp b/src/ui/audio/TrackView.cpp index a57866c8..75bb62b4 100644 --- a/src/ui/audio/TrackView.cpp +++ b/src/ui/audio/TrackView.cpp @@ -33,18 +33,25 @@ TrackView::TrackView( Database::Handler& db, Wt::WContainerWidget* parent) : Wt::WTableView( parent ), _db(db) { - _queryModel.setQuery(_db.getSession().query("select track from track").orderBy("track.artist_name,track.date,track.release_name,track.disc_number,track.track_number")); - _queryModel.addColumn( "track.artist_name", "Artist" ); - _queryModel.addColumn( "track.release_name", "Album" ); - _queryModel.addColumn( "track.disc_number", "Disc #" ); - _queryModel.addColumn( "track.track_number", "Track #" ); - _queryModel.addColumn( "track.name", "Track" ); - _queryModel.addColumn( "track.duration", "Duration" ); - _queryModel.addColumn( "track.date", "Date" ); - _queryModel.addColumn( "track.original_date", "Original Date" ); - _queryModel.addColumn( "track.genre_list", "Genres" ); - _queryModel.setBatchSize(500); + static const std::vector columnNames = + { + "Artist", + "Album", + "Disc #", + "Track #", + "Track", + "Duration", + "Date", + "Original Date", + "Genres", + }; + + Database::SearchFilter filter; + + Database::Track::updateTracksQueryModel(_db.getSession(), _queryModel, filter, columnNames); + + _queryModel.setBatchSize(300); this->setSortingEnabled(true); this->setSelectionMode(Wt::ExtendedSelection); @@ -60,7 +67,6 @@ _db(db) this->setColumnWidth(6, 70); // Date this->setColumnWidth(7, 70); // Original Date this->setColumnWidth(8, 180); // Genres - // this->setOverflow(Wt::WContainerWidget::OverflowScroll, Wt::Vertical); // Duration display @@ -102,28 +108,9 @@ _db(db) // Set constraints created by parent filters void -TrackView::refresh(const Constraint& constraint) +TrackView::refresh(Database::SearchFilter& filter) { - - SqlQuery sqlQuery; - - sqlQuery.select( "track" ); - sqlQuery.from().And( FromClause("track")); - sqlQuery.where().And(constraint.where); - - LMS_LOG(MOD_UI, SEV_DEBUG) << "TRACK REQ = '" << sqlQuery.get() << "'"; - - Wt::Dbo::Query query = _db.getSession().query( sqlQuery.get() ); - - query.groupBy("track").orderBy("track.artist_name,track.date,track.release_name,track.disc_number,track.track_number"); - - BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) { - LMS_LOG(MOD_UI, SEV_DEBUG) << "Binding value '" << bindArg << "'"; - query.bind(bindArg); - } - - _queryModel.setQuery( query, true ); - + Database::Track::updateTracksQueryModel(_db.getSession(), _queryModel, filter); } void @@ -138,10 +125,7 @@ TrackView::getSelectedTracks(std::vector& track_ids) if (!index.isValid()) continue; - const ResultType& result = _queryModel.resultRow( index.row() ); - - // Get the track part - Wt::Dbo::ptr track ( result.get<0>() ); + Database::Track::pointer track = _queryModel.resultRow( index.row() ); track_ids.push_back(track.id()); } @@ -177,10 +161,7 @@ TrackView::getTracks(std::vector& trackIds) for (int i = 0; i < _queryModel.rowCount(); ++i) { - const ResultType& result = _queryModel.resultRow( i ); - - // Get the track part - Wt::Dbo::ptr track ( result.get<0>() ); + Database::Track::pointer track = _queryModel.resultRow(i); trackIds.push_back(track.id()); } diff --git a/src/ui/audio/TrackView.hpp b/src/ui/audio/TrackView.hpp index 7b035bfc..6b0a24bf 100644 --- a/src/ui/audio/TrackView.hpp +++ b/src/ui/audio/TrackView.hpp @@ -38,10 +38,10 @@ class TrackView : public Wt::WTableView, public Filter // Filter interface // Set constraints created by parent filters - void refresh(const Constraint& constraint); + void refresh(Database::SearchFilter& filter); // Create constraints for child filters (N/A) - void getConstraint(Constraint& constraint) {} + void getConstraint(Database::SearchFilter& filter) {} // Get all the tracks that are currently selected void getSelectedTracks(std::vector& trackIds); @@ -64,7 +64,7 @@ class TrackView : public Wt::WTableView, public Filter Database::Handler& _db; - typedef boost::tuple ResultType; + typedef Database::Track::pointer ResultType; Wt::Dbo::QueryModel< ResultType > _queryModel; Wt::WTableView* _tableView;