nostrdb

an unfairly fast embedded nostr database backed by lmdb
git clone git://jb55.com/nostrdb
Log | Files | Refs | Submodules | README | LICENSE

commit 78f9ef06affe521fe7824df0817610d6f4f50b17
parent ab100d9e70018fb8984f13d308154a79c21c0df8
Author: William Casarin <jb55@jb55.com>
Date:   Sun,  3 Nov 2024 11:11:32 -0800

ndb_filter_is_subset_of

subset testing for filters. Can be used to see if one subset is
redundant in the presence of a another in the local relay model

Changelog-Added: Add ndb_filter_is_subset_of
Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Msrc/nostrdb.c | 41+++++++++++++++++++++++++++++++++++++++++
Msrc/nostrdb.h | 3+++
Mtest.c | 36++++++++++++++++++++++++++++++++++++
3 files changed, 80 insertions(+), 0 deletions(-)

diff --git a/src/nostrdb.c b/src/nostrdb.c @@ -2672,6 +2672,47 @@ ndb_filter_find_elements(struct ndb_filter *filter, enum ndb_filter_fieldtype ty return NULL; } +int ndb_filter_is_subset_of(struct ndb_filter *a, struct ndb_filter *b) +{ + int i; + struct ndb_filter_elements *b_field, *a_field; + + // Everything is always a subset of {} + if (b->num_elements == 0) + return 1; + + // We can't be a subset if the number of elements in the other + // filter is larger then the number of elements we have. + if (b->num_elements > a->num_elements) + return 0; + + // If our filter count matches, we can only be a subset if we are + // equal + if (b->num_elements == a->num_elements) + return ndb_filter_eq(a, b); + + // If our element count is larger than the other filter, then we + // must see if every element in the other filter exists in ours. If + // so, then we are a subset of the other. + // + // eg: B={k:1, a:b} <- A={t:x, k:1, a:b} + // + // A is a subset of B because `k:1` and `a:b` both exist in A + + for (i = 0; i < b->num_elements; i++) { + b_field = ndb_filter_get_elements(b, i); + a_field = ndb_filter_find_elements(a, b_field->field.type); + + if (a_field == NULL) + return 0; + + if (!ndb_filter_field_eq(a, a_field, b, b_field)) + return 0; + } + + return 1; +} + int ndb_filter_eq(struct ndb_filter *a, struct ndb_filter *b) { int i; diff --git a/src/nostrdb.h b/src/nostrdb.h @@ -497,6 +497,9 @@ int ndb_filter_add_int_element(struct ndb_filter *, uint64_t integer); int ndb_filter_add_str_element(struct ndb_filter *, const char *str); int ndb_filter_eq(struct ndb_filter *, struct ndb_filter *); +/// is `a` a subset of `b` +int ndb_filter_is_subset_of(struct ndb_filter *a, struct ndb_filter *b); + // filters from json int ndb_filter_from_json(const char *, int len, struct ndb_filter *filter, unsigned char *buf, int bufsize); diff --git a/test.c b/test.c @@ -1689,9 +1689,45 @@ static void test_filter_eq() { assert(ndb_filter_eq(f, f2)); } +static void test_filter_is_subset() { + struct ndb_filter global, *g = &global; + struct ndb_filter kind, *k = &kind; + struct ndb_filter kind_and_id, *ki = &kind_and_id; + + const unsigned char id[] = { + 0x03, 0x36, 0x94, 0x8b, 0xdf, 0xbf, 0x5f, 0x93, 0x98, 0x02, 0xeb, 0xa0, + 0x3a, 0xa7, 0x87, 0x35, 0xc8, 0x28, 0x25, 0x21, 0x1e, 0xec, 0xe9, 0x87, + 0xa6, 0xd2, 0xe2, 0x0e, 0x3c, 0xff, 0xf9, 0x30 + }; + + ndb_filter_init(g); + ndb_filter_end(g); + + ndb_filter_init(k); + assert(ndb_filter_start_field(k, NDB_FILTER_KINDS)); + assert(ndb_filter_add_int_element(k, 1)); + ndb_filter_end_field(k); + ndb_filter_end(k); + + ndb_filter_init(ki); + assert(ndb_filter_start_field(ki, NDB_FILTER_KINDS)); + assert(ndb_filter_add_int_element(ki, 1)); + ndb_filter_end_field(ki); + assert(ndb_filter_start_field(ki, NDB_FILTER_IDS)); + assert(ndb_filter_add_id_element(ki, id)); + ndb_filter_end_field(ki); + ndb_filter_end(ki); + + assert(ndb_filter_is_subset_of(g, k) == 0); + assert(ndb_filter_is_subset_of(k, g) == 1); + assert(ndb_filter_is_subset_of(ki, k) == 1); + assert(ndb_filter_is_subset_of(k, ki) == 0); +} + int main(int argc, const char *argv[]) { test_parse_filter_json(); test_filter_eq(); + test_filter_is_subset(); test_filter_json(); test_bech32_parsing(); test_single_url_parsing();