Add filtering of numerical cols to collection

CollectionFilterWidget: Updated the tooltip, to reflect the changes.
CollectionQuery: Add parsing for SQL operators and insert right SQL
"where" searches.
Song: Add list of numerical columns
playlistfilterparser.cpp/FilterParser: move time and rating parsing
functions to new file:
searchparserutils.cpp: Contains common code used to parse search terms
in playlist and collection filters.
This commit is contained in:
Dakes
2023-07-31 13:44:08 +02:00
committed by Jonas Kvinge
parent 82a8a890de
commit 7aa7cdf6f3
12 changed files with 266 additions and 99 deletions

View File

@@ -36,6 +36,7 @@
#include "collectionquery.h"
#include "collectionfilteroptions.h"
#include "utilities/searchparserutils.h"
CollectionQuery::CollectionQuery(const QSqlDatabase &db, const QString &songs_table, const QString &fts_table, const CollectionFilterOptions &filter_options)
: QSqlQuery(db),
@@ -78,6 +79,29 @@ CollectionQuery::CollectionQuery(const QSqlDatabase &db, const QString &songs_ta
query += "fts" + columntoken + "\"" + subtoken + "\"*";
}
}
else if (Song::kNumericalColumns.contains(token.section(':', 0, 0), Qt::CaseInsensitive)) {
// Account for multiple colons.
QString columntoken = token.section(':', 0, 0);
QString subtoken = token.section(':', 1, -1);
subtoken = subtoken.trimmed();
if (!subtoken.isEmpty()) {
QString comparator = RemoveSqlOperator(subtoken);
if (columntoken.compare("rating", Qt::CaseInsensitive) == 0) {
subtoken.replace(":", " ");
AddWhereRating(subtoken, comparator);
}
else if (columntoken.compare("length", Qt::CaseInsensitive) == 0) {
// time is saved in nanoseconds, so add 9 0's
QString parsedTime = QString::number(Utilities::ParseSearchTime(subtoken)) + "000000000";
AddWhere(columntoken, parsedTime, comparator);
}
else {
subtoken.replace(":", " ");
AddWhere(columntoken, subtoken, comparator);
}
}
}
// not a valid filter, remove
else {
token.replace(":", " ");
token = token.trimmed();
@@ -118,6 +142,23 @@ CollectionQuery::CollectionQuery(const QSqlDatabase &db, const QString &songs_ta
}
QString CollectionQuery::RemoveSqlOperator(QString &token) {
QString op = "=";
static QRegularExpression rxOp("^(=|<[>=]?|>=?|!=)");
QRegularExpressionMatch match = rxOp.match(token);
if (match.hasMatch()) {
op = match.captured(0);
}
token.remove(rxOp);
if (op == "!=") {
op = "<>";
}
return op;
}
void CollectionQuery::AddWhere(const QString &column, const QVariant &value, const QString &op) {
// Ignore 'literal' for IN
@@ -167,6 +208,37 @@ void CollectionQuery::AddWhereArtist(const QVariant &value) {
}
void CollectionQuery::AddWhereRating(const QVariant &value, const QString &op) {
float parsed_rating = Utilities::ParseSearchRating(value.toString());
// You can't query the database for a float, due to float precision errors,
// So we have to use a certain tolerance, so that the searched value is definetly included.
const float tolerance = 0.001;
if (op == "<") {
AddWhere("rating", parsed_rating-tolerance, "<");
}
else if (op == ">") {
AddWhere("rating", parsed_rating+tolerance, ">");
}
else if (op == "<=") {
AddWhere("rating", parsed_rating+tolerance, "<=");
}
else if (op == ">=") {
AddWhere("rating", parsed_rating-tolerance, ">=");
}
else if (op == "<>") {
where_clauses_ << QString("(rating<? OR rating>?)");
bound_values_ << parsed_rating - tolerance;
bound_values_ << parsed_rating + tolerance;
}
else /* (op == "=") */ {
AddWhere("rating", parsed_rating+tolerance, "<");
AddWhere("rating", parsed_rating-tolerance, ">");
}
}
void CollectionQuery::AddCompilationRequirement(const bool compilation) {
// The unary + is added to prevent sqlite from using the index idx_comp_artist.
// When joining with fts, sqlite 3.8 has a tendency to use this index and thereby nesting the tables in an order which gives very poor performance