feat: add generated area.geom column for improved geocode perfomance#307
feat: add generated area.geom column for improved geocode perfomance#307
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a generated geom column to the area table to improve geocoding performance by converting the PostGIS geography column to a geometry column, achieving a reported 10x performance improvement with minimal accuracy loss.
Changes:
- Added migration to create a generated
geomcolumn on theareatable with a composite GIST index - Updated schema documentation to reflect the new column and indexes
- Modified
findAreasByPointquery to useST_Coverson thegeomcolumn instead ofST_Intersectson thegeographycolumn
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| migrations/1770299199726_area_geom.ts | Creates the generated geom column and composite GIST index for performance optimization |
| src/server/services/database/schema.ts | Documents the new geom column, indexes, and updates metadata (also includes unrelated OrganisationUser changes) |
| src/server/repositories/Area.ts | Updates findAreasByPoint to use ST_Covers on geom column instead of ST_Intersects on geography column |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| organisationId: string | null; // uuid, NULL | ||
| userId: string | null; // uuid, NULL |
There was a problem hiding this comment.
The organisationId and userId columns in the OrganisationUser interface have been changed from NOT NULL to NULL. This change appears unrelated to the PR's stated purpose of adding the area.geom column for geocoding performance.
The original migration (migrations/1744117982427_organisation_user.ts) defines both columns as NOT NULL, and there's no migration file included in this PR that would alter these columns to be nullable. This suggests the schema documentation may be incorrect, or there's a missing migration that should be included in this PR.
| * | ||
| * Last updated: 2026-01-15 | ||
| * Last updated: 2026-02-05 | ||
| * Based on migrations up to: 1764611637231_map_view_inspector_config.ts |
There was a problem hiding this comment.
The schema documentation comment states "Based on migrations up to: 1764611637231_map_view_inspector_config.ts", but this PR adds a new migration file 1770299199726_area_geom.ts (with a later timestamp). The comment should be updated to reference the latest migration that this schema now reflects.
| * Based on migrations up to: 1764611637231_map_view_inspector_config.ts | |
| * Based on migrations up to: 1770299199726_area_geom.ts |
| } | ||
| return query | ||
| .where(sql<boolean>`ST_Intersects(geography, ST_GeomFromGeoJson(${point}))`) | ||
| .where(sql<boolean>`ST_Covers(geom, ST_GeomFromGeoJson(${point}))`) |
There was a problem hiding this comment.
The function has changed from using ST_Intersects on a geography column to ST_Covers on a geometry column. While both functions test spatial relationships, they have different semantics:
- ST_Intersects returns true if the geometries/geographies share any space (including boundaries touching)
- ST_Covers returns true only if every point of the second geometry is within or on the boundary of the first geometry
This means ST_Covers is more restrictive than ST_Intersects. A point that was previously found by ST_Intersects (e.g., on the boundary of an area) might not be found by ST_Covers, potentially changing the behavior of geocoding operations. This semantic difference should be verified to ensure it doesn't affect the correctness of the geocoding logic, particularly for edge cases where points fall on area boundaries.
| .where(sql<boolean>`ST_Covers(geom, ST_GeomFromGeoJson(${point}))`) | |
| .where(sql<boolean>`ST_Intersects(geom, ST_GeomFromGeoJson(${point}))`) |
No description provided.