mirror of
https://github.com/clangen/musikcube.git
synced 2025-03-29 19:20:28 +00:00
Refactored predicate and argument stuff into util module that can be
reused in CategoryTrackListQuery.
This commit is contained in:
parent
3659395af5
commit
273212bd0d
@ -31,6 +31,7 @@ set(CORE_SOURCES
|
||||
./library/query/local/SavePlaylistQuery.cpp
|
||||
./library/query/local/SearchTrackListQuery.cpp
|
||||
./library/query/local/TrackMetadataQuery.cpp
|
||||
./library/query/local/util/CategoryQueryUtil.cpp
|
||||
./library/metadata/MetadataMap.cpp
|
||||
./library/metadata/MetadataMapList.cpp
|
||||
./library/track/IndexerTrack.cpp
|
||||
|
@ -117,6 +117,7 @@
|
||||
<ClCompile Include="library\query\local\SavePlaylistQuery.cpp" />
|
||||
<ClCompile Include="library\query\local\SearchTrackListQuery.cpp" />
|
||||
<ClCompile Include="library\query\local\TrackMetadataQuery.cpp" />
|
||||
<ClCompile Include="library\query\local\util\CategoryQueryUtil.cpp" />
|
||||
<ClCompile Include="library\track\IndexerTrack.cpp" />
|
||||
<ClCompile Include="library\track\LibraryTrack.cpp" />
|
||||
<ClCompile Include="library\track\Track.cpp" />
|
||||
@ -176,6 +177,7 @@
|
||||
<ClInclude Include="library\query\local\TrackListQueryBase.h" />
|
||||
<ClInclude Include="Library\query\local\LocalQueryBase.h" />
|
||||
<ClInclude Include="library\query\local\TrackMetadataQuery.h" />
|
||||
<ClInclude Include="library\query\local\util\CategoryQueryUtil.h" />
|
||||
<ClInclude Include="library\track\IndexerTrack.h" />
|
||||
<ClInclude Include="library\track\LibraryTrack.h" />
|
||||
<ClInclude Include="library\track\Track.h" />
|
||||
|
@ -59,6 +59,9 @@
|
||||
<Filter Include="src\sdk\metadata">
|
||||
<UniqueIdentifier>{a39a447a-65ad-465c-839d-b96cc4a8e2e9}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="src\library\query\local\util">
|
||||
<UniqueIdentifier>{3c3cc444-39fd-482f-aca9-3e67b98d8fc0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
@ -199,6 +202,9 @@
|
||||
<ClCompile Include="library\query\local\ReplayGainQuery.cpp">
|
||||
<Filter>src\library\query\local</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="library\query\local\util\CategoryQueryUtil.cpp">
|
||||
<Filter>src\library\query\local\util</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.hpp">
|
||||
@ -486,5 +492,8 @@
|
||||
<ClInclude Include="library\query\local\ReplayGainQuery.h">
|
||||
<Filter>src\library\query\local</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="library\query\local\util\CategoryQueryUtil.h">
|
||||
<Filter>src\library\query\local\util</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -59,57 +59,6 @@ using namespace musik::core::db::local;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static std::map<std::string, std::string> PREDICATE_TO_COLUMN_MAP = {
|
||||
{ Track::ALBUM, "album_id" },
|
||||
{ Track::ARTIST, "visual_artist_id" },
|
||||
{ Track::ALBUM_ARTIST, "album_artist_id" },
|
||||
{ Track::GENRE, "visual_genre_id" }
|
||||
};
|
||||
|
||||
static std::map<std::string, std::pair<std::string, std::string>> REGULAR_PROPERTY_MAP = {
|
||||
{ "album", { "albums", "album_id" } },
|
||||
{ "artist", { "artists", "visual_artist_id" } },
|
||||
{ "album_artist", { "artists", "album_artist_id" } },
|
||||
{ "genre", { "genres", "visual_genre_id" } }
|
||||
};
|
||||
|
||||
static const std::string REGULAR_PREDICATE = " tracks.{{fk_id}}=? ";
|
||||
static const std::string REGULAR_FILTER = " AND LOWER({{table}}.name) LIKE ? ";
|
||||
|
||||
static const std::string EXTENDED_PREDICATE = " (key=? AND meta_value_id=?) ";
|
||||
static const std::string EXTENDED_FILTER = " AND LOWER(extended_metadata.value) LIKE ?";
|
||||
|
||||
static const std::string EXTENDED_INNER_JOIN =
|
||||
"INNER JOIN ( "
|
||||
" SELECT id AS track_id "
|
||||
" FROM extended_metadata "
|
||||
" WHERE {{extended_predicates}} "
|
||||
" GROUP BY track_id "
|
||||
" HAVING COUNT(track_id)={{extended_predicate_count}} "
|
||||
") AS md ON tracks.id=md.track_id ";
|
||||
|
||||
static const std::string REGULAR_PROPERTY_QUERY =
|
||||
"SELECT DISTINCT {{table}}.id, {{table}}.name "
|
||||
"FROM {{table}}, tracks "
|
||||
"{{extended_predicates}} "
|
||||
"WHERE {{table}}.id=tracks.{{fk_id}} AND tracks.visible=1 "
|
||||
"{{regular_predicates}} "
|
||||
"{{regular_filter}} "
|
||||
"ORDER BY {{table}}.sort_order";
|
||||
|
||||
static const std::string EXTENDED_PROPERTY_QUERY =
|
||||
"SELECT DISTINCT meta_value_id, value "
|
||||
"FROM extended_metadata "
|
||||
"INNER JOIN ( "
|
||||
" SELECT id AS track_id "
|
||||
" FROM tracks "
|
||||
" WHERE tracks.visible=1 {{regular_predicates}} "
|
||||
") AS reg on extended_metadata.id=reg.track_id "
|
||||
"{{extended_predicates}} "
|
||||
"WHERE extended_metadata.key=? "
|
||||
"{{extended_filter}} "
|
||||
"ORDER BY extended_metadata.value ASC";
|
||||
|
||||
static const std::string UNFILTERED_PLAYLISTS_QUERY =
|
||||
"SELECT DISTINCT id, name "
|
||||
"FROM playlists "
|
||||
@ -121,19 +70,6 @@ static const std::string FILTERED_PLAYLISTS_QUERY =
|
||||
"WHERE LOWER(name) LIKE LOWER(?) "
|
||||
"ORDER BY name;";
|
||||
|
||||
/* quick and dirty structures used to track bind arguments */
|
||||
struct Id : public CategoryListQuery::Argument {
|
||||
int64_t id;
|
||||
Id(int64_t id) : id(id) { }
|
||||
virtual void Bind(Statement& stmt, int pos) const { stmt.BindInt64(pos, id); }
|
||||
};
|
||||
|
||||
struct String : public CategoryListQuery::Argument {
|
||||
std::string str;
|
||||
String(const std::string& str) : str(str) { }
|
||||
virtual void Bind(Statement& stmt, int pos) const { stmt.BindText(pos, str.c_str()); }
|
||||
};
|
||||
|
||||
/* data structure that we can return to plugins who need metadata info */
|
||||
class ValueList : public musik::core::sdk::IValueList {
|
||||
public:
|
||||
@ -167,19 +103,19 @@ static void replaceAll(std::string& input, const std::string& find, const std::s
|
||||
|
||||
CategoryListQuery::CategoryListQuery(
|
||||
const std::string& trackField, const std::string& filter)
|
||||
: CategoryListQuery(trackField, PredicateList(), filter) {
|
||||
: CategoryListQuery(trackField, category::PredicateList(), filter) {
|
||||
}
|
||||
|
||||
CategoryListQuery::CategoryListQuery(
|
||||
const std::string& trackField,
|
||||
const Predicate predicate,
|
||||
const category::Predicate predicate,
|
||||
const std::string& filter)
|
||||
: CategoryListQuery(trackField, PredicateList{ predicate }, filter) {
|
||||
: CategoryListQuery(trackField, category::PredicateList{ predicate }, filter) {
|
||||
}
|
||||
|
||||
CategoryListQuery::CategoryListQuery(
|
||||
const std::string& trackField,
|
||||
const PredicateList predicates,
|
||||
const category::PredicateList predicates,
|
||||
const std::string& filter)
|
||||
: trackField(trackField)
|
||||
, filter(filter) {
|
||||
@ -192,21 +128,15 @@ CategoryListQuery::CategoryListQuery(
|
||||
this->filter = "%" + wild + "%";
|
||||
}
|
||||
|
||||
auto end = REGULAR_PROPERTY_MAP.end();
|
||||
|
||||
for (auto p : predicates) {
|
||||
if (REGULAR_PROPERTY_MAP.find(p.first) != end) {
|
||||
this->regular.push_back(p);
|
||||
}
|
||||
else {
|
||||
this->extended.push_back(p);
|
||||
}
|
||||
}
|
||||
category::SplitPredicates(predicates, this->regular, this->extended);
|
||||
|
||||
auto end = category::REGULAR_PROPERTY_MAP.end();
|
||||
|
||||
if (trackField == "playlists") {
|
||||
this->outputType = Playlist;
|
||||
}
|
||||
else if (REGULAR_PROPERTY_MAP.find(trackField) != end) {
|
||||
else if (category::GetPropertyType(trackField) == category::PropertyType::Regular) {
|
||||
this->outputType = Regular;
|
||||
}
|
||||
else {
|
||||
@ -215,64 +145,6 @@ CategoryListQuery::CategoryListQuery(
|
||||
}
|
||||
|
||||
CategoryListQuery::~CategoryListQuery() {
|
||||
|
||||
}
|
||||
|
||||
std::string CategoryListQuery::JoinRegular(
|
||||
const PredicateList& pred, ArgumentList& args, const std::string& prefix)
|
||||
{
|
||||
std::string result;
|
||||
if (pred.size()) {
|
||||
for (size_t i = 0; i < pred.size(); i++) {
|
||||
if (i > 0) { result += " AND "; }
|
||||
auto p = pred[i];
|
||||
auto str = REGULAR_PREDICATE;
|
||||
auto map = REGULAR_PROPERTY_MAP[p.first];
|
||||
replaceAll(str, "{{fk_id}}", map.second);
|
||||
result += str;
|
||||
args.push_back(std::make_shared<Id>(p.second));
|
||||
}
|
||||
|
||||
if (prefix.size()) {
|
||||
result = prefix + result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string CategoryListQuery::InnerJoinExtended(
|
||||
const PredicateList& pred, ArgumentList& args)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
std::string joined = JoinExtended(pred, args);
|
||||
if (joined.size()) {
|
||||
result = EXTENDED_INNER_JOIN;
|
||||
replaceAll(result, "{{extended_predicates}}", joined);
|
||||
replaceAll(result, "{{extended_predicate_count}}", std::to_string(pred.size()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string CategoryListQuery::JoinExtended(
|
||||
const PredicateList& pred, ArgumentList& args)
|
||||
{
|
||||
std::string result;
|
||||
for (size_t i = 0; i < pred.size(); i++) {
|
||||
if (i > 0) { result += " OR "; }
|
||||
result += EXTENDED_PREDICATE;
|
||||
auto p = pred[i];
|
||||
args.push_back(std::make_shared<String>(p.first));
|
||||
args.push_back(std::make_shared<Id>(p.second));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void CategoryListQuery::Apply(Statement& stmt, const ArgumentList& args) {
|
||||
for (size_t i = 0; i < args.size(); i++) {
|
||||
args[i]->Bind(stmt, (int) i);
|
||||
}
|
||||
}
|
||||
|
||||
CategoryListQuery::ResultList CategoryListQuery::GetResult() {
|
||||
@ -310,18 +182,18 @@ void CategoryListQuery::QueryPlaylist(musik::core::db::Connection& db) {
|
||||
}
|
||||
|
||||
void CategoryListQuery::QueryRegular(musik::core::db::Connection &db) {
|
||||
ArgumentList args;
|
||||
category::ArgumentList args;
|
||||
|
||||
auto prop = REGULAR_PROPERTY_MAP[this->trackField];
|
||||
std::string query = REGULAR_PROPERTY_QUERY;
|
||||
auto prop = category::REGULAR_PROPERTY_MAP[this->trackField];
|
||||
std::string query = category::REGULAR_PROPERTY_QUERY;
|
||||
std::string extended = InnerJoinExtended(this->extended, args);
|
||||
std::string regular = JoinRegular(this->regular, args, " AND ");
|
||||
std::string regularFilter;
|
||||
|
||||
if (this->filter.size()) {
|
||||
regularFilter = REGULAR_FILTER;
|
||||
regularFilter = category::REGULAR_FILTER;
|
||||
replaceAll(regularFilter, "{{table}}", prop.first);
|
||||
args.push_back(std::make_shared<String>(this->filter));
|
||||
args.push_back(category::StringArgument(this->filter));
|
||||
}
|
||||
|
||||
replaceAll(query, "{{table}}", prop.first);
|
||||
@ -336,23 +208,23 @@ void CategoryListQuery::QueryRegular(musik::core::db::Connection &db) {
|
||||
}
|
||||
|
||||
void CategoryListQuery::QueryExtended(musik::core::db::Connection &db) {
|
||||
ArgumentList args;
|
||||
category::ArgumentList args;
|
||||
|
||||
std::string query = EXTENDED_PROPERTY_QUERY;
|
||||
std::string regular = JoinRegular(this->regular, args, " AND ");
|
||||
std::string extended = InnerJoinExtended(this->extended, args);
|
||||
std::string query = category::EXTENDED_PROPERTY_QUERY;
|
||||
std::string regular = category::JoinRegular(this->regular, args, " AND ");
|
||||
std::string extended = category::InnerJoinExtended(this->extended, args);
|
||||
std::string extendedFilter;
|
||||
|
||||
if (this->filter.size()) {
|
||||
extendedFilter = EXTENDED_FILTER;
|
||||
args.push_back(std::make_shared<String>(this->filter));
|
||||
extendedFilter = category::EXTENDED_FILTER;
|
||||
args.push_back(category::StringArgument(this->filter));
|
||||
}
|
||||
|
||||
replaceAll(query, "{{regular_predicates}}", regular);
|
||||
replaceAll(query, "{{extended_predicates}}", extended);
|
||||
replaceAll(query, "{{extended_filter}}", extendedFilter);
|
||||
|
||||
args.push_back(std::make_shared<String>(this->trackField));
|
||||
args.push_back(category::StringArgument(this->trackField));
|
||||
|
||||
DUMP(query);
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <core/library/query/local/LocalQueryBase.h>
|
||||
#include <core/library/query/local/util/CategoryQueryUtil.h>
|
||||
#include <core/db/Statement.h>
|
||||
#include <core/db/Connection.h>
|
||||
#include <core/sdk/IValueList.h>
|
||||
@ -72,11 +73,6 @@ namespace musik { namespace core { namespace db { namespace local {
|
||||
int64_t id;
|
||||
};
|
||||
|
||||
using Predicate = std::pair<std::string, int64_t>;
|
||||
using PredicateList = std::vector<Predicate>;
|
||||
struct Argument { virtual void Bind(Statement& stmt, int pos) const = 0; };
|
||||
using ArgumentList = std::vector<std::shared_ptr<Argument>>;
|
||||
|
||||
typedef std::shared_ptr<std::vector<
|
||||
std::shared_ptr<Result> > > ResultList;
|
||||
|
||||
@ -86,12 +82,12 @@ namespace musik { namespace core { namespace db { namespace local {
|
||||
|
||||
CategoryListQuery(
|
||||
const std::string& trackField,
|
||||
const Predicate predicate,
|
||||
const category::Predicate predicate,
|
||||
const std::string& filter = "");
|
||||
|
||||
CategoryListQuery(
|
||||
const std::string& trackField,
|
||||
const PredicateList predicate,
|
||||
const category::PredicateList predicate,
|
||||
const std::string& filter = "");
|
||||
|
||||
virtual ~CategoryListQuery();
|
||||
@ -114,25 +110,10 @@ namespace musik { namespace core { namespace db { namespace local {
|
||||
void QueryExtended(musik::core::db::Connection &db);
|
||||
void ProcessResult(musik::core::db::Statement &stmt);
|
||||
|
||||
static std::string JoinRegular(
|
||||
const PredicateList& pred,
|
||||
ArgumentList& args,
|
||||
const std::string& prefix = "");
|
||||
|
||||
static std::string InnerJoinExtended(
|
||||
const PredicateList& pred, ArgumentList& args);
|
||||
|
||||
static std::string JoinExtended(
|
||||
const PredicateList& pred, ArgumentList& args);
|
||||
|
||||
static void Apply(
|
||||
musik::core::db::Statement& stmt,
|
||||
const ArgumentList& args);
|
||||
|
||||
std::string trackField;
|
||||
std::string filter;
|
||||
OutputType outputType;
|
||||
PredicateList regular, extended;
|
||||
category::PredicateList regular, extended;
|
||||
ResultList result;
|
||||
};
|
||||
|
||||
|
163
src/core/library/query/local/util/CategoryQueryUtil.cpp
Normal file
163
src/core/library/query/local/util/CategoryQueryUtil.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2007-2017 musikcube team
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pch.hpp"
|
||||
#include "CategoryQueryUtil.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
|
||||
using musik::core::db::Statement;
|
||||
using musik::core::db::Row;
|
||||
|
||||
using namespace musik::core::db;
|
||||
using namespace musik::core::library::constants;
|
||||
using namespace musik::core::db::local;
|
||||
|
||||
static void replaceAll(std::string& input, const std::string& find, const std::string& replace) {
|
||||
size_t pos = input.find(find);
|
||||
while (pos != std::string::npos) {
|
||||
input.replace(pos, find.size(), replace);
|
||||
pos = input.find(find, pos + replace.size());
|
||||
}
|
||||
}
|
||||
|
||||
namespace musik { namespace core { namespace db { namespace local { namespace category {
|
||||
|
||||
struct Id : public Argument {
|
||||
int64_t id;
|
||||
Id(int64_t id) : id(id) { }
|
||||
virtual void Bind(Statement& stmt, int pos) const { stmt.BindInt64(pos, id); }
|
||||
};
|
||||
|
||||
struct String : public Argument {
|
||||
std::string str;
|
||||
String(const std::string& str) : str(str) { }
|
||||
virtual void Bind(Statement& stmt, int pos) const { stmt.BindText(pos, str.c_str()); }
|
||||
};
|
||||
|
||||
std::shared_ptr<Argument> IdArgument(int64_t id) {
|
||||
return std::make_shared<Id>(id);
|
||||
}
|
||||
|
||||
std::shared_ptr<Argument> StringArgument(const std::string str) {
|
||||
return std::make_shared<String>(str);
|
||||
}
|
||||
|
||||
PropertyType GetPropertyType(const std::string& property) {
|
||||
auto found = REGULAR_PROPERTY_MAP.find(property);
|
||||
auto end = REGULAR_PROPERTY_MAP.end();
|
||||
|
||||
return (found == end)
|
||||
? category::PropertyType::Extended
|
||||
: category::PropertyType::Regular;
|
||||
}
|
||||
|
||||
void SplitPredicates(
|
||||
const PredicateList& input,
|
||||
PredicateList& regular,
|
||||
PredicateList& extended)
|
||||
{
|
||||
auto end = REGULAR_PROPERTY_MAP.end();
|
||||
|
||||
for (auto p : input) {
|
||||
if (REGULAR_PROPERTY_MAP.find(p.first) != end) {
|
||||
regular.push_back(p);
|
||||
}
|
||||
else {
|
||||
extended.push_back(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string JoinRegular(
|
||||
const PredicateList& pred,
|
||||
ArgumentList& args,
|
||||
const std::string& prefix)
|
||||
{
|
||||
std::string result;
|
||||
if (pred.size()) {
|
||||
for (size_t i = 0; i < pred.size(); i++) {
|
||||
if (i > 0) { result += " AND "; }
|
||||
auto p = pred[i];
|
||||
auto str = REGULAR_PREDICATE;
|
||||
auto map = REGULAR_PROPERTY_MAP[p.first];
|
||||
replaceAll(str, "{{fk_id}}", map.second);
|
||||
result += str;
|
||||
args.push_back(std::make_shared<Id>(p.second));
|
||||
}
|
||||
|
||||
if (prefix.size()) {
|
||||
result = prefix + result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string category::InnerJoinExtended(
|
||||
const PredicateList& pred, ArgumentList& args)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
std::string joined = JoinExtended(pred, args);
|
||||
if (joined.size()) {
|
||||
result = EXTENDED_INNER_JOIN;
|
||||
replaceAll(result, "{{extended_predicates}}", joined);
|
||||
replaceAll(result, "{{extended_predicate_count}}", std::to_string(pred.size()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string JoinExtended(
|
||||
const PredicateList& pred, ArgumentList& args)
|
||||
{
|
||||
std::string result;
|
||||
for (size_t i = 0; i < pred.size(); i++) {
|
||||
if (i > 0) { result += " OR "; }
|
||||
result += EXTENDED_PREDICATE;
|
||||
auto p = pred[i];
|
||||
args.push_back(std::make_shared<String>(p.first));
|
||||
args.push_back(std::make_shared<Id>(p.second));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Apply(Statement& stmt, const ArgumentList& args) {
|
||||
for (size_t i = 0; i < args.size(); i++) {
|
||||
args[i]->Bind(stmt, (int)i);
|
||||
}
|
||||
}
|
||||
|
||||
} } } } }
|
184
src/core/library/query/local/util/CategoryQueryUtil.h
Normal file
184
src/core/library/query/local/util/CategoryQueryUtil.h
Normal file
@ -0,0 +1,184 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2007-2017 musikcube team
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <core/db/Statement.h>
|
||||
#include <core/db/Connection.h>
|
||||
#include <memory>
|
||||
|
||||
namespace musik { namespace core { namespace db { namespace local {
|
||||
|
||||
namespace category {
|
||||
namespace constants = musik::core::library::constants;
|
||||
|
||||
/* there are two types of track resources in our app: Regular, and Extended. "Regular"
|
||||
properties are well-defined and optimized, and include album, artist, album artist,
|
||||
and genre. they are fast to retrieve and easy to query. however, we also allow for
|
||||
plugins to index arbitrary track metadata in a completely denormalized key/value
|
||||
store. these resource types are called "Extended" properties. */
|
||||
|
||||
enum class PropertyType: int { Regular, Extended };
|
||||
|
||||
/* property name to foreign key id */
|
||||
static std::map<std::string, std::string> PREDICATE_TO_COLUMN_MAP = {
|
||||
{ constants::Track::ALBUM, "album_id" },
|
||||
{ constants::Track::ARTIST, "visual_artist_id" },
|
||||
{ constants::Track::ALBUM_ARTIST, "album_artist_id" },
|
||||
{ constants::Track::GENRE, "visual_genre_id" }
|
||||
};
|
||||
|
||||
/* resource type to a pair { <table_name>, <track_table_fk_name> } */
|
||||
static std::map<std::string, std::pair<std::string, std::string>> REGULAR_PROPERTY_MAP = {
|
||||
{ "album",{ "albums", "album_id" } },
|
||||
{ "artist",{ "artists", "visual_artist_id" } },
|
||||
{ "album_artist",{ "artists", "album_artist_id" } },
|
||||
{ "genre",{ "genres", "visual_genre_id" } }
|
||||
};
|
||||
|
||||
static const std::string REGULAR_PREDICATE = " tracks.{{fk_id}}=? ";
|
||||
static const std::string REGULAR_FILTER = " AND LOWER({{table}}.name) LIKE ? ";
|
||||
|
||||
static const std::string EXTENDED_PREDICATE = " (key=? AND meta_value_id=?) ";
|
||||
static const std::string EXTENDED_FILTER = " AND LOWER(extended_metadata.value) LIKE ?";
|
||||
|
||||
static const std::string EXTENDED_INNER_JOIN =
|
||||
"INNER JOIN ( "
|
||||
" SELECT id AS track_id "
|
||||
" FROM extended_metadata "
|
||||
" WHERE {{extended_predicates}} "
|
||||
" GROUP BY track_id "
|
||||
" HAVING COUNT(track_id)={{extended_predicate_count}} "
|
||||
") AS md ON tracks.id=md.track_id ";
|
||||
|
||||
/* REGULAR_PROPERTY_QUERY is used to return a list of property values and their
|
||||
respective IDs for regular properties, that is, albums, artists, album artists,
|
||||
and genres. these resource types are the most common, and are optimized for display.
|
||||
the query may also be predicated to select all resources that are related to other
|
||||
resources, including extended resources. the full requerly propety looks something
|
||||
like the following example where we query all albums made in "1995" by "J. Cantrell": */
|
||||
|
||||
// SELECT DISTINCT albums.id, albums.name
|
||||
// FROM albums, tracks
|
||||
// INNER JOIN(
|
||||
// SELECT id AS track_id
|
||||
// FROM extended_metadata
|
||||
// WHERE
|
||||
// (key = "year" AND value = "1995") OR
|
||||
// (key = "composer" AND value = "J. Cantrell")
|
||||
// GROUP BY track_id
|
||||
// HAVING COUNT(track_id) = 2
|
||||
// ) AS md ON tracks.id = md.track_id
|
||||
// WHERE
|
||||
// albums.id = tracks.album_id AND
|
||||
// tracks.visible = 1;
|
||||
|
||||
static const std::string REGULAR_PROPERTY_QUERY =
|
||||
"SELECT DISTINCT {{table}}.id, {{table}}.name "
|
||||
"FROM {{table}}, tracks "
|
||||
"{{extended_predicates}} "
|
||||
"WHERE {{table}}.id=tracks.{{fk_id}} AND tracks.visible=1 "
|
||||
"{{regular_predicates}} "
|
||||
"{{regular_filter}} "
|
||||
"ORDER BY {{table}}.sort_order";
|
||||
|
||||
/* REGULAR_PROPERTY_QUERY is similar to REGULAR_PROPERTY_QUERY, but is used to
|
||||
retrieve non-standard metadata fields. it's slower, has (potentially) more joins,
|
||||
and is generally more difficult to use. here's an example where we select all
|
||||
"years" for a particular artist "595" where the composer is "J. Cantrell": */
|
||||
|
||||
// SELECT DISTINCT meta_value_id, value
|
||||
// FROM extended_metadata
|
||||
// INNER JOIN(
|
||||
// SELECT id AS track_id
|
||||
// FROM tracks
|
||||
// WHERE visual_artist_id = 595
|
||||
// ) AS reg ON extended_metadata.id = reg.track_id
|
||||
// INNER JOIN(
|
||||
// SELECT id AS track_id
|
||||
// FROM extended_metadata
|
||||
// WHERE
|
||||
// (key = "composer" AND value = "J. Cantrell")
|
||||
// GROUP BY track_id
|
||||
// HAVING COUNT(track_id) = 1
|
||||
// ) AS md ON extended_metadata.id = md.track_id
|
||||
// WHERE
|
||||
// extended_metadata.key = "year";
|
||||
|
||||
static const std::string EXTENDED_PROPERTY_QUERY =
|
||||
"SELECT DISTINCT meta_value_id, value "
|
||||
"FROM extended_metadata "
|
||||
"INNER JOIN ( "
|
||||
" SELECT id AS track_id "
|
||||
" FROM tracks "
|
||||
" WHERE tracks.visible=1 {{regular_predicates}} "
|
||||
") AS reg on extended_metadata.id=reg.track_id "
|
||||
"{{extended_predicates}} "
|
||||
"WHERE extended_metadata.key=? "
|
||||
"{{extended_filter}} "
|
||||
"ORDER BY extended_metadata.value ASC";
|
||||
|
||||
using Predicate = std::pair<std::string, int64_t>;
|
||||
using PredicateList = std::vector<Predicate>;
|
||||
struct Argument { virtual void Bind(Statement& stmt, int pos) const = 0; };
|
||||
using ArgumentList = std::vector<std::shared_ptr<Argument>>;
|
||||
|
||||
extern PropertyType GetPropertyType(const std::string& key);
|
||||
|
||||
extern std::shared_ptr<Argument> IdArgument(int64_t);
|
||||
extern std::shared_ptr<Argument> StringArgument(const std::string);
|
||||
|
||||
extern void SplitPredicates(
|
||||
const PredicateList& input,
|
||||
PredicateList& regular,
|
||||
PredicateList& extended);
|
||||
|
||||
extern std::string JoinRegular(
|
||||
const PredicateList& pred,
|
||||
ArgumentList& args,
|
||||
const std::string& prefix = "");
|
||||
|
||||
extern std::string InnerJoinExtended(
|
||||
const PredicateList& pred, ArgumentList& args);
|
||||
|
||||
extern std::string JoinExtended(
|
||||
const PredicateList& pred, ArgumentList& args);
|
||||
|
||||
extern void Apply(
|
||||
musik::core::db::Statement& stmt,
|
||||
const ArgumentList& args);
|
||||
}
|
||||
|
||||
} } } }
|
Loading…
x
Reference in New Issue
Block a user