commit bbed448ccbaaead5cc12f99424dcf79299f88a3a
parent 3fb4d81d48356cf5d078238576b856983b8ce193
Author: William Casarin <jb55@jb55.com>
Date: Fri, 16 Aug 2024 12:17:00 -0700
nostrdb: ndb_filter_from_json
Changelog-Added: Add method for parsing filter json
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
2 files changed, 297 insertions(+), 5 deletions(-)
diff --git a/nostrdb/src/nostrdb.c b/nostrdb/src/nostrdb.c
@@ -726,7 +726,8 @@ static int ndb_filter_start_field_impl(struct ndb_filter *filter, enum ndb_filte
for (i = 0; i < filter->num_elements; i++) {
el = ndb_filter_get_elements(filter, i);
assert(el);
- if (el->field.type == field) {
+ // TODO: fix this tags check to try to find matching tags
+ if (el->field.type == field && field != NDB_FILTER_TAGS) {
fprintf(stderr, "ndb_filter_start_field: field '%s' already exists\n",
ndb_filter_field_name(field));
return 0;
@@ -796,7 +797,9 @@ static int ndb_filter_add_element(struct ndb_filter *filter, union ndb_filter_el
return 0;
break;
case NDB_ELEMENT_STRING:
- if (!cursor_push_c_str(&filter->data_buf, el.string))
+ if (!cursor_push(&filter->data_buf, (unsigned char *)el.string.string, el.string.len))
+ return 0;
+ if (!cursor_push_byte(&filter->data_buf, 0))
return 0;
break;
case NDB_ELEMENT_INT:
@@ -840,7 +843,8 @@ static int ndb_filter_set_elem_type(struct ndb_filter *filter,
return 1;
}
-int ndb_filter_add_str_element(struct ndb_filter *filter, const char *str)
+
+int ndb_filter_add_str_element_len(struct ndb_filter *filter, const char *str, int len)
{
union ndb_filter_element el;
struct ndb_filter_elements *current;
@@ -864,10 +868,17 @@ int ndb_filter_add_str_element(struct ndb_filter *filter, const char *str)
if (!ndb_filter_set_elem_type(filter, NDB_ELEMENT_STRING))
return 0;
- el.string = str;
+ el.string.string = str;
+ el.string.len = len;
+
return ndb_filter_add_element(filter, el);
}
+int ndb_filter_add_str_element(struct ndb_filter *filter, const char *str)
+{
+ return ndb_filter_add_str_element_len(filter, str, strlen(str));
+}
+
int ndb_filter_add_int_element(struct ndb_filter *filter, uint64_t integer)
{
union ndb_filter_element el;
@@ -5460,6 +5471,260 @@ int ndb_ws_event_from_json(const char *json, int len, struct ndb_tce *tce,
return 0;
}
+static enum ndb_filter_fieldtype
+ndb_filter_parse_field(const char *tok, int len, char *tagchar)
+{
+ *tagchar = 0;
+
+ if (len == 0)
+ return 0;
+
+ if (len == 7 && !strncmp(tok, "authors", 7)) {
+ return NDB_FILTER_AUTHORS;
+ } else if (len == 3 && !strncmp(tok, "ids", 3)) {
+ return NDB_FILTER_IDS;
+ } else if (len == 5 && !strncmp(tok, "kinds", 5)) {
+ return NDB_FILTER_KINDS;
+ } else if (len == 2 && tok[0] == '#') {
+ *tagchar = tok[1];
+ return NDB_FILTER_TAGS;
+ } else if (len == 5 && !strncmp(tok, "since", 5)) {
+ return NDB_FILTER_SINCE;
+ } else if (len == 5 && !strncmp(tok, "until", 5)) {
+ return NDB_FILTER_UNTIL;
+ } else if (len == 5 && !strncmp(tok, "limit", 5)) {
+ return NDB_FILTER_LIMIT;
+ }
+
+ return 0;
+}
+
+static int ndb_filter_parse_json_ids(struct ndb_json_parser *parser,
+ struct ndb_filter *filter)
+{
+ jsmntok_t *tok;
+ const char *start;
+ unsigned char hexbuf[32];
+ int tok_len, i, size;
+
+ tok = &parser->toks[parser->i++];
+
+ if (tok->type != JSMN_ARRAY) {
+ ndb_debug("parse_json_ids: not an array\n");
+ return 0;
+ }
+
+ size = tok->size;
+
+ for (i = 0; i < size; parser->i++, i++) {
+ tok = &parser->toks[parser->i];
+ start = parser->json + tok->start;
+ tok_len = toksize(tok);
+
+ if (tok->type != JSMN_STRING) {
+ ndb_debug("parse_json_ids: not a string '%d'\n", tok->type);
+ return 0;
+ }
+
+ if (tok_len != 64) {
+ ndb_debug("parse_json_ids: not len 64: '%.*s'\n", tok_len, start);
+ return 0;
+ }
+
+ // id
+ if (!hex_decode(start, tok_len, hexbuf, sizeof(hexbuf))) {
+ ndb_debug("parse_json_ids: hex decode failed\n");
+ return 0;
+ }
+
+ ndb_debug("adding id elem\n");
+ if (!ndb_filter_add_id_element(filter, hexbuf)) {
+ ndb_debug("parse_json_ids: failed to add id element\n");
+ return 0;
+ }
+ }
+
+ parser->i--;
+ return 1;
+}
+
+static int ndb_filter_parse_json_elems(struct ndb_json_parser *parser,
+ struct ndb_filter *filter)
+{
+ jsmntok_t *tok;
+ const char *start;
+ int tok_len;
+ unsigned char hexbuf[32];
+ enum ndb_generic_element_type typ;
+ tok = NULL;
+ int i, size;
+
+ tok = &parser->toks[parser->i++];
+
+ if (tok->type != JSMN_ARRAY)
+ return 0;
+
+ size = tok->size;
+
+ for (i = 0; i < size; i++, parser->i++) {
+ tok = &parser->toks[parser->i];
+ start = parser->json + tok->start;
+ tok_len = toksize(tok);
+
+ if (tok->type != JSMN_STRING)
+ return 0;
+
+ if (i == 0) {
+ if (tok_len == 64 && hex_decode(start, 64, hexbuf, sizeof(hexbuf))) {
+ typ = NDB_ELEMENT_ID;
+ if (!ndb_filter_add_id_element(filter, hexbuf)) {
+ ndb_debug("failed to push id elem\n");
+ return 0;
+ }
+ } else {
+ typ = NDB_ELEMENT_STRING;
+ if (!ndb_filter_add_str_element_len(filter, start, tok_len))
+ return 0;
+ }
+ } else if (typ == NDB_ELEMENT_ID) {
+ if (!hex_decode(start, 64, hexbuf, sizeof(hexbuf)))
+ return 0;
+ if (!ndb_filter_add_id_element(filter, hexbuf))
+ return 0;
+ } else if (typ == NDB_ELEMENT_STRING) {
+ if (!ndb_filter_add_str_element_len(filter, start, tok_len))
+ return 0;
+ } else {
+ // ???
+ return 0;
+ }
+ }
+
+ parser->i--;
+ return 1;
+}
+
+static int ndb_filter_parse_json_int(struct ndb_json_parser *parser,
+ struct ndb_filter *filter)
+{
+ jsmntok_t *tok;
+ const char *start;
+ int tok_len;
+ unsigned int value;
+
+ tok = &parser->toks[parser->i];
+ start = parser->json + tok->start;
+ tok_len = toksize(tok);
+
+ if (tok->type != JSMN_PRIMITIVE)
+ return 0;
+
+ if (!parse_unsigned_int(start, tok_len, &value))
+ return 0;
+
+ if (!ndb_filter_add_int_element(filter, (uint64_t)value))
+ return 0;
+
+ ndb_debug("added int elem %d\n", value);
+
+ return 1;
+}
+
+
+static int ndb_filter_parse_json_ints(struct ndb_json_parser *parser,
+ struct ndb_filter *filter)
+{
+ jsmntok_t *tok;
+ int size, i;
+
+ tok = &parser->toks[parser->i++];
+
+ if (tok->type != JSMN_ARRAY)
+ return 0;
+
+ size = tok->size;
+
+ for (i = 0; i < size; parser->i++, i++) {
+ if (!ndb_filter_parse_json_int(parser, filter))
+ return 0;
+ }
+
+ parser->i--;
+ return 1;
+}
+
+
+static int ndb_filter_parse_json(struct ndb_json_parser *parser,
+ struct ndb_filter *filter)
+{
+ jsmntok_t *tok = NULL;
+ const char *json = parser->json;
+ const char *start;
+ char tag;
+ int tok_len;
+ enum ndb_filter_fieldtype field;
+
+ if (parser->toks[parser->i++].type != JSMN_OBJECT)
+ return 0;
+
+ for (; parser->i < parser->num_tokens; parser->i++) {
+ tok = &parser->toks[parser->i++];
+ start = json + tok->start;
+ tok_len = toksize(tok);
+
+ if (!(field = ndb_filter_parse_field(start, tok_len, &tag))) {
+ ndb_debug("failed field '%.*s'\n", tok_len, start);
+ continue;
+ }
+
+ if (tag) {
+ ndb_debug("starting tag field '%c'\n", tag);
+ if (!ndb_filter_start_tag_field(filter, tag)) {
+ ndb_debug("failed to start tag field '%c'\n", tag);
+ return 0;
+ }
+ } else if (!ndb_filter_start_field(filter, field)) {
+ ndb_debug("field already started\n");
+ return 0;
+ }
+
+ // we parsed a top-level field
+ switch(field) {
+ case NDB_FILTER_AUTHORS:
+ case NDB_FILTER_IDS:
+ if (!ndb_filter_parse_json_ids(parser, filter)) {
+ ndb_debug("failed to parse filter ids/authors\n");
+ return 0;
+ }
+ break;
+ case NDB_FILTER_SINCE:
+ case NDB_FILTER_UNTIL:
+ case NDB_FILTER_LIMIT:
+ if (!ndb_filter_parse_json_int(parser, filter)) {
+ ndb_debug("failed to parse filter since/until/limit\n");
+ return 0;
+ }
+ break;
+ case NDB_FILTER_KINDS:
+ if (!ndb_filter_parse_json_ints(parser, filter)) {
+ ndb_debug("failed to parse filter kinds\n");
+ return 0;
+ }
+ break;
+ case NDB_FILTER_TAGS:
+ if (!ndb_filter_parse_json_elems(parser, filter)) {
+ ndb_debug("failed to parse filter tags\n");
+ return 0;
+ }
+ break;
+ }
+
+ ndb_filter_end_field(filter);
+ }
+
+ return ndb_filter_end(filter);
+}
+
int ndb_parse_json_note(struct ndb_json_parser *parser, struct ndb_note **note)
{
jsmntok_t *tok = NULL;
@@ -5557,6 +5822,25 @@ int ndb_parse_json_note(struct ndb_json_parser *parser, struct ndb_note **note)
return ndb_builder_finalize(&parser->builder, note, NULL);
}
+int ndb_filter_from_json(const char *json, int len, struct ndb_filter *filter,
+ unsigned char *buf, int bufsize)
+{
+ struct ndb_json_parser parser;
+ int res;
+
+ if (filter->finalized)
+ return 0;
+
+ ndb_json_parser_init(&parser, json, len, buf, bufsize);
+ if ((res = ndb_json_parser_parse(&parser, NULL)) < 0)
+ return res;
+
+ if (parser.num_tokens < 1)
+ return 0;
+
+ return ndb_filter_parse_json(&parser, filter);
+}
+
int ndb_note_from_json(const char *json, int len, struct ndb_note **note,
unsigned char *buf, int bufsize)
{
diff --git a/nostrdb/src/nostrdb.h b/nostrdb/src/nostrdb.h
@@ -223,8 +223,13 @@ struct ndb_iterator {
int index;
};
-union ndb_filter_element {
+struct ndb_filter_string {
const char *string;
+ int len;
+};
+
+union ndb_filter_element {
+ struct ndb_filter_string string;
const unsigned char *id;
uint64_t integer;
};
@@ -491,6 +496,9 @@ int ndb_filter_add_id_element(struct ndb_filter *, const unsigned char *id);
int ndb_filter_add_int_element(struct ndb_filter *, uint64_t integer);
int ndb_filter_add_str_element(struct ndb_filter *, const char *str);
+// filters from json
+int ndb_filter_from_json(const char *, int len, struct ndb_filter *filter, unsigned char *buf, int bufsize);
+
// getting field elements
unsigned char *ndb_filter_get_id_element(const struct ndb_filter *, const struct ndb_filter_elements *, int index);
const char *ndb_filter_get_string_element(const struct ndb_filter *, const struct ndb_filter_elements *, int index);