commit f984ced23a898281ca46250f04045d4a1f4f5321
parent 4683cfda826bffb1a7cc6564efb4ea0cf4496a55
Author: triesap <triesap@radroots.dev>
Date: Sun, 4 Jan 2026 03:07:17 +0000
events: merge stored and fetched list-set results
- Query local database for matching list-set events
- Support multi d-tag queries with per-tag deduplication
- Merge stored and fetched event streams by id
- Preserve existing time-bounds and limit truncation
Diffstat:
1 file changed, 67 insertions(+), 1 deletion(-)
diff --git a/src/api/jsonrpc/methods/events/list_set/list.rs b/src/api/jsonrpc/methods/events/list_set/list.rs
@@ -91,6 +91,69 @@ where
items
}
+fn merge_list_set_events(
+ stored: Vec<RadrootsNostrEvent>,
+ fetched: Vec<RadrootsNostrEvent>,
+) -> Vec<RadrootsNostrEvent> {
+ let mut seen = HashSet::new();
+ let mut combined = Vec::with_capacity(stored.len() + fetched.len());
+ for event in stored.into_iter().chain(fetched) {
+ let id = event.id.to_string();
+ if seen.insert(id) {
+ combined.push(event);
+ }
+ }
+ combined
+}
+
+async fn query_list_set_events(
+ client: &RadrootsNostrClient,
+ base_filter: RadrootsNostrFilter,
+ d_tags: Option<Vec<String>>,
+) -> Result<Vec<RadrootsNostrEvent>, RpcError> {
+ match d_tags {
+ Some(d_tags) if d_tags.len() > 1 => {
+ let mut events = Vec::new();
+ let mut seen = HashSet::new();
+ for d_tag in d_tags.into_iter().filter(|tag| !tag.trim().is_empty()) {
+ let filter = base_filter.clone().identifiers([d_tag]);
+ let items = client
+ .database()
+ .query(filter)
+ .await
+ .map_err(|e| RpcError::Other(format!("query failed: {e}")))?;
+ for item in items {
+ let id = item.id.to_string();
+ if seen.insert(id) {
+ events.push(item);
+ }
+ }
+ }
+ Ok(events)
+ }
+ Some(d_tags) => {
+ let mut filter = base_filter;
+ if let Some(d_tag) = d_tags.into_iter().find(|tag| !tag.trim().is_empty()) {
+ filter = filter.identifiers([d_tag]);
+ }
+ let events = client
+ .database()
+ .query(filter)
+ .await
+ .map_err(|e| RpcError::Other(format!("query failed: {e}")))?;
+ Ok(events)
+ }
+ None => {
+ let events = client
+ .database()
+ .query(base_filter)
+ .await
+ .map_err(|e| RpcError::Other(format!("query failed: {e}")))?;
+ Ok(events)
+ }
+ }
+}
+
async fn fetch_list_set_events(
client: &RadrootsNostrClient,
base_filter: RadrootsNostrFilter,
@@ -170,7 +233,9 @@ pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Res
filter = apply_time_bounds(filter, since, until);
- let events = fetch_list_set_events(
+ let stored = query_list_set_events(&ctx.state.client, filter.clone(), d_tags.clone())
+ .await?;
+ let fetched = fetch_list_set_events(
&ctx.state.client,
filter,
d_tags,
@@ -178,6 +243,7 @@ pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Res
)
.await?;
+ let events = merge_list_set_events(stored, fetched);
let mut items = build_list_set_rows(events);
if items.len() > limit {
items.truncate(limit);