Files
server-26/infra/firestore/firestore.indexes.json
Logan CusanoandClaude Sonnet 5 bd04bdbd69 firestore: declare DESC indexes for the orderBy(desc) queries (#33/#51)
The old //direction note ('ASC serves orderBy desc') was wrong for these query shapes and left useCalls/useIncidents/useAlerts and search_calls throwing FAILED_PRECONDITION. Declare calls/incidents/alert_events (…, DESC) to match the live DB (indexes created via gcloud 2026-09-08). Drop the misleading 'delete these duplicates' drift note.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01Tbknwttzou4s46PAykmtix
2026-09-07 23:20:48 -04:00

82 lines
5.3 KiB
JSON

{
"//": "Composite indexes for the c2-server database. Firestore auto-indexes single-field lookups and equality-only compound queries; an equality filter combined with an inequality, an orderBy on a different field, or array-contains needs an explicit composite index or the query fails at runtime with FAILED_PRECONDITION. Deploy with: firebase deploy --only firestore:indexes --project <project-id> (firebase.json pins database c2-server — without that key the CLI targets (default) and changes nothing the app can see).",
"//direction": "The sort field's ORDER here must match the query's orderBy direction. The old note claimed 'Firestore scans either direction so ASC serves orderBy(desc)' — that is WRONG for these query shapes and cost us three broken pages (server-26 #33/#51/#110-followup, 2026-09-08): useCalls/useIncidents/useAlerts and c2-core search_calls all orderBy(x,'desc') and each got FAILED_PRECONDITION until an explicit DESCENDING index existed. A range/inequality filter with no orderBy (the backend status/ended_at, system_id/started_at, system_id/ended_at entries) is genuinely direction-agnostic and stays ASCENDING.",
"//drift-2026-09-08": "Reconciled against the live c2-server via `gcloud firestore indexes composite list` (server-26#33). Live already carries the three DESC indexes below (calls(org_id,started_at DESC), incidents(org_id,started_at DESC), alert_events(org_id,triggered_at DESC)) plus alert_events(acknowledged,org_id,triggered_at DESC) — created directly with gcloud on 2026-09-08 to unbreak Archive + Watch. This file now declares them so a `firebase deploy --only firestore:indexes` is a no-op, NOT a set of deletions. Do NOT delete calls(org_id,started_at DESC) or incidents(org_id,started_at DESC) — the pre-2026-09-08 note calling them deletable 'duplicates of the ASC entries' was the bug. The only genuinely dead index is the pre-tenancy alert_events(acknowledged,triggered_at) with no org_id, which may be deleted.",
"indexes": [
{
"//": "drb-frontend lib/useCalls.ts + c2-core routers/calls.py search_calls — org-scoped call list, orderBy started_at DESC.",
"collectionGroup": "calls",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "org_id", "order": "ASCENDING" },
{ "fieldPath": "started_at", "order": "DESCENDING" }
]
},
{
"//": "drb-frontend lib/useCalls.ts — the calls attached to one incident.",
"collectionGroup": "calls",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "org_id", "order": "ASCENDING" },
{ "fieldPath": "incident_ids", "arrayConfig": "CONTAINS" }
]
},
{
"//": "c2-core internal/recorrelation_sweep.py:45 — status == 'ended' AND ended_at >= cutoff. Range filter, no orderBy: direction-agnostic. Backend only.",
"collectionGroup": "calls",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "status", "order": "ASCENDING" },
{ "fieldPath": "ended_at", "order": "ASCENDING" }
]
},
{
"//": "c2-core internal/dedup.py:84 — system_id == X AND started_at within a +/- window. Range filter, direction-agnostic. Was live-failing on essentially every inbound call (server-26#84): dedup caught the FAILED_PRECONDITION and degraded to \"not a duplicate\", so double-heard transmissions were stored twice. Created directly on c2-server 2026-08-28.",
"collectionGroup": "calls",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "system_id", "order": "ASCENDING" },
{ "fieldPath": "started_at", "order": "ASCENDING" }
]
},
{
"//": "c2-core internal/vocabulary_learner.py:290 — system_id == X AND ended_at >= cutoff. Range filter, direction-agnostic. Backend only.",
"collectionGroup": "calls",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "system_id", "order": "ASCENDING" },
{ "fieldPath": "ended_at", "order": "ASCENDING" }
]
},
{
"//": "drb-frontend lib/useIncidents.ts — org-scoped incident browse, orderBy started_at DESC.",
"collectionGroup": "incidents",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "org_id", "order": "ASCENDING" },
{ "fieldPath": "started_at", "order": "DESCENDING" }
]
},
{
"//": "drb-frontend lib/useAlerts.ts — org-scoped alert feed, where(org_id ==) orderBy(triggered_at DESC).",
"collectionGroup": "alert_events",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "org_id", "order": "ASCENDING" },
{ "fieldPath": "triggered_at", "order": "DESCENDING" }
]
},
{
"//": "drb-frontend lib/useAlerts.ts useUnacknowledgedAlerts (nav badge) and the /watch \"Triggered Alerts\" tab — where(org_id ==) where(acknowledged == false) orderBy(triggered_at DESC). Field tuple + triggered_at DESCENDING copy the console create_composite link verbatim (server-26#51). Distinct from the (org_id, triggered_at) feed index above (no acknowledged filter).",
"collectionGroup": "alert_events",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "acknowledged", "order": "ASCENDING" },
{ "fieldPath": "org_id", "order": "ASCENDING" },
{ "fieldPath": "triggered_at", "order": "DESCENDING" }
]
}
],
"fieldOverrides": []
}