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:
parent
1efb9d41db
commit
45553c5f61
1
NEWS
1
NEWS
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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](){
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue