···1+-- Add missing indexes on document foreign keys used by get_profile_posts
2+CREATE INDEX IF NOT EXISTS comments_on_documents_document_idx
3+ ON public.comments_on_documents (document);
4+5+CREATE INDEX IF NOT EXISTS document_mentions_in_bsky_document_idx
6+ ON public.document_mentions_in_bsky (document);
7+8+CREATE INDEX IF NOT EXISTS documents_in_publications_document_idx
9+ ON public.documents_in_publications (document);
10+11+-- Add a stored generated column to extract the DID from at:// URIs
12+-- at://did:plc:xxx/collection/rkey -> did:plc:xxx
13+ALTER TABLE public.documents
14+ ADD COLUMN identity_did text GENERATED ALWAYS AS (split_part(uri, '/', 3)) STORED;
15+16+-- Composite index for efficient profile post lookups: filter by DID, order by sort_date
17+CREATE INDEX documents_identity_did_sort_idx
18+ ON public.documents (identity_did, sort_date DESC, uri DESC);
19+20+-- Rewrite get_profile_posts to use the new identity_did column
21+CREATE OR REPLACE FUNCTION get_profile_posts(
22+ p_did text,
23+ p_cursor_sort_date timestamptz DEFAULT NULL,
24+ p_cursor_uri text DEFAULT NULL,
25+ p_limit int DEFAULT 20
26+)
27+RETURNS TABLE (
28+ uri text,
29+ data jsonb,
30+ sort_date timestamptz,
31+ comments_count bigint,
32+ mentions_count bigint,
33+ recommends_count bigint,
34+ publication_uri text,
35+ publication_record jsonb,
36+ publication_name text
37+)
38+LANGUAGE sql STABLE
39+AS $$
40+ SELECT
41+ d.uri,
42+ d.data,
43+ d.sort_date,
44+ (SELECT count(*) FROM comments_on_documents c WHERE c.document = d.uri),
45+ (SELECT count(*) FROM document_mentions_in_bsky m WHERE m.document = d.uri),
46+ (SELECT count(*) FROM recommends_on_documents r WHERE r.document = d.uri),
47+ pub.uri,
48+ pub.record,
49+ pub.name
50+ FROM documents d
51+ LEFT JOIN LATERAL (
52+ SELECT p.uri, p.record, p.name
53+ FROM documents_in_publications dip
54+ JOIN publications p ON p.uri = dip.publication
55+ WHERE dip.document = d.uri
56+ LIMIT 1
57+ ) pub ON true
58+ WHERE d.identity_did = p_did
59+ AND (
60+ p_cursor_sort_date IS NULL
61+ OR d.sort_date < p_cursor_sort_date
62+ OR (d.sort_date = p_cursor_sort_date AND d.uri < p_cursor_uri)
63+ )
64+ ORDER BY d.sort_date DESC, d.uri DESC
65+ LIMIT p_limit;
66+$$;