mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
72 lines
4.0 KiB
SQL
72 lines
4.0 KiB
SQL
-- ── Events (partitioned by month on created_at) ──────────────────────────────
|
|
-- Conformance: "Channel-less global events and DMs". `community_id` leads the
|
|
-- PK and every hot-path index. Partition stays BY RANGE (created_at) — the
|
|
-- monthly partition manager is unchanged (Max's call, plan §5/Lane0 contract).
|
|
-- Cross-community dedup: same signed event may exist in two communities;
|
|
-- (community_id, created_at, id) dedupes within one, allows across.
|
|
|
|
CREATE TABLE events (
|
|
community_id UUID NOT NULL REFERENCES communities(id),
|
|
id BYTEA NOT NULL,
|
|
pubkey BYTEA NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL,
|
|
kind INT NOT NULL,
|
|
tags JSONB NOT NULL,
|
|
content TEXT NOT NULL,
|
|
-- Full-text search vector (Typesense → Postgres FTS). Generated/STORED so
|
|
-- it is a single source of truth — no sidecar indexer to keep coherent
|
|
-- (Quinn option A, Lane-0 call). 'simple' config = no stemming/stopwords,
|
|
-- matching the existing substring-ish search semantics; the search lane can
|
|
-- revisit the config behind evidence. Tenant scoping is by the
|
|
-- community-leading btree filters BitmapAnd-ed with the GIN probe, so the
|
|
-- GIN index itself stays the minimal `GIN (search_tsv)` (Max's caveat:
|
|
-- avoid btree_gin unless EXPLAIN proves it buys something).
|
|
-- Privacy: encrypted/private routing wrappers and p-gated membership notices
|
|
-- must never be discoverable through NIP-50 full-text search. NULL tsvector
|
|
-- never matches `@@`.
|
|
-- Keep in sync with migrations (final state: 0001 + 0005 + 0014 + 0033).
|
|
search_tsv TSVECTOR GENERATED ALWAYS AS (
|
|
CASE WHEN kind IN (1059, 30179, 30300, 30350, 30622, 44100, 44101, 44200) THEN NULL::tsvector
|
|
ELSE to_tsvector('simple', content)
|
|
END
|
|
) STORED,
|
|
sig BYTEA NOT NULL,
|
|
received_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
channel_id UUID,
|
|
deleted_at TIMESTAMPTZ,
|
|
d_tag TEXT,
|
|
not_before BIGINT,
|
|
delivered_at BIGINT,
|
|
PRIMARY KEY (community_id, created_at, id)
|
|
) PARTITION BY RANGE (created_at);
|
|
|
|
-- Direct id lookup: the PK can't serve `WHERE id=$1` because created_at sits
|
|
-- between community_id and id. This index makes the scoped form
|
|
-- `WHERE community_id=$ AND id=$` index-served, not a partition scan.
|
|
CREATE INDEX idx_events_community_id ON events (community_id, id, created_at DESC);
|
|
-- Hot-path indexes, all community-leading.
|
|
CREATE INDEX idx_events_community_channel_created
|
|
ON events (community_id, channel_id, created_at DESC, id);
|
|
CREATE INDEX idx_events_community_pubkey_kind_created
|
|
ON events (community_id, pubkey, kind, created_at DESC, id);
|
|
CREATE INDEX idx_events_community_kind_created
|
|
ON events (community_id, kind, created_at DESC, id);
|
|
CREATE INDEX idx_events_community_deleted ON events (community_id, deleted_at);
|
|
-- Addressable (replaceable) and NIP-33 parameterized lookups.
|
|
CREATE INDEX idx_events_addressable
|
|
ON events (community_id, kind, pubkey, channel_id, deleted_at);
|
|
CREATE INDEX idx_events_parameterized
|
|
ON events (community_id, kind, pubkey, d_tag, created_at DESC, id)
|
|
WHERE d_tag IS NOT NULL AND deleted_at IS NULL;
|
|
CREATE INDEX idx_events_not_before ON events (community_id, not_before)
|
|
WHERE not_before IS NOT NULL AND deleted_at IS NULL AND delivered_at IS NULL;
|
|
-- Full-text search. Minimal GIN over the generated tsvector; community scoping
|
|
-- is supplied by the community-leading btree filters above (BitmapAnd), so this
|
|
-- stays a single-column GIN. The search lane confirms the final spelling with
|
|
-- EXPLAIN before its work lands (Quinn option A; Max's index-spelling caveat).
|
|
CREATE INDEX idx_events_search_tsv ON events USING GIN (search_tsv);
|
|
|
|
-- e-tag containment (`tags @> '[["e","<hex>"]]'`) for the aux closure and #e
|
|
-- reads. Mirrors migrations/0004; jsonb_path_ops supports exactly @>.
|
|
CREATE INDEX idx_events_tags_gin ON events USING GIN (tags jsonb_path_ops);
|