Skip to content

feat(search): implement global search across all entity types (ALL-731)#207

Merged
texture-fleet-agent[bot] merged 2 commits intomainfrom
meridian/fix-search-index
May 6, 2026
Merged

feat(search): implement global search across all entity types (ALL-731)#207
texture-fleet-agent[bot] merged 2 commits intomainfrom
meridian/fix-search-index

Conversation

@texture-fleet-agent
Copy link
Copy Markdown
Contributor

@texture-fleet-agent texture-fleet-agent Bot commented May 6, 2026

Fixes /search endpoint returning 0 results for known entities (e.g. tri-state, reported by Morgan in Relay bug #3).

Root cause

searchFromDb() in lib/data/search.ts was a TODO stub returning [] for every call — independently of whether search_vector was populated.

Audit of the production DB confirmed the tsvector columns are actually already fully populated via GENERATED ALWAYS AS STORED expressions on utilities, programs, power_plants, ev_stations, and substations. The endpoint just wasn't reading them.

Fix

lib/data/search.ts now actually queries the database:

  • For tables with a search_vector tsvector column: websearch_to_tsquery (punctuation-tolerant, handles tri-state correctly) combined with an ILIKE fallback on the primary name column. Results ranked by ts_rank.
  • For tables without search_vector (pricing_nodes, transmission_lines, isos, rtos, balancing_authorities): plain ILIKE on the name column.
  • All queries scoped to deleted_at IS NULL (soft-deletes).
  • Fan out across all 9 entity types in parallel (Promise.all) — full sweep completes in 40–85 ms against Neon HTTP.
  • Graceful degradation: a failing per-type query returns [] instead of throwing, so one bad query never blacks out the whole /search response.

drizzle/0007_fix_search_vectors.sql (already pushed, idempotent) ensures the search_vector columns are GENERATED ALWAYS AS — it's a no-op against the current DB state but protects us against a hypothetical replay against a fresh database.

Verification against prod DB

/search?q=tri-state
  utility (2)           Tri-State Electric Member, Tri-State G&T Association
  power-plant (5)       Craig (CO), JM Shafer Generating Station, Pyramid, …
  ev-station (1)        Tri-State Nissan
  transmission-line (3) TRI-STATE G & T ASSN-owned lines

Smoke test

lib/data/search.test.ts (new) runs in CI via vitest without a live DB. It mocks @/lib/db/client, captures every drizzle SQL fragment, and asserts:

  • All 9 entity types are queried by default.
  • types filter scopes correctly.
  • Unknown types fall back to all types.
  • Per-type DB failures degrade to [].
  • tsvector tables use websearch_to_tsquery + ILIKE, not plainto_tsquery.
  • Non-tsvector tables use plain ILIKE.
  • User input is parameterized (no SQL injection).
  • Every entity type points at the right underlying table.

10 assertions, <100 ms to run.

Ref: memory/specs/relay-commongrid-bugs-2026-05-06.md Bug #3
Fixes ALL-731

@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
commongrid Ready Ready Preview, Comment May 6, 2026 11:18pm

Request Review

@texture-fleet-agent texture-fleet-agent Bot changed the title fix(search): rebuild search_vector with GENERATED ALWAYS AS (ALL-731) feat(search): implement global search across all entity types (ALL-731) May 6, 2026
Fixes /search endpoint returning 0 results for known entities (e.g. tri-state).

Root cause:
  searchFromDb() in lib/data/search.ts was a TODO stub returning [] for every
  call. The database already has populated search_vector columns (GENERATED
  ALWAYS AS STORED) on utilities, programs, power_plants, and ev_stations —
  the endpoint just wasn't reading them.

Fix:
  Implement searchFromDb() to actually query the DB:
  - For tables with a search_vector tsvector column, use
    websearch_to_tsquery (tolerant of punctuation like hyphens) with an
    ILIKE fallback on the primary name column. Rank by ts_rank.
  - For tables without search_vector (pricing_nodes, transmission_lines,
    isos, rtos, balancing_authorities) use ILIKE on the name column.
  - All queries scoped to 'deleted_at IS NULL' (soft-deletes).
  - Fan out across all 9 entity types in parallel (Promise.all).
  - Graceful degradation: a failing per-type query returns [] instead of
    throwing, so one bad query never blacks out the full /search response.

Verified against production DB:
  /search?q=tri-state returns:
    utility (2) — Tri-State Electric Member, Tri-State G&T Association
    power-plant (5)  — Craig (CO), JM Shafer Generating Station, ...
    ev-station (1) — Tri-State Nissan
    transmission-line (3) — TRI-STATE G & T ASSN owned lines

Smoke test (lib/data/search.test.ts) runs in CI via vitest without a live
DB by mocking @/lib/db/client and asserting SQL shape, parameterization,
entity-type fan-out, and graceful degradation. 10 assertions, <100ms.

Timing: full 9-type fan-out completes in 40-85ms against Neon HTTP.

Ref: memory/specs/relay-commongrid-bugs-2026-05-06.md Bug #3
Complements the hermetic vitest unit tests (lib/data/search.test.ts)
with a live-DB regression guard gated on $DATABASE_URL.

Asserts the specific bugs flagged in Morgan's Relay report:
  - /search?q=tri-state must return Tri-State G&T (ALL-731 bug #3)
  - /search?q=duke%20energy must return Duke Energy utilities
  - /search?q=demand%20response must return Demand Response programs
  - /search?q=pjm&types=isos must return PJM Interconnection
  - broad queries fan out across buckets (utility + transmission-line)
  - per-type limit is respected
  - empty/whitespace query returns []

Skips cleanly on local machines without DATABASE_URL so `npm test`
stays hermetic and fast. When DATABASE_URL is provisioned in CI, this
test file becomes the last line of defense before a silent `/search`
regression ships.
@texture-fleet-agent texture-fleet-agent Bot force-pushed the meridian/fix-search-index branch from 68645f3 to dbf1d3a Compare May 6, 2026 23:15
@texture-fleet-agent texture-fleet-agent Bot merged commit f7b543c into main May 6, 2026
7 checks passed
@texture-fleet-agent texture-fleet-agent Bot deleted the meridian/fix-search-index branch May 6, 2026 23:20
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