Skip to content

Commit cfa53e5

Browse files
authored
Merge pull request #75 from constructive-io/devin/1774561358-revive-postgis-fix-fast-flag
docs: revive PR #66 PostGIS updates + demote --fast flag with idempotency warning
2 parents cb75175 + 15ea906 commit cfa53e5

2 files changed

Lines changed: 133 additions & 7 deletions

File tree

.agents/skills/constructive-graphql/references/search-postgis.md

Lines changed: 119 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ PostGIS columns are exposed as GeoJSON objects in GraphQL. The `graphile-postgis
8484

8585
### GeoJSON Output
8686

87-
PostGIS fields return GeoJSON-structured data:
87+
PostGIS fields return GeoJSON-structured data with type-specific subfields:
8888

8989
```graphql
9090
{
@@ -103,6 +103,52 @@ PostGIS fields return GeoJSON-structured data:
103103
}
104104
```
105105

106+
#### Type-Specific Fields
107+
108+
| Geometry Type | Available Fields |
109+
|---------------|------------------|
110+
| Point | `x`, `y`, `z` (if 3D), `geojson`, `srid` |
111+
| LineString | `points` (array of Points), `geojson`, `srid` |
112+
| Polygon | `exterior` (ring), `interiors` (holes), `geojson`, `srid` |
113+
| Multi* | `geometries` (array via union type), `geojson`, `srid` |
114+
| GeometryCollection | `geometries` (array via union type), `geojson`, `srid` |
115+
116+
For geography columns, Point fields use `longitude`/`latitude` instead of `x`/`y`.
117+
118+
#### Measurement Fields
119+
120+
Polygon and LineString types expose computed measurement fields (geodesic, in meters/sq meters):
121+
122+
| Field | Available On | Description |
123+
|-------|-------------|-------------|
124+
| `area` | Polygon | Geodesic area in square meters |
125+
| `length` | LineString | Geodesic length in meters |
126+
| `perimeter` | Polygon | Geodesic perimeter in meters |
127+
128+
```graphql
129+
{
130+
zones {
131+
nodes {
132+
boundary {
133+
area # sq meters
134+
perimeter # meters
135+
geojson
136+
}
137+
}
138+
}
139+
}
140+
```
141+
142+
#### Transformation Fields
143+
144+
All geometry types expose computed transformation fields:
145+
146+
| Field | Returns | Description |
147+
|-------|---------|-------------|
148+
| `centroid` | `[x, y]` | Mean of all coordinates |
149+
| `bbox` | `[minX, minY, maxX, maxY]` | Bounding box |
150+
| `numPoints` | `Int` | Total coordinate count |
151+
106152
### Spatial Filter Operators
107153

108154
The connection filter PostGIS plugin exposes these operators on geometry/geography columns:
@@ -122,8 +168,34 @@ The connection filter PostGIS plugin exposes these operators on geometry/geograp
122168
| `overlaps` | geometry | Same dimension, share space, not fully contained |
123169
| `touches` | geometry | At least one common point, interiors don't intersect |
124170
| `within` | geometry | A is completely inside B |
171+
| `orderingEquals` | geometry | Same geometry and same point ordering |
125172
| `intersects3D` | geometry | Share any portion of space in 3D |
126173

174+
#### Distance Operator
175+
176+
| Operator | Works On | Description |
177+
|----------|----------|-------------|
178+
| `withinDistance` | geometry, geography | Within a given distance (ST_DWithin) |
179+
180+
`withinDistance` is a compound input — it takes a `point` (GeoJSON geometry) and a `distance` (Float, meters for geography, SRID units for geometry):
181+
182+
```graphql
183+
{
184+
restaurants(
185+
where: {
186+
location: {
187+
withinDistance: {
188+
point: { type: "Point", coordinates: [-73.99, 40.73] }
189+
distance: 5000 # 5km for geography columns
190+
}
191+
}
192+
}
193+
) {
194+
nodes { id name }
195+
}
196+
}
197+
```
198+
127199
#### Bounding Box Operators
128200

129201
| Operator | Description |
@@ -218,6 +290,26 @@ const result = await db.location.findMany({
218290
}).execute();
219291
```
220292

293+
```typescript
294+
// Find restaurants within 5km of a point
295+
const result = await db.restaurant.findMany({
296+
where: {
297+
location: {
298+
withinDistance: {
299+
point: { type: 'Point', coordinates: [-73.99, 40.73] },
300+
distance: 5000, // meters for geography columns
301+
},
302+
},
303+
cuisine: { equalTo: 'italian' },
304+
},
305+
select: {
306+
id: true,
307+
name: true,
308+
location: { x: true, y: true },
309+
},
310+
}).execute();
311+
```
312+
221313
```typescript
222314
// Find zones that contain a point
223315
const result = await db.zone.findMany({
@@ -238,6 +330,21 @@ const result = await db.zone.findMany({
238330

239331
---
240332

333+
## Aggregate Functions
334+
335+
The PostGIS plugin registers aggregate functions for geometry columns:
336+
337+
| Function | Description |
338+
|----------|-------------|
339+
| `ST_Extent` | Bounding box of all geometries |
340+
| `ST_Union` | Union of all geometries into one |
341+
| `ST_Collect` | Collect all geometries into a GeometryCollection |
342+
| `ST_ConvexHull` | Convex hull of all geometries |
343+
344+
These are available through the Graphile aggregates system when enabled.
345+
346+
---
347+
241348
## Combining PostGIS with Text Search
242349

243350
PostGIS spatial queries can be combined with text search filters for location-aware search:
@@ -268,9 +375,17 @@ const result = await db.restaurant.findMany({
268375

269376
## When to Use PostGIS
270377

271-
- Proximity search ("find restaurants within 5km")
272-
- Geofencing ("is this point inside this boundary?")
273-
- Spatial containment ("which zone contains this location?")
378+
- Proximity search ("find restaurants within 5km") — use `withinDistance`
379+
- Geofencing ("is this point inside this boundary?") — use `contains` / `coveredBy`
380+
- Spatial containment ("which zone contains this location?") — use `within`
381+
- Area/length calculations — use `area`, `length`, `perimeter` fields
382+
- Bounding box queries — use `bbox` field or `bboxContains` filter
274383
- Route and path queries
275384
- Any query involving geographic or geometric relationships
276385
- Combined with text search for location-aware search experiences
386+
387+
## Cross-References
388+
389+
- For database setup and Docker: See [pgpm skill](../../pgpm/SKILL.md) and [pgpm Docker reference](../../pgpm/references/docker.md)
390+
- For plugin implementation details: See `graphile/graphile-postgis/` in `constructive-io/constructive`
391+
- For codegen and ORM patterns: See [codegen-orm-patterns.md](./codegen-orm-patterns.md)

.agents/skills/pgpm/references/cli.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,22 @@ pgpm deploy --database mydb
4949

5050
# Deploy specific package to a tag
5151
pgpm deploy --package mypackage --to @v1.0.0
52-
53-
# Fast deployment (no transactions)
54-
pgpm deploy --fast --no-tx
5552
```
5653

54+
> **⚠️ WARNING: `--fast` flag (use with extreme caution)**
55+
>
56+
> ```bash
57+
> pgpm deploy --fast --no-tx
58+
> ```
59+
>
60+
> `--fast` is **NOT idempotent** — it is meant to be run **once only** for
61+
> quick testing on a fresh database. It skips dependency-tracking checks, so
62+
> if your package shares dependencies with anything already deployed (e.g.
63+
> `pgpm-verify`), it will blindly attempt to re-deploy them, causing errors.
64+
>
65+
> **Only use `--fast` on a throwaway database that has nothing else deployed.**
66+
> For all other cases, use the standard `pgpm deploy` command.
67+
5768
**pgpm verify** — Verify database state matches expected migrations
5869
5970
```bash

0 commit comments

Comments
 (0)