Added regex support to CategoryListQuery and SearchTrackListQuery, but

they're not wired up yet.
This commit is contained in:
casey langen 2020-11-07 20:19:56 -08:00
parent ba9b98794f
commit d4a1fba4b6
10 changed files with 69 additions and 24 deletions

View File

@ -305,6 +305,7 @@ ITrackList* LocalMetadataProxy::QueryTracks(const char* query, int limit, int of
std::shared_ptr<SearchTrackListQuery> search(
new SearchTrackListQuery(
this->library,
SearchTrackListQuery::MatchType::Substring,
std::string(query ? query : ""),
TrackSortType::Album));
@ -456,6 +457,7 @@ IValueList* LocalMetadataProxy::QueryCategoryWithPredicate(
std::shared_ptr<CategoryListQuery> search(
new CategoryListQuery(
CategoryListQuery::MatchType::Substring,
type,
{ field, predicateId },
std::string(filter ? filter : "")));
@ -480,7 +482,10 @@ IValueList* LocalMetadataProxy::QueryCategoryWithPredicates(
auto predicateList = toPredicateList(predicates, predicateCount);
auto query = std::make_shared<CategoryListQuery>(
type, predicateList, std::string(filter ? filter : ""));
CategoryListQuery::MatchType::Substring,
type,
predicateList,
std::string(filter ? filter : ""));
this->library->EnqueueAndWait(query);

View File

@ -50,6 +50,11 @@ namespace musik { namespace core { namespace library { namespace query {
public sigslot::has_slots<>
{
public:
enum class MatchType : int {
Substring = 1,
Regex = 2
};
QueryBase()
: status(IQuery::Idle)
, options(0)

View File

@ -56,35 +56,44 @@ static const std::string kUnfilteredPlaylistsQuery =
static const std::string kFilteredPlaylistsQuery =
"SELECT DISTINCT id, name "
"FROM playlists"
"WHERE LOWER(name) LIKE LOWER(?) "
"WHERE LOWER(name) {{match_type}} ? "
"ORDER BY name;";
const std::string CategoryListQuery::kQueryName = "CategoryListQuery";
static std::string getMatchType(CategoryListQuery::MatchType matchType) {
return matchType == CategoryListQuery::MatchType::Regex ? "REGEXP" : "LIKE";
}
CategoryListQuery::CategoryListQuery() {
}
CategoryListQuery::CategoryListQuery(
const std::string& trackField, const std::string& filter)
: CategoryListQuery(trackField, category::PredicateList(), filter) {
MatchType matchType,
const std::string& trackField,
const std::string& filter)
: CategoryListQuery(matchType, trackField, category::PredicateList(), filter) {
}
CategoryListQuery::CategoryListQuery(
MatchType matchType,
const std::string& trackField,
const category::Predicate predicate,
const std::string& filter)
: CategoryListQuery(trackField, category::PredicateList{ predicate }, filter) {
: CategoryListQuery(matchType, trackField, category::PredicateList{ predicate }, filter) {
}
CategoryListQuery::CategoryListQuery(
MatchType matchType,
const std::string& trackField,
const category::PredicateList predicates,
const std::string& filter)
: trackField(trackField)
: matchType(MatchType::Regex)
, trackField(trackField)
, filter(filter) {
result.reset(new SdkValueList());
if (this->filter.size()) {
if (this->filter.size() && this->matchType == MatchType::Substring) {
/* transform "FilteR" => "%filter%" */
std::string wild = this->filter;
std::transform(wild.begin(), wild.end(), wild.begin(), tolower);
@ -134,6 +143,8 @@ void CategoryListQuery::QueryPlaylist(musik::core::db::Connection& db) {
? kFilteredPlaylistsQuery
: kUnfilteredPlaylistsQuery;
category::ReplaceAll(query, "{{match_type}}", getMatchType(matchType));
Statement stmt(query.c_str() , db);
if (filtered) {
@ -157,6 +168,7 @@ void CategoryListQuery::QueryRegular(musik::core::db::Connection &db) {
if (this->filter.size()) {
regularFilter = category::REGULAR_FILTER;
category::ReplaceAll(regularFilter, "{{table}}", prop.first);
category::ReplaceAll(regularFilter, "{{match_type}}", getMatchType(matchType));
args.push_back(category::StringArgument(this->filter));
}
@ -184,6 +196,7 @@ void CategoryListQuery::QueryExtended(musik::core::db::Connection &db) {
if (this->filter.size()) {
extendedFilter = category::EXTENDED_FILTER;
args.push_back(category::StringArgument(this->filter));
category::ReplaceAll(extendedFilter, "{{match_type}}", getMatchType(matchType));
}
category::ReplaceAll(query, "{{regular_predicates}}", regular);
@ -242,6 +255,7 @@ std::string CategoryListQuery::SerializeQuery() {
query["options"] = {
{ "trackField", this->trackField },
{ "filter", this->filter },
{ "matchType", this->matchType },
{ "outputType", this->outputType },
{ "regularPredicateList", PredicateListToJson(this->regular) },
{ "extendedPredicateList", PredicateListToJson(this->extended) }
@ -269,6 +283,7 @@ std::shared_ptr<CategoryListQuery> CategoryListQuery::DeserializeQuery(const std
std::shared_ptr<CategoryListQuery> result(new CategoryListQuery());
result->trackField = options.value("trackField", "");
result->filter = options.value("filter", "");
result->matchType = options.value("matchType", MatchType::Substring);
result->outputType = (OutputType)options.value("outputType", OutputType::Regular);
PredicateListFromJson(options["regularPredicateList"], result->regular);
PredicateListFromJson(options["extendedPredicateList"], result->extended);

View File

@ -52,15 +52,18 @@ namespace musik { namespace core { namespace library { namespace query {
using Result = SdkValueList::Shared;
CategoryListQuery(
MatchType matchType,
const std::string& trackField,
const std::string& filter = "");
CategoryListQuery(
MatchType matchType,
const std::string& trackField,
const category::Predicate predicate,
const std::string& filter = "");
CategoryListQuery(
MatchType matchType,
const std::string& trackField,
const category::PredicateList predicate,
const std::string& filter = "");
@ -100,6 +103,7 @@ namespace musik { namespace core { namespace library { namespace query {
std::string trackField;
std::string filter;
MatchType matchType;
OutputType outputType;
category::PredicateList regular, extended;
Result result;

View File

@ -61,9 +61,13 @@ using namespace boost::algorithm;
const std::string SearchTrackListQuery::kQueryName = "SearchTrackListQuery";
SearchTrackListQuery::SearchTrackListQuery(
ILibraryPtr library, const std::string& filter, TrackSortType sort)
ILibraryPtr library,
MatchType matchType,
const std::string& filter,
TrackSortType sort)
{
this->library = library;
this->matchType = matchType;
this->sortType = sort;
this->filter = filter;
@ -105,11 +109,10 @@ bool SearchTrackListQuery::OnRun(Connection& db) {
headers.reset(new std::set<size_t>());
}
bool useRegex = (matchType == MatchType::Regex);
bool hasFilter = (this->filter.size() > 0);
std::string lastAlbum;
size_t index = 0;
std::string query;
if (hasFilter) {
@ -119,9 +122,11 @@ bool SearchTrackListQuery::OnRun(Connection& db) {
"WHERE "
" tracks.visible=1 AND "
+ this->orderByPredicate +
"(tracks.title LIKE ? OR al.name LIKE ? OR ar.name LIKE ? OR gn.name LIKE ?) "
"(tracks.title {{match_type}} ? OR al.name {{match_type}} ? OR ar.name {{match_type}} ? OR gn.name {{match_type}} ?) "
" AND tracks.album_id=al.id AND tracks.visual_genre_id=gn.id AND tracks.visual_artist_id=ar.id "
"ORDER BY " + this->orderBy + " ";
ReplaceAll(query, "{{match_type}}", useRegex ? "REGEXP" : "LIKE");
}
else {
query =
@ -139,11 +144,13 @@ bool SearchTrackListQuery::OnRun(Connection& db) {
Statement trackQuery(query.c_str(), db);
if (hasFilter) {
std::string filterWithWildcard = "%" + trim_copy(to_lower_copy(filter)) + "%";
trackQuery.BindText(0, filterWithWildcard);
trackQuery.BindText(1, filterWithWildcard);
trackQuery.BindText(2, filterWithWildcard);
trackQuery.BindText(3, filterWithWildcard);
std::string patternToMatch = useRegex
? filter : "%" + trim_copy(to_lower_copy(filter)) + "%";
trackQuery.BindText(0, patternToMatch);
trackQuery.BindText(1, patternToMatch);
trackQuery.BindText(2, patternToMatch);
trackQuery.BindText(3, patternToMatch);
}
while (trackQuery.Step() == Row) {
@ -173,7 +180,8 @@ std::string SearchTrackListQuery::SerializeQuery() {
{ "name", kQueryName },
{ "options", {
{ "filter", filter },
{ "sortType", sortType}
{ "matchType", matchType },
{ "sortType", sortType }
}}
};
return FinalizeSerializedQueryWithLimitAndOffset(output);
@ -194,6 +202,7 @@ std::shared_ptr<SearchTrackListQuery> SearchTrackListQuery::DeserializeQuery(mus
auto options = nlohmann::json::parse(data)["options"];
auto result = std::make_shared<SearchTrackListQuery>(
library,
options.value("matchType", MatchType::Substring),
options["filter"].get<std::string>(),
options["sortType"].get<TrackSortType>());
result->ExtractLimitAndOffsetFromDeserializedQuery(options);

View File

@ -45,6 +45,7 @@ namespace musik { namespace core { namespace library { namespace query {
SearchTrackListQuery(
musik::core::ILibraryPtr library,
MatchType matchType,
const std::string& filter,
TrackSortType sort);
@ -70,6 +71,7 @@ namespace musik { namespace core { namespace library { namespace query {
private:
musik::core::ILibraryPtr library;
MatchType matchType;
bool parseHeaders;
std::string orderBy;
std::string orderByPredicate;

View File

@ -71,10 +71,10 @@ namespace musik { namespace core { namespace library { namespace query {
};
static const std::string REGULAR_PREDICATE = " tracks.{{fk_id}}=? ";
static const std::string REGULAR_FILTER = " AND LOWER({{table}}.name) LIKE ? ";
static const std::string REGULAR_FILTER = " AND LOWER({{table}}.name) {{match_type}} ? ";
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_FILTER = " AND LOWER(extended_metadata.value) {{match_type}} ?";
static const std::string EXTENDED_INNER_JOIN =
"INNER JOIN ( "
@ -193,7 +193,7 @@ namespace musik { namespace core { namespace library { namespace query {
and other supplementary information. */
static const std::string ALBUM_LIST_FILTER =
" AND (LOWER(album) like ? OR LOWER(album_artist) like ?) ";
" AND (LOWER(album) LIKE ? OR LOWER(album_artist) LIKE ?) ";
static const std::string ALBUM_LIST_QUERY =
"SELECT DISTINCT "

View File

@ -149,7 +149,11 @@ void TrackSearchLayout::Requery() {
const std::string& filter = this->input->GetText();
const TrackSortType sortOrder = getDefaultTrackSort(this->prefs);
this->trackList->Requery(std::shared_ptr<TrackListQueryBase>(
new SearchTrackListQuery(this->library, filter, sortOrder)));
new SearchTrackListQuery(
this->library,
SearchTrackListQuery::MatchType::Substring,
filter,
sortOrder)));
}
void TrackSearchLayout::PlayFromTop() {

View File

@ -151,8 +151,8 @@ static void queryPlaylists(
ILibraryPtr library,
std::function<void(std::shared_ptr<CategoryListQuery>)> callback)
{
std::shared_ptr<CategoryListQuery> query(
new CategoryListQuery(Playlists::TABLE_NAME, ""));
std::shared_ptr<CategoryListQuery> query(new CategoryListQuery(
CategoryListQuery::MatchType::Substring, Playlists::TABLE_NAME, ""));
library->Enqueue(query, [callback, query](auto q) {
callback(query->GetStatus() == IQuery::Finished

View File

@ -109,7 +109,8 @@ void CategoryListView::RequeryWithField(
this->fieldIdColumn = getFieldIdColumn(fieldName);
this->selectAfterQuery = selectAfterQuery;
this->filter = filter;
this->activeQuery.reset(new CategoryListQuery(fieldName, filter));
this->activeQuery.reset(new CategoryListQuery(
CategoryListQuery::MatchType::Substring, fieldName, filter));
this->library->Enqueue(activeQuery);
}