This commit adds the sort and window parameter to "sticker find"

The three new compare operators "eq", "gt" and "lt" are casting the values to int.

Sort supports:
- uri: sort by uri
- value: sort by value as string
- value_int: casts value to int

Closes #1894
This commit is contained in:
jcorporation 2024-03-07 20:52:11 +01:00
parent 1efb9d41db
commit 45553c5f61
8 changed files with 187 additions and 42 deletions

1
NEWS
View File

@ -13,6 +13,7 @@ ver 0.24 (not yet released)
- new commands "stickernames" and "playlistlength"
- new "search"/"find" filter "added-since"
- allow range in listplaylist and listplaylistinfo
- "sticker find" supports sort and window parameter and new sticker compare operators "eq", "lt" and "gt"
* database
- attribute "added" shows when each song was added to the database
- proxy: require MPD 0.21 or later

View File

@ -1486,19 +1486,21 @@ the database for songs).
.. _command_sticker_find:
:command:`sticker find {TYPE} {URI} {NAME}`
:command:`sticker find {TYPE} {URI} {NAME} [sort {SORTTYPE}] [window {START:END}]`
Searches the sticker database for stickers with the
specified name, below the specified directory (URI).
For each matching song, it prints the URI and that one
sticker's value.
``sort`` sorts the result by "``uri``","``value`` or "``value_int``" (casts the sticker value to an integer). [#since_0_24]_
.. _command_sticker_find_value:
:command:`sticker find {TYPE} {URI} {NAME} = {VALUE}`
:command:`sticker find {TYPE} {URI} {NAME} = {VALUE} [sort {SORTTYPE}] [window {START:END}]`
Searches for stickers with the given value.
Other supported operators are:
"``<``", "``>``"
"``<``", "``>``" for strings and "``eq``", "``lt``", "``gt``" to cast the value to an integer.
Examples:

View File

@ -78,7 +78,8 @@ public:
return CommandResult::OK;
}
virtual CommandResult Find(const char *uri, const char *name, StickerOperator op, const char *value) {
virtual CommandResult Find(const char *uri, const char *name, StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window) {
auto data = CallbackContext{
.name = name,
.sticker_type = sticker_type,
@ -97,6 +98,7 @@ public:
uri,
name,
op, value,
sort, descending, window,
callback, &data);
return CommandResult::OK;
@ -172,7 +174,8 @@ public:
database.ReturnSong(song);
}
CommandResult Find(const char *uri, const char *name, StickerOperator op, const char *value) override {
CommandResult Find(const char *uri, const char *name, StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window) override {
struct sticker_song_find_data data = {
response,
name,
@ -180,6 +183,7 @@ public:
sticker_song_find(sticker_database, database, uri, data.name,
op, value,
sort, descending, window,
sticker_song_find_print_cb, &data);
return CommandResult::OK;
@ -386,7 +390,37 @@ handle_sticker(Client &client, Request args, Response &r)
return handler->Delete(uri, sticker_name);
/* find */
if ((args.size() == 4 || args.size() == 6) && StringIsEqual(cmd, "find")) {
if (args.size() >= 4 && StringIsEqual(cmd, "find")) {
RangeArg window = RangeArg::All();
if (args.size() >= 6 && StringIsEqual(args[args.size() - 2], "window")) {
window = args.ParseRange(args.size() - 1);
args.pop_back();
args.pop_back();
}
auto sort = "";
bool descending = false;
if (args.size() >= 6 && StringIsEqual(args[args.size() - 2], "sort")) {
const char *s = args.back();
if (*s == '-') {
descending = true;
++s;
}
if (StringIsEqual(s, "uri") ||
StringIsEqual(s, "value") ||
StringIsEqual(s, "value_int")
) {
sort = s;
}
else {
r.FmtError(ACK_ERROR_ARG, "Unknown sort tag \"{}\"", s);
return CommandResult::ERROR;
}
args.pop_back();
args.pop_back();
}
bool has_op = args.size() > 4;
auto value = has_op ? args[5] : nullptr;
StickerOperator op = StickerOperator::EXISTS;
@ -399,12 +433,18 @@ handle_sticker(Client &client, Request args, Response &r)
op = StickerOperator::LESS_THAN;
else if (StringIsEqual(op_s, ">"))
op = StickerOperator::GREATER_THAN;
else if (StringIsEqual(op_s, "eq"))
op = StickerOperator::EQUALS_INT;
else if (StringIsEqual(op_s, "lt"))
op = StickerOperator::LESS_THAN_INT;
else if (StringIsEqual(op_s, "gt"))
op = StickerOperator::GREATER_THAN_INT;
else {
r.FmtError(ACK_ERROR_ARG, "bad operator \"{}\"", op_s);
return CommandResult::ERROR;
}
}
return handler->Find(uri, sticker_name, op, value);
return handler->Find(uri, sticker_name, op, value, sort, descending, window);
}
r.Error(ACK_ERROR_ARG, "bad request");

View File

@ -10,6 +10,7 @@
#include "util/StringCompare.hxx"
#include "util/ScopeExit.hxx"
#include <fmt/format.h>
#include <cassert>
#include <iterator>
#include <array>
@ -17,6 +18,19 @@
using namespace Sqlite;
enum sticker_sql_find {
STICKER_SQL_FIND,
STICKER_SQL_FIND_VALUE,
STICKER_SQL_FIND_LT,
STICKER_SQL_FIND_GT,
STICKER_SQL_FIND_EQ_INT,
STICKER_SQL_FIND_LT_INT,
STICKER_SQL_FIND_GT_INT,
STICKER_SQL_FIND_COUNT
};
enum sticker_sql {
STICKER_SQL_GET,
STICKER_SQL_LIST,
@ -24,10 +38,6 @@ enum sticker_sql {
STICKER_SQL_INSERT,
STICKER_SQL_DELETE,
STICKER_SQL_DELETE_VALUE,
STICKER_SQL_FIND,
STICKER_SQL_FIND_VALUE,
STICKER_SQL_FIND_LT,
STICKER_SQL_FIND_GT,
STICKER_SQL_DISTINCT_TYPE_URI,
STICKER_SQL_TRANSACTION_BEGIN,
STICKER_SQL_TRANSACTION_COMMIT,
@ -37,6 +47,29 @@ enum sticker_sql {
STICKER_SQL_COUNT
};
static constexpr auto sticker_sql_find = std::array {
//[STICKER_SQL_FIND] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=?",
//[STICKER_SQL_FIND_VALUE] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value=?",
//[STICKER_SQL_FIND_LT] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value<?",
//[STICKER_SQL_FIND_GT] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value>?",
//[STICKER_SQL_FIND_EQ_INT] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND CAST(value AS INT)=?",
//[STICKER_SQL_FIND_LT_INT] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND CAST(value AS INT)<?",
//[STICKER_SQL_FIND_GT_INT] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND CAST(value AS INT)>?",
};
static constexpr auto sticker_sql = std::array {
//[STICKER_SQL_GET] =
"SELECT value FROM sticker WHERE type=? AND uri=? AND name=?",
@ -50,17 +83,6 @@ static constexpr auto sticker_sql = std::array {
"DELETE FROM sticker WHERE type=? AND uri=?",
//[STICKER_SQL_DELETE_VALUE] =
"DELETE FROM sticker WHERE type=? AND uri=? AND name=?",
//[STICKER_SQL_FIND] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=?",
//[STICKER_SQL_FIND_VALUE] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value=?",
//[STICKER_SQL_FIND_LT] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value<?",
//[STICKER_SQL_FIND_GT] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value>?",
//[STICKER_SQL_DISTINCT_TYPE_URI] =
"SELECT DISTINCT type,uri FROM sticker",
@ -297,7 +319,8 @@ StickerDatabase::Load(const char *type, const char *uri)
sqlite3_stmt *
StickerDatabase::BindFind(const char *type, const char *base_uri,
const char *name,
StickerOperator op, const char *value)
StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window)
{
assert(type != nullptr);
assert(name != nullptr);
@ -305,25 +328,70 @@ StickerDatabase::BindFind(const char *type, const char *base_uri,
if (base_uri == nullptr)
base_uri = "";
auto order_by = StringIsEmpty(sort)
? std::string()
: StringIsEqual(sort, "value_int")
? fmt::format("ORDER BY CAST(value AS INT) {}", descending ? "desc" : "asc")
: fmt::format("ORDER BY {} {}", sort, descending ? "desc" : "asc");
auto offset = window.IsAll()
? std::string()
: window.IsOpenEnded()
? fmt::format("LIMIT -1 OFFSET {}", window.start)
: fmt::format("LIMIT {} OFFSET {}", window.Count(), window.start);
std::string sql_str;
sqlite3_stmt *sql;
switch (op) {
case StickerOperator::EXISTS:
BindAll(stmt[STICKER_SQL_FIND], type, base_uri, name);
return stmt[STICKER_SQL_FIND];
sql_str = fmt::format("{} {} {}",
sticker_sql_find[STICKER_SQL_FIND], order_by, offset);
sql = Prepare(db, sql_str.c_str());
BindAll(sql, type, base_uri, name);
return sql;
case StickerOperator::EQUALS:
BindAll(stmt[STICKER_SQL_FIND_VALUE],
type, base_uri, name, value);
return stmt[STICKER_SQL_FIND_VALUE];
sql_str = fmt::format("{} {} {}",
sticker_sql_find[STICKER_SQL_FIND_VALUE], order_by, offset);
sql = Prepare(db, sql_str.c_str());
BindAll(sql, type, base_uri, name, value);
return sql;
case StickerOperator::LESS_THAN:
BindAll(stmt[STICKER_SQL_FIND_LT],
type, base_uri, name, value);
return stmt[STICKER_SQL_FIND_LT];
sql_str = fmt::format("{} {} {}",
sticker_sql_find[STICKER_SQL_FIND_LT], order_by, offset);
sql = Prepare(db, sql_str.c_str());
BindAll(sql, type, base_uri, name, value);
return sql;
case StickerOperator::GREATER_THAN:
BindAll(stmt[STICKER_SQL_FIND_GT],
type, base_uri, name, value);
return stmt[STICKER_SQL_FIND_GT];
sql_str = fmt::format("{} {} {}",
sticker_sql_find[STICKER_SQL_FIND_GT], order_by, offset);
sql = Prepare(db, sql_str.c_str());
BindAll(sql, type, base_uri, name, value);
return sql;
case StickerOperator::EQUALS_INT:
sql_str = fmt::format("{} {} {}",
sticker_sql_find[STICKER_SQL_FIND_EQ_INT], order_by, offset);
sql = Prepare(db, sql_str.c_str());
BindAll(sql, type, base_uri, name, value);
return sql;
case StickerOperator::LESS_THAN_INT:
sql_str = fmt::format("{} {} {}",
sticker_sql_find[STICKER_SQL_FIND_LT_INT], order_by, offset);
sql = Prepare(db, sql_str.c_str());
BindAll(sql, type, base_uri, name, value);
return sql;
case StickerOperator::GREATER_THAN_INT:
sql_str = fmt::format("{} {} {}",
sticker_sql_find[STICKER_SQL_FIND_GT_INT], order_by, offset);
sql = Prepare(db, sql_str.c_str());
BindAll(sql, type, base_uri, name, value);
return sql;
}
assert(false);
@ -333,18 +401,18 @@ StickerDatabase::BindFind(const char *type, const char *base_uri,
void
StickerDatabase::Find(const char *type, const char *base_uri, const char *name,
StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window,
void (*func)(const char *uri, const char *value,
void *user_data),
void *user_data)
{
assert(func != nullptr);
sqlite3_stmt *const s = BindFind(type, base_uri, name, op, value);
sqlite3_stmt *const s = BindFind(type, base_uri, name, op, value, sort, descending, window);
assert(s != nullptr);
AtScopeExit(s) {
sqlite3_reset(s);
sqlite3_clear_bindings(s);
sqlite3_finalize(s);
};
ExecuteForEach(s, [s, func, user_data](){

View File

@ -28,6 +28,7 @@
#include "Match.hxx"
#include "lib/sqlite/Database.hxx"
#include "protocol/RangeArg.hxx"
#include <sqlite3.h>
@ -46,10 +47,6 @@ class StickerDatabase {
SQL_INSERT,
SQL_DELETE,
SQL_DELETE_VALUE,
SQL_FIND,
SQL_FIND_VALUE,
SQL_FIND_LT,
SQL_FIND_GT,
SQL_DISTINCT_TYPE_URI,
SQL_TRANSACTION_BEGIN,
SQL_TRANSACTION_COMMIT,
@ -59,6 +56,19 @@ class StickerDatabase {
SQL_COUNT
};
enum SQL_FIND {
SQL_FIND,
SQL_FIND_VALUE,
SQL_FIND_LT,
SQL_FIND_GT,
SQL_FIND_EQ_INT,
SQL_FIND_LT_INT,
SQL_FIND_GT_INT,
SQL_FIND_COUNT
};
std::string path;
Sqlite::Database db;
@ -143,6 +153,7 @@ public:
*/
void Find(const char *type, const char *base_uri, const char *name,
StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window,
void (*func)(const char *uri, const char *value,
void *user_data),
void *user_data);
@ -178,7 +189,8 @@ private:
sqlite3_stmt *BindFind(const char *type, const char *base_uri,
const char *name,
StickerOperator op, const char *value);
StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window);
};
#endif

View File

@ -28,6 +28,24 @@ enum class StickerOperator {
* value bigger than the specified one.
*/
GREATER_THAN,
/**
* Matches if a sticker with the specified name exists with a
* integer value equal the specified one.
*/
EQUALS_INT,
/**
* Matches if a sticker with the specified name exists with a
* integer value smaller than the specified one.
*/
LESS_THAN_INT,
/**
* Matches if a sticker with the specified name exists with a
* integer value bigger than the specified one.
*/
GREATER_THAN_INT,
};
#endif

View File

@ -92,6 +92,7 @@ void
sticker_song_find(StickerDatabase &sticker_database, const Database &db,
const char *base_uri, const char *name,
StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window,
void (*func)(const LightSong &song, const char *value,
void *user_data),
void *user_data)
@ -114,5 +115,6 @@ sticker_song_find(StickerDatabase &sticker_database, const Database &db,
data.base_uri_length = strlen(data.base_uri);
sticker_database.Find("song", data.base_uri, name, op, value,
sort, descending, window,
sticker_song_find_cb, &data);
}

View File

@ -5,6 +5,7 @@
#define MPD_SONG_STICKER_HXX
#include "Match.hxx"
#include "protocol/RangeArg.hxx"
#include <string>
@ -80,6 +81,7 @@ void
sticker_song_find(StickerDatabase &sticker_database, const Database &db,
const char *base_uri, const char *name,
StickerOperator op, const char *value,
const char *sort, bool descending, RangeArg window,
void (*func)(const LightSong &song, const char *value,
void *user_data),
void *user_data);