Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/nostrdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -3625,12 +3625,18 @@ int ndb_search_profile(struct ndb_txn *txn, struct ndb_search *search, const cha
int rc;
struct ndb_search_key s;
MDB_val k, v;
size_t query_len;
search->cursor = NULL;

MDB_cursor **cursor = (MDB_cursor **)&search->cursor;

ndb_make_search_key_low(&s, query);

// Store the lowercased query for prefix matching in subsequent calls
lowercase_strncpy(search->query, query, sizeof(search->query) - 1);
search->query[sizeof(search->query) - 1] = '\0';
query_len = strlen(search->query);

k.mv_data = &s;
k.mv_size = sizeof(s);

Expand All @@ -3650,6 +3656,11 @@ int ndb_search_profile(struct ndb_txn *txn, struct ndb_search *search, const cha
search->key = k.mv_data;
assert(v.mv_size == 8);
search->profile_key = *((uint64_t*)v.mv_data);

// Verify the first result matches the query prefix
if (strncmp(search->key->search, search->query, query_len) != 0) {
goto cleanup;
}
return 1;
}

Expand All @@ -3670,10 +3681,12 @@ int ndb_search_profile_next(struct ndb_search *search)
int rc;
MDB_val k, v;
unsigned char *init_id;
size_t query_len;

init_id = search->key->id;
k.mv_data = search->key;
k.mv_size = sizeof(*search->key);
query_len = strlen(search->query);

retry:
if ((rc = mdb_cursor_get(search->cursor, &k, &v, MDB_NEXT))) {
Expand All @@ -3685,6 +3698,11 @@ int ndb_search_profile_next(struct ndb_search *search)
assert(v.mv_size == 8);
search->profile_key = *((uint64_t*)v.mv_data);

// Check if this result still matches the query prefix
if (strncmp(search->key->search, search->query, query_len) != 0) {
return 0;
}

// skip duplicate pubkeys
if (!memcmp(init_id, search->key->id, 32))
goto retry;
Expand Down
1 change: 1 addition & 0 deletions src/nostrdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ struct ndb_search {
struct ndb_search_key *key;
uint64_t profile_key;
void *cursor; // MDB_cursor *
char query[24]; // Original query for prefix matching
};

// From-client event
Expand Down
55 changes: 55 additions & 0 deletions test.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <assert.h>
#include <unistd.h>
#include <sys/stat.h>
#include <ctype.h>

#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))

Expand Down Expand Up @@ -910,6 +911,58 @@ static void test_profile_search(struct ndb *ndb)
ndb_end_query(&txn);
}

static void test_profile_search_prefix(struct ndb *ndb)
{
struct ndb_txn txn;
struct ndb_search search;
int count = 0;
const char *name;
NdbProfile_table_t profile;
const char *prefix = "jean";
size_t prefix_len = strlen(prefix);

assert(ndb_begin_query(ndb, &txn));

// Search for prefix "jean"
if (!ndb_search_profile(&txn, &search, prefix)) {
// No results is valid if no profiles match
ndb_end_query(&txn);
printf("ok test_profile_search_prefix (no matches)\n");
return;
}

// First result should match the prefix
profile = lookup_profile(&txn, search.profile_key);
name = NdbProfile_name_get(profile);
assert(name != NULL);
// Convert to lowercase for comparison since search is case-insensitive
for (size_t i = 0; i < prefix_len && name[i]; i++) {
assert(tolower((unsigned char)name[i]) == tolower((unsigned char)prefix[i]));
}
count++;

// All subsequent results should also match the prefix
while (ndb_search_profile_next(&search)) {
profile = lookup_profile(&txn, search.profile_key);
name = NdbProfile_name_get(profile);
assert(name != NULL);
// Verify prefix match (case-insensitive)
for (size_t i = 0; i < prefix_len && name[i]; i++) {
assert(tolower((unsigned char)name[i]) == tolower((unsigned char)prefix[i]));
}
count++;
// Safety limit to avoid infinite loops in tests
if (count > 1000) break;
}

// The search should have stopped because prefix no longer matches,
// not because we ran out of entries in the entire database
ndb_search_profile_end(&search);
ndb_end_query(&txn);

printf("ok test_profile_search_prefix (found %d matches)\n", count);
}

static void test_profile_updates()
{
static const int alloc_size = 1024 * 1024;
Expand Down Expand Up @@ -994,6 +1047,7 @@ static void test_load_profiles()
ndb_end_query(&txn);

test_profile_search(ndb);
test_profile_search_prefix(ndb);

ndb_destroy(ndb);

Expand Down Expand Up @@ -1038,6 +1092,7 @@ static void test_migrate() {
assert(ndb_end_query(&txn));

test_profile_search(ndb);
test_profile_search_prefix(ndb);
ndb_destroy(ndb);
}

Expand Down
Loading