commit b81490cda1c6c18459e515c130e88b331dc9fea9
parent 4c179987772f6d4d47b972b48391f1a99e1c9b5e
Author: William Casarin <jb55@jb55.com>
Date: Thu, 30 Oct 2025 13:31:59 -0700
migration: reply counter + test
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
| M | src/nostrdb.c | | | 219 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- |
| M | test.c | | | 23 | +++++++++++++++-------- |
2 files changed, 158 insertions(+), 84 deletions(-)
diff --git a/src/nostrdb.c b/src/nostrdb.c
@@ -2029,9 +2029,151 @@ static unsigned char *ndb_note_last_id_tag(struct ndb_note *note, char type)
return last;
}
+/* get reply information from a note */
+static void ndb_parse_reply(struct ndb_note *note, struct ndb_note_reply *note_reply)
+{
+ unsigned char *root, *reply, *mention, *id;
+ const char *marker;
+ struct ndb_iterator iter;
+ struct ndb_str str;
+ uint16_t count;
+ int any_marker, first;
+
+ any_marker = 0;
+ first = 1;
+ root = NULL;
+ reply = NULL;
+ mention = NULL;
+
+ // get the liked event id (last id)
+ ndb_tags_iterate_start(note, &iter);
+ while (ndb_tags_iterate_next(&iter)) {
+ if (root && reply && mention)
+ break;
+
+ marker = NULL;
+ count = ndb_tag_count(iter.tag);
+
+ if (count < 2)
+ continue;
+
+ str = ndb_tag_str(note, iter.tag, 0);
+ if (!(str.flag == NDB_PACKED_STR && str.str[0] == 'e'))
+ continue;
+
+ str = ndb_tag_str(note, iter.tag, 1);
+ if (str.flag != NDB_PACKED_ID)
+ continue;
+ id = str.id;
+
+ /* if we have the marker, assign it */
+ if (count >= 4) {
+ str = ndb_tag_str(note, iter.tag, 3);
+ if (str.flag == NDB_PACKED_STR)
+ marker = str.str;
+ }
-static int ndb_count_replies(struct ndb_txn *txn, const unsigned char *note_id, uint16_t *direct_replies, uint32_t *thread_replies)
+ if (marker) {
+ any_marker = true;
+ if (!strcmp(marker, "root"))
+ root = id;
+ else if (!strcmp(marker, "reply"))
+ reply = id;
+ else if (!strcmp(marker, "mention"))
+ mention = id;
+ } else if (!any_marker && first) {
+ root = id;
+ first = 0;
+ } else if (!any_marker && !reply) {
+ reply = id;
+ }
+ }
+
+ note_reply->reply = reply;
+ note_reply->root = root;
+ note_reply->mention = mention;
+}
+
+static int ndb_is_reply_to_root(struct ndb_note_reply *reply)
{
+ if (reply->root && !reply->reply)
+ return 0;
+ else if (reply->root && reply->reply)
+ return !memcmp(reply->root, reply->reply, 32);
+ else
+ return 0;
+}
+
+
+int ndb_count_replies(struct ndb_txn *txn, const unsigned char *note_id, uint16_t *direct_replies, uint32_t *thread_replies)
+{
+ MDB_val k, v;
+ MDB_cursor *cur;
+ MDB_dbi db;
+
+ int rc;
+ uint64_t note_key;
+ size_t size;
+ struct ndb_note *note;
+ unsigned char *keybuf, *reply_id;
+ struct ndb_note_reply reply;
+ char buffer[41]; /* 1 + 32 + 8 */
+
+ *direct_replies = 0;
+ *thread_replies = 0;
+
+ db = txn->lmdb->dbs[NDB_DB_NOTE_TAGS];
+ if ((rc = mdb_cursor_open(txn->mdb_txn, db, &cur))) {
+ fprintf(stderr, "ndb_count_reactions: mdb_cursor_open failed, error %d\n", rc);
+ return 0;
+ }
+
+ buffer[0] = 'e';
+ memcpy(&buffer[1], note_id, 32);
+ memset(&buffer[33], 0x00, 8);
+
+ k.mv_data = buffer;
+ k.mv_size = sizeof(buffer);
+ v.mv_data = NULL;
+ v.mv_size = 0;
+
+ if (mdb_cursor_get(cur, &k, &v, MDB_SET_RANGE))
+ goto cleanup;
+
+ do {
+ keybuf = (unsigned char *)k.mv_data;
+ note_key = *((uint64_t*)v.mv_data);
+ if (k.mv_size < sizeof(buffer))
+ break;
+ if (keybuf[0] != 'e')
+ break;
+ if (memcmp(&keybuf[1], note_id, 32))
+ break;
+ if (!(note = ndb_get_note_by_key(txn, note_key, &size)))
+ continue;
+ if (ndb_note_kind(note) != 1)
+ continue;
+
+ ndb_parse_reply(note, &reply);
+
+ if (ndb_is_reply_to_root(&reply)) {
+ reply_id = reply.root;
+ } else {
+ reply_id = reply.reply;
+ }
+
+ if (reply_id && !memcmp(reply_id, note_id, 32)) {
+ (*direct_replies)++;
+ }
+
+ if (reply.root && !memcmp(reply.root, note_id, 32)) {
+ (*thread_replies)++;
+ }
+
+ } while (mdb_cursor_get(cur, &k, &v, MDB_NEXT) == 0);
+
+cleanup:
+ mdb_cursor_close(cur);
return 1;
}
@@ -5632,81 +5774,6 @@ static unsigned char *ndb_note_first_tag_id(struct ndb_note *note, char tag)
return NULL;
}
-/* get reply information from a note */
-static void ndb_parse_reply(struct ndb_note *note, struct ndb_note_reply *note_reply)
-{
- unsigned char *root, *reply, *mention, *id;
- const char *marker;
- struct ndb_iterator iter;
- struct ndb_str str;
- uint16_t count;
- int any_marker, first;
-
- any_marker = 0;
- first = 1;
- root = NULL;
- reply = NULL;
- mention = NULL;
-
- // get the liked event id (last id)
- ndb_tags_iterate_start(note, &iter);
- while (ndb_tags_iterate_next(&iter)) {
- if (root && reply && mention)
- break;
-
- marker = NULL;
- count = ndb_tag_count(iter.tag);
-
- if (count < 2)
- continue;
-
- str = ndb_tag_str(note, iter.tag, 0);
- if (!(str.flag == NDB_PACKED_STR && str.str[0] == 'e'))
- continue;
-
- str = ndb_tag_str(note, iter.tag, 1);
- if (str.flag != NDB_PACKED_ID)
- continue;
- id = str.id;
-
- /* if we have the marker, assign it */
- if (count >= 4) {
- str = ndb_tag_str(note, iter.tag, 3);
- if (str.flag == NDB_PACKED_STR)
- marker = str.str;
- }
-
- if (marker) {
- any_marker = true;
- if (!strcmp(marker, "root"))
- root = id;
- else if (!strcmp(marker, "reply"))
- reply = id;
- else if (!strcmp(marker, "mention"))
- mention = id;
- } else if (!any_marker && first) {
- root = id;
- first = 0;
- } else if (!any_marker && !reply) {
- reply = id;
- }
- }
-
- note_reply->reply = reply;
- note_reply->root = root;
- note_reply->mention = mention;
-}
-
-static int ndb_is_reply_to_root(struct ndb_note_reply *reply)
-{
- if (reply->root && !reply->reply)
- return 0;
- else if (reply->root && reply->reply)
- return !memcmp(reply->root, reply->reply, 32);
- else
- return 0;
-}
-
static int ndb_increment_quote_metadata(
struct ndb_txn *txn,
unsigned char *quoted_note_id,
diff --git a/test.c b/test.c
@@ -31,6 +31,7 @@ static void delete_test_db() {
}
int ndb_count_reactions(struct ndb_txn *txn, const unsigned char *note_id, uint32_t *count);
+int ndb_count_replies(struct ndb_txn *txn, const unsigned char *note_id, uint16_t *direct_replies, uint32_t *thread_replies);
static void db_load_events(struct ndb *ndb, const char *filename)
{
@@ -78,8 +79,8 @@ static void test_count_metadata()
struct ndb_note_meta *meta;
//struct ndb_note_meta_entry *counts;
struct ndb_note_meta_entry *entry;
- uint16_t count;
- uint32_t total_reactions, reactions, replies;
+ uint16_t count, direct_replies[2];
+ uint32_t total_reactions, reactions, thread_replies[2];
int i;
reactions = 0;
@@ -118,13 +119,13 @@ static void test_count_metadata()
assert(entry);
assert(*ndb_note_meta_counts_quotes(entry) == 2);
- replies = *ndb_note_meta_counts_thread_replies(entry);
- printf("\t# thread replies %d\n", replies);
- assert(replies == 93);
+ thread_replies[0] = *ndb_note_meta_counts_thread_replies(entry);
+ printf("\t# thread replies %d\n", thread_replies[0]);
+ assert(thread_replies[0] == 93);
- replies = *ndb_note_meta_counts_direct_replies(entry);
- printf("\t# direct replies %d\n", replies);
- assert(replies == 14);
+ direct_replies[0] = *ndb_note_meta_counts_direct_replies(entry);
+ printf("\t# direct replies %d\n", direct_replies[0]);
+ assert(direct_replies[0] == 14);
total_reactions = *ndb_note_meta_counts_total_reactions(entry);
printf("\t# total reactions %d\n", reactions);
@@ -143,6 +144,12 @@ static void test_count_metadata()
ndb_count_reactions(&txn, id, &reactions);
printf("\t# after-counted reactions %d\n", reactions);
assert(reactions == total_reactions);
+
+ ndb_count_replies(&txn, id, &direct_replies[1], &thread_replies[1]);
+ printf("\t# after-counted replies direct:%d thread:%d\n", direct_replies[1], thread_replies[1]);
+ assert(direct_replies[0] == direct_replies[1]);
+ assert(thread_replies[0] == thread_replies[1]);
+
ndb_end_query(&txn);
ndb_destroy(ndb);