Skip to content

Commit

Permalink
This commit adds the sort and window parameter to "sticker find"
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jcorporation committed Jan 16, 2024
1 parent 380e0ab commit 53c3512
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 42 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ ver 0.24 (not yet released)
- stickers on playlists and some tag types
- new command "stickernames"
- new "search"/"find" filter "added-since"
- "sticker find" supports sort and window parameter and new sticker compare operators "lt" and "gt"
* database
- attribute "added" shows when each song was added to the database
- proxy: require MPD 0.21 or later
Expand Down
8 changes: 5 additions & 3 deletions doc/protocol.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1470,19 +1470,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 "``lt``", "``gt``" to cast the value to an integer.

Examples:

Expand Down
48 changes: 44 additions & 4 deletions src/command/StickerCommands.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ class DomainHandler {
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,
Expand All @@ -97,6 +98,7 @@ class DomainHandler {
uri,
name,
op, value,
sort, descending, window,
callback, &data);

return CommandResult::OK;
Expand Down Expand Up @@ -172,14 +174,16 @@ class SongHandler final : public DomainHandler {
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,
};

sticker_song_find(sticker_database, database, uri, data.name,
op, value,
sort, descending, window,
sticker_song_find_print_cb, &data);

return CommandResult::OK;
Expand Down Expand Up @@ -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;
Expand All @@ -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");
Expand Down
128 changes: 98 additions & 30 deletions src/sticker/Database.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,34 @@
#include "util/StringCompare.hxx"
#include "util/ScopeExit.hxx"

#include <fmt/format.h>
#include <cassert>
#include <iterator>
#include <array>
#include <stdexcept>

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,
STICKER_SQL_UPDATE,
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,
Expand All @@ -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=?",
Expand All @@ -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",
Expand Down Expand Up @@ -297,33 +319,79 @@ 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);

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);
Expand All @@ -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](){
Expand Down
22 changes: 17 additions & 5 deletions src/sticker/Database.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include "Match.hxx"
#include "lib/sqlite/Database.hxx"
#include "protocol/RangeArg.hxx"

#include <sqlite3.h>

Expand All @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Loading

0 comments on commit 53c3512

Please sign in to comment.