Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/06-concepts/02-models/01-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fields:
employees: List<Employee>
```

Supported types are [bool](https://api.dart.dev/dart-core/bool-class.html), [int](https://api.dart.dev/dart-core/int-class.html), [double](https://api.dart.dev/dart-core/double-class.html), [String](https://api.dart.dev/dart-core/String-class.html), [Duration](https://api.dart.dev/dart-core/Duration-class.html), [DateTime](https://api.dart.dev/dart-core/DateTime-class.html), [ByteData](https://api.dart.dev/dart-typed_data/ByteData-class.html), [UuidValue](https://pub.dev/documentation/uuid/latest/uuid_value/UuidValue-class.html), [Uri](https://api.dart.dev/dart-core/Uri-class.html), [BigInt](https://api.dart.dev/dart-core/BigInt-class.html), [Vector](./models/vector-fields#vector), [HalfVector](./models/vector-fields#halfvector), [SparseVector](./models/vector-fields#sparsevector), [Bit](./models/vector-fields#bit) and other serializable [classes](#class), [exceptions](#exception) and [enums](#enum). You can also use [List](https://api.dart.dev/dart-core/List-class.html)s, [Map](https://api.dart.dev/dart-core/Map-class.html)s and [Set](https://api.dart.dev/dart-core/Set-class.html)s of the supported types, just make sure to specify the types. All supported types can also be used inside [Record](https://api.dart.dev/dart-core/Record-class.html)s. Null safety is supported. Once your classes are generated, you can use them as parameters or return types to endpoint methods.
Supported types are [bool](https://api.dart.dev/dart-core/bool-class.html), [int](https://api.dart.dev/dart-core/int-class.html), [double](https://api.dart.dev/dart-core/double-class.html), [String](https://api.dart.dev/dart-core/String-class.html), [Duration](https://api.dart.dev/dart-core/Duration-class.html), [DateTime](https://api.dart.dev/dart-core/DateTime-class.html), [ByteData](https://api.dart.dev/dart-typed_data/ByteData-class.html), [UuidValue](https://pub.dev/documentation/uuid/latest/uuid_value/UuidValue-class.html), [Uri](https://api.dart.dev/dart-core/Uri-class.html), [BigInt](https://api.dart.dev/dart-core/BigInt-class.html), [Vector](./models/vector-fields#vector), [HalfVector](./models/vector-fields#halfvector), [SparseVector](./models/vector-fields#sparsevector), [Bit](./models/vector-fields#bit), [GeographyPoint](./models/geography-fields#geographypoint), [GeographyLineString](./models/geography-fields#geographylinestring), [GeographyPolygon](./models/geography-fields#geographypolygon), [GeographyGeometryCollection](./models/geography-fields#geographygeometrycollection) and other serializable [classes](#class), [exceptions](#exception) and [enums](#enum). You can also use [List](https://api.dart.dev/dart-core/List-class.html)s, [Map](https://api.dart.dev/dart-core/Map-class.html)s and [Set](https://api.dart.dev/dart-core/Set-class.html)s of the supported types, just make sure to specify the types. All supported types can also be used inside [Record](https://api.dart.dev/dart-core/Record-class.html)s. Null safety is supported. Once your classes are generated, you can use them as parameters or return types to endpoint methods.

### Required fields

Expand Down
62 changes: 62 additions & 0 deletions docs/06-concepts/02-models/04-geography-fields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Geography fields

Geography types are used for storing geospatial data on the surface of the Earth. They are stored as PostGIS geography columns in PostgreSQL using the WGS 84 coordinate system (SRID 4326), which is the standard used by GPS.

All geography types support spatial filter operations such as proximity search, intersection, containment, and distance-based ordering. See the [Geography operators](../database/filter#geography-operators) section for details.

To ensure optimal performance with spatial queries, consider creating a GIST index on your geography fields. See the [Geography indexes](../database/indexing#geography-indexes) section for more details.

:::info
The usage of Geography fields requires the PostGIS PostgreSQL extension to be installed. To set up PostGIS in a new or existing project, see the [Upgrading to PostGIS support](../../upgrading/upgrade-to-postgis) guide.
:::

## GeographyPoint

The `GeographyPoint` type stores a single geographic location defined by longitude and latitude.

```yaml
class: Store
table: store
fields:
name: String
location: GeographyPoint
address: String?
```

## GeographyLineString

The `GeographyLineString` type stores an ordered sequence of points forming a path or route.

```yaml
class: DeliveryRoute
table: delivery_route
fields:
name: String
path: GeographyLineString
description: String?
```

## GeographyPolygon

The `GeographyPolygon` type stores a closed region defined by an exterior ring and optional interior holes.

```yaml
class: DeliveryZone
table: delivery_zone
fields:
name: String
boundary: GeographyPolygon
description: String?
```

## GeographyGeometryCollection

The `GeographyGeometryCollection` type stores a collection of mixed geography types — points, lines, and polygons — as a single field.

```yaml
class: Region
table: region
fields:
name: String
features: GeographyGeometryCollection
```
20 changes: 20 additions & 0 deletions docs/06-concepts/06-database/04-indexing.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,23 @@ If more than one distance function is going to be frequently used on the same ve
:::

For more details on vector indexes and its configuration, refer to the [pgvector extension documentation](https://github.com/pgvector/pgvector/tree/master?tab=readme-ov-file#indexing).

### Geography indexes

Geography columns benefit from GIST (Generalized Search Tree) spatial indexes, which significantly improve the performance of spatial queries such as proximity searches, intersection tests, and containment checks.

```yaml
class: Store
table: store
fields:
name: String
location: GeographyPoint
indexes:
store_location_idx:
fields: location
type: gist
```

:::tip
A GIST index on a geography column accelerates all spatial operations (`intersects`, `dwithin`, `distance`, `contains`, `within`). For tables with many rows and frequent spatial queries, adding a GIST index is strongly recommended.
:::
89 changes: 89 additions & 0 deletions docs/06-concepts/06-database/06-filter.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,3 +404,92 @@ await User.db.find(
```

In the example we fetch all users that have only "book" orders.

## Geography operators

All geography field types (`GeographyPoint`, `GeographyLineString`, `GeographyPolygon`, `GeographyGeometryCollection`) support spatial filter and ordering operations.

### intersects

Returns rows where the geography column spatially intersects the given geography value. Wraps `ST_Intersects`.

```dart
var point = GeographyPoint(longitude: 2.35, latitude: 48.85);

await Store.db.find(
session,
where: (t) => t.location.intersects(point),
);
```

### dwithin

Returns rows where the geography column is within a given distance (in metres) of the given geography value. Wraps `ST_DWithin`.

```dart
var point = GeographyPoint(longitude: 2.35, latitude: 48.85);

await Store.db.find(
session,
where: (t) => t.location.dwithin(point, 1000),
);
```

### distance

Returns the distance in metres between the geography column and the given geography value. Wraps `ST_Distance`. The result can be used in `orderBy` for nearest-first ordering, or compared numerically in `where`.

```dart
var point = GeographyPoint(longitude: 2.35, latitude: 48.85);

// Order results nearest-first
await Store.db.find(
session,
orderBy: (t) => t.location.distance(point),
);

// Filter by distance and order results nearest-first
await Store.db.find(
session,
where: (t) => t.location.distance(point) < 5000,
orderBy: (t) => t.location.distance(point),
);
```

### contains

Returns rows where the geography column fully contains the given geography value. Wraps `ST_Covers`.

```dart
var point = GeographyPoint(longitude: 2.35, latitude: 48.85);

await DeliveryZone.db.find(
session,
where: (t) => t.boundary.contains(point),
);
```

### within

Returns rows where the geography column is fully within the given geography value. Wraps `ST_CoveredBy`.

```dart
var zone = GeographyPolygon(
exteriorRing: [
GeographyPoint(longitude: 2.30, latitude: 48.82),
GeographyPoint(longitude: 2.40, latitude: 48.82),
GeographyPoint(longitude: 2.40, latitude: 48.90),
GeographyPoint(longitude: 2.30, latitude: 48.90),
GeographyPoint(longitude: 2.30, latitude: 48.82),
],
);

await Store.db.find(
session,
where: (t) => t.location.within(zone),
);
```

:::tip
For optimal performance with spatial queries, consider creating a GIST index on your geography fields. See the [Geography indexes](indexing#geography-indexes) section for more details.
:::
116 changes: 116 additions & 0 deletions docs/09-upgrading/05-upgrade-to-postgis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Upgrading to PostGIS support

New Serverpod projects do not include PostGIS by default. To use geography fields in your models, you need a PostgreSQL instance with the PostGIS extension installed.

:::info
This upgrade is only necessary if you want to use geography fields in your models. If you do not plan to use geography fields, you can skip this upgrade.
:::

:::warning
If trying to use geography fields without upgrading, you will encounter an error when applying migrations.
:::

## For Docker-based environments

1. Update your `docker-compose.yml` to use a PostgreSQL image with PostGIS (e.g., `postgis/postgis:16-3.5`):

```yaml
services:
postgres:
image: postgis/postgis:16-3.5 # <-- Change from postgres image here
ports:
- '8090:5432'
environment:
POSTGRES_USER: postgres
POSTGRES_DB: <projectname>
POSTGRES_PASSWORD: <DB_PASSWORD>
volumes:
- <projectname>_data:/var/lib/postgresql/data

# Other services...

postgres_test:
image: postgis/postgis:16-3.5 # <-- Change from postgres image here
ports:
- '9090:5432'
environment:
POSTGRES_USER: postgres
POSTGRES_DB: <projectname>_test
POSTGRES_PASSWORD: <DB_TEST_PASSWORD>
volumes:
- <projectname>_test_data:/var/lib/postgresql/data
```

If your project also uses [vector fields](../concepts/models/vector-fields), you need both pgvector and PostGIS. Create a custom `Dockerfile` instead:

```dockerfile
FROM pgvector/pgvector:pg16
RUN apt-get update \
&& apt-get install -y --no-install-recommends postgresql-16-postgis-3 \
&& rm -rf /var/lib/apt/lists/*
```

Then reference it in your `docker-compose.yml`:

```yaml
services:
postgres:
build:
context: .
dockerfile: Dockerfile
ports:
- '8090:5432'
environment:
POSTGRES_USER: postgres
POSTGRES_DB: <projectname>
POSTGRES_PASSWORD: <DB_PASSWORD>
volumes:
- <projectname>_data:/var/lib/postgresql/data
```

<!-- markdownlint-disable-next-line MD029-->
2. Recreate your containers to use the new image:

```bash
docker compose down
docker compose up -d
```

<!-- markdownlint-disable-next-line MD029-->
3. Create your first geography field in a model:

```yaml
class: Store
table: store
fields:
name: String
location: GeographyPoint
```

<!-- markdownlint-disable-next-line MD029-->
4. Generate and apply a migration:

```bash
$ serverpod create-migration
$ dart run bin/main.dart --apply-migrations
```

For more details on creating and applying migrations, see the [Migrations](../concepts/database/migrations) section.

The PostGIS extension will be automatically enabled during the first migration that includes a geography column.

## For managed PostgreSQL services

For cloud providers (AWS RDS, Google Cloud SQL, Azure Database, etc.), ensure that the PostGIS extension is available on your PostgreSQL instance. Most major managed services support PostGIS with no additional setup required. If available, the extension will be enabled automatically when applying the migration.

:::tip
If the cloud provider instructs you to run a `CREATE EXTENSION postgis;` command, you can skip this step as Serverpod will handle it automatically during migration.
:::

## Troubleshooting

If you encounter issues with PostGIS:

- Verify that your PostgreSQL version is 12 or later.
- Check that the PostGIS extension is properly installed on the instance.
- Ensure your database user has the necessary permissions to create extensions.
Loading