From d5ff695eed6a5c07b15a361d6344e4bdf0a49f1f Mon Sep 17 00:00:00 2001 From: casey langen Date: Mon, 1 Jan 2018 00:28:07 -0800 Subject: [PATCH] Fixed broken TrackMetadataQuery, and also implemented generic metadata queries in CategoryTrackListQuery. CategoryListQuery still unsupported -- that's gonna be the hard one. --- .../query/local/CategoryTrackListQuery.cpp | 158 ++++++++++++------ .../query/local/CategoryTrackListQuery.h | 9 + .../query/local/TrackMetadataQuery.cpp | 29 ++-- 3 files changed, 130 insertions(+), 66 deletions(-) diff --git a/src/core/library/query/local/CategoryTrackListQuery.cpp b/src/core/library/query/local/CategoryTrackListQuery.cpp index 9aa9346b1..3cf434fa3 100755 --- a/src/core/library/query/local/CategoryTrackListQuery.cpp +++ b/src/core/library/query/local/CategoryTrackListQuery.cpp @@ -39,7 +39,6 @@ #include #include -#include #include #include @@ -58,18 +57,15 @@ using namespace musik::core::library::constants; using namespace boost::algorithm; static std::map FIELD_TO_FOREIGN_KEY = { - std::make_pair(Track::ALBUM, Track::ALBUM_ID), - std::make_pair(Track::ARTIST, Track::ARTIST_ID), - std::make_pair(Track::GENRE, Track::GENRE_ID), - std::make_pair(Track::ALBUM_ARTIST, Track::ALBUM_ARTIST_ID), - std::make_pair(Playlists::TABLE_NAME, Playlists::TABLE_NAME) + { Track::ALBUM, Track::ALBUM_ID }, + { Track::ARTIST, Track::ARTIST_ID }, + { Track::GENRE, Track::GENRE_ID }, + { Track::ALBUM_ARTIST, Track::ALBUM_ARTIST_ID }, + { Playlists::TABLE_NAME, Playlists::TABLE_NAME } }; CategoryTrackListQuery::CategoryTrackListQuery( - ILibraryPtr library, - const std::string& column, - int64_t id, - const std::string& filter) + ILibraryPtr library, const std::string& column, int64_t id, const std::string& filter) { this->library = library; this->id = id; @@ -81,11 +77,14 @@ CategoryTrackListQuery::CategoryTrackListQuery( this->filter = "%" + trim_copy(to_lower_copy(filter)) + "%"; } - if (FIELD_TO_FOREIGN_KEY.find(column) == FIELD_TO_FOREIGN_KEY.end()) { - throw std::runtime_error("invalid input column specified"); + if (FIELD_TO_FOREIGN_KEY.find(column) != FIELD_TO_FOREIGN_KEY.end()) { + this->type = (column == Playlists::TABLE_NAME) ? Playlist : Regular; + this->column = FIELD_TO_FOREIGN_KEY[column]; /* optimized query */ + } + else { + this->type = Extended; + this->column = column; /* generalized query */ } - - this->column = FIELD_TO_FOREIGN_KEY[column]; } CategoryTrackListQuery::~CategoryTrackListQuery() { @@ -111,64 +110,121 @@ size_t CategoryTrackListQuery::GetQueryHash() { return this->hash; } -bool CategoryTrackListQuery::OnRun(Connection& db) { - if (result) { - result.reset(new musik::core::TrackList(this->library)); - headers.reset(new std::set()); +void CategoryTrackListQuery::PlaylistQuery(musik::core::db::Connection &db) { + /* playlists are a special case. we already have a query for this, so + delegate to that. */ + GetPlaylistQuery query(this->library, this->id); + query.Run(db); + this->result = query.GetResult(); +} + +void CategoryTrackListQuery::RegularQuery(musik::core::db::Connection &db) { + /* these are the most common queries in the app, and are more optimized + than extended metadata queries. */ + std::string query = + "SELECT DISTINCT t.id, al.name " + "FROM tracks t, albums al, artists ar, genres gn " + "WHERE t.visible=1 AND t.%s=? AND t.album_id=al.id AND t.visual_genre_id=gn.id AND t.visual_artist_id=ar.id "; + + if (this->filter.size()) { + query += " AND (t.title LIKE ? OR al.name LIKE ? OR ar.name LIKE ? OR gn.name LIKE ?) "; } - if (this->column == Playlists::TABLE_NAME) { - /* playlists are a special case. we already have a query for this, so - delegate to that. */ + query += "ORDER BY al.name, disc, track, ar.name %s"; + query = boost::str(boost::format(query) % this->column % this->GetLimitAndOffset()); - GetPlaylistQuery query(this->library, this->id); - query.Run(db); - this->result = query.GetResult(); + Statement trackQuery(query.c_str(), db); + + if (this->filter.size()) { + trackQuery.BindInt64(0, this->id); + trackQuery.BindText(1, this->filter); + trackQuery.BindText(2, this->filter); + trackQuery.BindText(3, this->filter); + trackQuery.BindText(4, this->filter); } else { - /* regular old category query... */ + trackQuery.BindInt64(0, this->id); + } - std::string lastAlbum; - size_t index = 0; + this->ProcessResult(trackQuery); +} +void CategoryTrackListQuery::ExtendedQuery(musik::core::db::Connection &db) { + int64_t keyId = 0; + + { + Statement keyQuery("SELECT DISTINCT id FROM meta_keys WHERE LOWER(name)=LOWER(?)", db); + keyQuery.BindText(0, this->column); + if (keyQuery.Step() == db::Row) { + keyId = keyQuery.ColumnInt64(0); + } + } + + if (keyId > 0) { + /* the core library allows for storage of arbitrary metadata in the form + of key/value pairs linked to tracks. they are slower and require additional + joins and subqueries, but are fully supported here. */ std::string query = "SELECT DISTINCT t.id, al.name " - "FROM tracks t, albums al, artists ar, genres gn " - "WHERE t.visible=1 AND t.%s=? AND t.album_id=al.id AND t.visual_genre_id=gn.id AND t.visual_artist_id=ar.id "; + "FROM tracks t, albums al, artists ar, genres gn, track_meta tm, meta_keys mk, meta_values mv " + "WHERE t.visible=1 " + " AND t.album_id=al.id AND t.visual_genre_id=gn.id AND t.visual_artist_id=ar.id " + " AND t.id=tm.track_id AND tm.meta_value_id=mv.id AND mv.meta_key_id=mk.id " + " AND mk.id=? AND mv.id=? "; if (this->filter.size()) { query += " AND (t.title LIKE ? OR al.name LIKE ? OR ar.name LIKE ? OR gn.name LIKE ?) "; } query += "ORDER BY al.name, disc, track, ar.name %s"; - - query = boost::str(boost::format(query) % this->column % this->GetLimitAndOffset()); + query = boost::str(boost::format(query) % this->GetLimitAndOffset()); Statement trackQuery(query.c_str(), db); + int bindOffset = 0; + trackQuery.BindInt64(bindOffset++, keyId); + trackQuery.BindInt64(bindOffset++, this->id); + if (this->filter.size()) { - trackQuery.BindInt64(0, this->id); - trackQuery.BindText(1, this->filter); - trackQuery.BindText(2, this->filter); - trackQuery.BindText(3, this->filter); - trackQuery.BindText(4, this->filter); - } - else { - trackQuery.BindInt64(0, this->id); + trackQuery.BindText(bindOffset++, this->filter); + trackQuery.BindText(bindOffset++, this->filter); + trackQuery.BindText(bindOffset++, this->filter); + trackQuery.BindText(bindOffset++, this->filter); } - while (trackQuery.Step() == Row) { - int64_t id = trackQuery.ColumnInt64(0); - std::string album = trackQuery.ColumnText(1); - - if (album != lastAlbum) { - headers->insert(index); - lastAlbum = album; - } - - result->Add(id); - ++index; - } + this->ProcessResult(trackQuery); } +} + +void CategoryTrackListQuery::ProcessResult(musik::core::db::Statement& trackQuery) { + std::string lastAlbum; + size_t index = 0; + + while (trackQuery.Step() == Row) { + int64_t id = trackQuery.ColumnInt64(0); + std::string album = trackQuery.ColumnText(1); + + if (album != lastAlbum) { + headers->insert(index); + lastAlbum = album; + } + + result->Add(id); + ++index; + } +} + +bool CategoryTrackListQuery::OnRun(Connection& db) { + if (result) { + result.reset(new musik::core::TrackList(this->library)); + headers.reset(new std::set()); + } + + switch (this->type) { + case Playlist: this->PlaylistQuery(db); break; + case Regular: this->RegularQuery(db); break; + case Extended: this->ExtendedQuery(db); break; + } + return true; } diff --git a/src/core/library/query/local/CategoryTrackListQuery.h b/src/core/library/query/local/CategoryTrackListQuery.h index 069546e23..f11b8737a 100755 --- a/src/core/library/query/local/CategoryTrackListQuery.h +++ b/src/core/library/query/local/CategoryTrackListQuery.h @@ -37,6 +37,7 @@ #include #include #include +#include #include "TrackListQueryBase.h" @@ -62,10 +63,18 @@ namespace musik { namespace core { namespace db { namespace local { virtual bool OnRun(musik::core::db::Connection &db); private: + enum Type { Playlist, Regular, Extended }; + + void PlaylistQuery(musik::core::db::Connection &db); + void RegularQuery(musik::core::db::Connection &db); + void ExtendedQuery(musik::core::db::Connection &db); + void ProcessResult(musik::core::db::Statement& stmt); + musik::core::ILibraryPtr library; Result result; Headers headers; std::string column; + Type type; int64_t id; size_t hash; std::string filter; diff --git a/src/core/library/query/local/TrackMetadataQuery.cpp b/src/core/library/query/local/TrackMetadataQuery.cpp index 0b5267e69..6feece761 100644 --- a/src/core/library/query/local/TrackMetadataQuery.cpp +++ b/src/core/library/query/local/TrackMetadataQuery.cpp @@ -104,21 +104,20 @@ bool TrackMetadataQuery::OnRun(Connection& db) { result->SetValue(constants::Track::BPM, trackQuery.ColumnText(2)); result->SetValue(constants::Track::DURATION, trackQuery.ColumnText(3)); result->SetValue(constants::Track::FILESIZE, trackQuery.ColumnText(4)); - result->SetValue(constants::Track::YEAR, trackQuery.ColumnText(5)); - result->SetValue(constants::Track::TITLE, trackQuery.ColumnText(6)); - result->SetValue(constants::Track::FILENAME, trackQuery.ColumnText(7)); - result->SetValue(constants::Track::THUMBNAIL_ID, trackQuery.ColumnText(8)); - result->SetValue(constants::Track::ALBUM, trackQuery.ColumnText(9)); - result->SetValue(constants::Track::ALBUM_ARTIST, trackQuery.ColumnText(10)); - result->SetValue(constants::Track::GENRE, trackQuery.ColumnText(11)); - result->SetValue(constants::Track::ARTIST, trackQuery.ColumnText(12)); - result->SetValue(constants::Track::FILETIME, trackQuery.ColumnText(13)); - result->SetValue(constants::Track::GENRE_ID, trackQuery.ColumnText(14)); - result->SetValue(constants::Track::ARTIST_ID, trackQuery.ColumnText(15)); - result->SetValue(constants::Track::ALBUM_ARTIST_ID, trackQuery.ColumnText(16)); - result->SetValue(constants::Track::ALBUM_ID, trackQuery.ColumnText(17)); - result->SetValue(constants::Track::SOURCE_ID, trackQuery.ColumnText(18)); - result->SetValue(constants::Track::EXTERNAL_ID, trackQuery.ColumnText(19)); + result->SetValue(constants::Track::TITLE, trackQuery.ColumnText(5)); + result->SetValue(constants::Track::FILENAME, trackQuery.ColumnText(6)); + result->SetValue(constants::Track::THUMBNAIL_ID, trackQuery.ColumnText(7)); + result->SetValue(constants::Track::ALBUM, trackQuery.ColumnText(8)); + result->SetValue(constants::Track::ALBUM_ARTIST, trackQuery.ColumnText(9)); + result->SetValue(constants::Track::GENRE, trackQuery.ColumnText(10)); + result->SetValue(constants::Track::ARTIST, trackQuery.ColumnText(11)); + result->SetValue(constants::Track::FILETIME, trackQuery.ColumnText(12)); + result->SetValue(constants::Track::GENRE_ID, trackQuery.ColumnText(13)); + result->SetValue(constants::Track::ARTIST_ID, trackQuery.ColumnText(14)); + result->SetValue(constants::Track::ALBUM_ARTIST_ID, trackQuery.ColumnText(15)); + result->SetValue(constants::Track::ALBUM_ID, trackQuery.ColumnText(16)); + result->SetValue(constants::Track::SOURCE_ID, trackQuery.ColumnText(17)); + result->SetValue(constants::Track::EXTERNAL_ID, trackQuery.ColumnText(18)); } else { result->SetValue(constants::Track::EXTERNAL_ID, trackQuery.ColumnText(0));