-- CreateIndex —— 新筛选维度的偏索引:上次时间 / 上次医生 / 偏好医生
--
-- 与 20260728020000 那 14 条同一套路数、同一理由,不重复长篇:
--   Prisma 把画像筛选编译成 `key = 'visit_recency' AND (data #> '{X}') = '"…"'`,
--   没有索引,planner 只能按 key 捞出**全部** visit_recency 行再回堆过滤 jsonb。
--   2026-07-29 生产实测过后果:某维度 190,210 行里只中 2,156 行,读 1.3GB 堆、单次筛人 20+ 秒。
--
-- ⚠️ **加筛选维度必须同时加索引** —— persona-tag-filters.ts 顶部写死的纪律,这三条就是它的兑现。
--
-- 形态:全是标量等值 → btree (表达式, persona_id)。
--   第二列 persona_id 不是排序用,是让内层 EXISTS 走 **index-only scan**
--   (它只 SELECT persona_id,索引自带就不用回堆;实测数据见 20260728020000)。
--
-- ⭐ 医生两条同样是**等值**索引,不是模糊匹配索引 —— 这是刻意的:
--   前端输入框在 /plans/doctors 拉到的名单上做客户端模糊,选中后发**精确姓名**。
--   若改成后端 ILIKE '%x%',这两条索引立刻失效、退回全分区扫。要做真·后端模糊
--   得先上 pg_trgm + GIN,并确认托管 PG 允许装扩展。
--
-- 【为什么不用 CONCURRENTLY】visit_recency 是新特征,建索引时表里一行都没有
--   (要等下一轮画像重算才写入),普通 CREATE INDEX 瞬间完成、锁不到任何东西。
--   等数据铺开后再加别的维度索引,才需要 CONCURRENTLY + 部署前手工先建。
CREATE INDEX IF NOT EXISTS "persona_features_visit_recency_bucket_idx"
  ON "persona_features" (((data #> '{bucket}'::text[])), "persona_id") WHERE "key" = 'visit_recency';
CREATE INDEX IF NOT EXISTS "persona_features_visit_recency_last_doctor_idx"
  ON "persona_features" (((data #> '{lastDoctor}'::text[])), "persona_id") WHERE "key" = 'visit_recency';
CREATE INDEX IF NOT EXISTS "persona_features_visit_recency_pref_doctor_idx"
  ON "persona_features" (((data #> '{preferredDoctor}'::text[])), "persona_id") WHERE "key" = 'visit_recency';
