Skip to content

feat(relay): push #t tag filters into SQL via JSONB containment (#3959) - #3967

Open
iroiro147 wants to merge 1 commit into
block:mainfrom
iroiro147:claude/issue3959b-20260731
Open

feat(relay): push #t tag filters into SQL via JSONB containment (#3959)#3967
iroiro147 wants to merge 1 commit into
block:mainfrom
iroiro147:claude/issue3959b-20260731

Conversation

@iroiro147

Copy link
Copy Markdown

Problem

NIP-01 #t filters (multi-value OR semantics) were post-filtered after the DB LIMIT bound. A page shorter than the page-size limit could therefore not be trusted as result-set exhaustion: matching events below the LIMIT boundary were invisible to the caller, and any client reasoning "short page ⇒ done" was unsound.

This made tag-constrained REQs unreliable for: subscription clients (NIP-RS full-state load), music/DJ decks (kind:30078 tag families), channel templates (#h,#t combinations), and any tool building local state from tag-filtered queries.

Refs #3959.

Fix

Push #t into SQL via tags @> '[["t","<value>"]]' JSONB containment, OR-joined across multi-values (matching NIP-01 filter semantics for multiple #t values on one filter). This mirrors the existing #e pushdown (event.rs:467) and reuses the idx_events_tags_gin GIN index (migrations/0004).

Changes:

  1. EventQuery gains t_tags: Option<Vec<String>> (buzz-db/src/event.rs)
  2. query_events appends the JSONB containment clause: AND (tags @> '[["t","x"]]' OR tags @> '[["t","y"]]') (buzz-db/src/event.rs)
  3. filter_to_query_params extracts generic_tags[#t] into t_tags (buzz-relay/src/handlers/req.rs)
  4. filter_fully_pushable now accepts #t (previously routed to the slow COUNT path with post-filtering)
  5. doc comment updated to reflect #t pushability

apply_count_fallback_limit inherits t_tags from the shared EventQuery construction — no special-case needed.

Tests

Three new regression tests in handlers/req.rs:

  • t_tag_multi_value_is_fully_pushable — OR semantics accepted
  • t_tag_empty_values_still_pushable — vacuous case (no constraint emitted)
  • filter_to_query_params_populates_t_tags — SQL population verified

All 834 existing tests pass (9 pre-existing media/admin failures — same set fails on clean main).

Note on Defect A

The separate EOSE-delivery barrier defect (Defect A in issue #3959) — events accepted during a query's EOSE window may be delivered late — requires relay-architectural work (coordinating dispatch_persistent_event's spawned fins with EOSE emission). That is not addressed here; this PR fixes the tag-filter half.

NIP-01 #t filters (multi-value OR semantics) were previously post-filtered
after the DB LIMIT bound. A short page could therefore not be trusted as
result-set exhaustion — matching events below the LIMIT boundary were
invisible to the client.

Push #t into SQL via the existing idx_events_tags_gin GIN index
(tags @> '["t","<value>"]' per value, OR-joined). This mirrors the
existing #e pushdown (event.rs:467) and affects filter_fully_pushable:
#t-tagged queries now take the fast COUNT path without post-filtering.

filter_to_query_params extracts generic_tags[#t] into EventQuery::t_tags;
query_events appends the JSONB containment clause alongside the existing
#e/#channel/#kind/#author/#id pushdowns. Empty values are vacuously
pushable (no constraint emitted).

Regression tests:
- t_tag_multi_value_is_fully_pushable: multi-value OR semantics accepted
- t_tag_empty_values_still_pushable: vacuous case
- filter_to_query_params_populates_t_tags: mapping verified

Refs block#3959. Closes the #t pushdown gap in the full-state-load
conformance ladder. The separate EOSE-delivery barrier (Defect A) remains
open and requires relay-architectural work.

Signed-off-by: iroiro147 <sarthak.singh@juspay.in>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant