• Start

Geospatial

Location-based patterns

Apply geospatial modelling in SurrealDB to store-of-interest search, delivery zones, asset tracking, and other location-first product features.

Once you can store geometry types and run spatial queries with distance in mind, most product features follow a small set of patterns.

Store each site's coordinates as a Point, optionally with a category and opening hours. At query time, supply the user's location, compute distance or bounding-box filters, and sort by proximity. For dense datasets, consider geohash prefixes or spatial indexes (where supported) to avoid scanning the whole table.

For example, consider records used to store millions of events by location. These can use array-based record IDs that start with a geohash, making it easy to query every record with exactly this hash and no need to use any further filtering.

DEFINE FUNCTION fn::create_geo_record($point: point) {
LET $hash = geo::hash::encode($point, 4);
CREATE ONLY event:[$hash, $point, rand::ulid()];
};

fn::create_geo_record((50.0, 50.1));
fn::create_geo_record((50.1, 50.1));
fn::create_geo_record((50.5, 50.1));

SELECT * FROM event:['v0gt', NONE, NONE]..['v0gt', .., ..];

Output: returns two of three above events

[
{
id: event:[
'v0gt',
(50f, 50.1f),
'01KNTYS85HQZ7T4D066MAVA3G9'
]
},
{
id: event:[
'v0gt',
(50.1f, 50.1f),
'01KNTYS85H8JHM3T9YKGMT2BV8'
]
}
]

Represent coverage as polygons (or multipolygons for islands). New customers or orders are classified with point-in-polygon tests.

For a single zone record, store the boundary as a polygon geometry and test whether a customer’s location falls inside it using CONTAINS (polygon on the left, point on the right):

DEFINE TABLE service_area SCHEMAFULL;
DEFINE FIELD name ON service_area TYPE string;
DEFINE FIELD boundary ON service_area TYPE geometry<polygon>;

CREATE service_area:greater_london SET
name = "Greater London",
boundary = {
type: "Polygon",
coordinates: [[
[-0.38314819, 51.37692386],
[0.1785278, 51.37692386],
[0.1785278, 51.61460570],
[-0.38314819, 51.61460570],
[-0.38314819, 51.37692386]
]]
};

LET $customer = (-0.118092, 51.509865);
SELECT name FROM service_area WHERE boundary CONTAINS $customer;

Output

[
{
name: 'Greater London'
}
]

A point outside the ring (for example further north) returns no records. For overlapping territories, you may return several matches and disambiguate with priority, postal rules, or extra fields on service_area.

For vehicles or assets reporting on a schedule, store recent positions as points with timestamps and use line strings or buffers when you care about adherence to a route corridor rather than a single snapshot.

Was this page helpful?