From cdeca71ef0025edcd49904db354de1ad88690f45 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 24 Dec 2025 14:48:56 +0000 Subject: [PATCH] feat(relay-selection): add per-relay filter optimization Add RelayFilterMap type and buildPerRelayFilters function to group authors by their target relays. This enables consumers to send only relevant authors to each relay instead of the full filter. Result now includes: - perRelayFilters: Array of { relay, authors[] } mappings This reduces bandwidth and improves relay processing efficiency. --- src/services/relay-selection.ts | 36 +++++++++++++++++++++++++++++++++ src/types/relay-selection.ts | 22 ++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/services/relay-selection.ts b/src/services/relay-selection.ts index 2cc21c7..0437b7c 100644 --- a/src/services/relay-selection.ts +++ b/src/services/relay-selection.ts @@ -25,6 +25,7 @@ import type { RelaySelectionResult, RelaySelectionReasoning, RelaySelectionOptions, + RelayFilterMap, } from "@/types/relay-selection"; /** @@ -486,6 +487,37 @@ function buildReasoning( ); } +/** + * Builds per-relay filter maps for optimized queries + * + * Groups authors by which relay they should be queried from, + * allowing consumers to send only relevant authors to each relay. + * + * @param selectedPointers - ProfilePointers after optimization (with filtered relays) + * @returns Array of per-relay filter mappings + */ +function buildPerRelayFilters( + selectedPointers: ProfilePointer[], +): RelayFilterMap[] { + // Group authors by relay + const relayToAuthors = new Map>(); + + for (const pointer of selectedPointers) { + for (const relay of pointer.relays || []) { + if (!relayToAuthors.has(relay)) { + relayToAuthors.set(relay, new Set()); + } + relayToAuthors.get(relay)!.add(pointer.pubkey); + } + } + + // Convert to array + return Array.from(relayToAuthors.entries()).map(([relay, authors]) => ({ + relay, + authors: Array.from(authors), + })); +} + /** * Creates a fallback result when no pubkeys or all fetches failed * @@ -707,9 +739,13 @@ export async function selectRelaysForFilter( pTagPointers, ); + // Build per-relay filter maps for optimized queries + const perRelayFilters = buildPerRelayFilters(selectedPointers); + return { relays, reasoning, isOptimized: true, + perRelayFilters, }; } diff --git a/src/types/relay-selection.ts b/src/types/relay-selection.ts index 35ad329..f71df3e 100644 --- a/src/types/relay-selection.ts +++ b/src/types/relay-selection.ts @@ -17,6 +17,14 @@ export interface RelaySelectionResult { /** True if using NIP-65 optimization, false if using fallback */ isOptimized: boolean; + + /** + * Per-relay author mappings for optimized queries (optional) + * + * When present, consumers can use this to send only relevant + * authors to each relay instead of the full authors list. + */ + perRelayFilters?: RelayFilterMap[]; } /** @@ -52,3 +60,17 @@ export interface RelaySelectionOptions { /** Timeout in ms for fetching kind:10002 events (default: 1000) */ timeout?: number; } + +/** + * Per-relay filter mapping for optimized queries + * + * Instead of sending the same filter to all relays, we can send + * only the relevant authors to each relay based on their relay lists. + */ +export interface RelayFilterMap { + /** Relay URL (normalized) */ + relay: string; + + /** Authors that should be queried from this relay */ + authors: string[]; +}