A geometry is a type based on the GeoJSON spec that is optimised for working with data pertaining to locations on Earth.
SurrealDB makes working with GeoJSON easy, with support for Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, and Collection values. SurrealQL automatically detects GeoJSON objects converting them into a single data type.
| Type | Description |
|---|---|
Point | A geolocation point with longitude and latitude |
LineString | A GeoJSON LineString value for storing a geometric path |
Polygon | A GeoJSON Polygon value for storing a geometric area |
MultiPoint | A value which contains multiple geometry points |
MultiLineString | A value which contains multiple geometry lines |
MultiPolygon | A value which contains multiple geometry polygons |
Collection | A value which contains multiple different geometry types |
There are two main points to keep in mind when creating a geometry type in SurrealDB. They are:
Point is in the order (longitude, latitude), and not the other way around.geometry created from an object must contain a type field and a coordinates field, no more and no less.This can be shown by calling the type::is_geometry() function on some sample objects.
-- true: has `type` and `coordinates` field with valid data { type: "Point", coordinates: [-0.118092, 51.509865] }.is_geometry(); -- false: lacks 'type' field { coordinates: [-0.118092, 51.509865] }.is_geometry(); -- false: has an extra field { type: "Point", coordinates: [-0.118092, 51.509865], unnecessary: "data" }.is_geometry();
PointThe simplest form of GeoJSON that SurrealDB supports is a geolocation point. These can be written using two different formats. The first format is that of an object that matches the GeoJSON spec.
CREATE city:london SET centre = { type: "Point", coordinates: [-0.118092, 51.509865], };
The other format is a simple 2-element tuple containing the longitude and the latitude of a location. This output for this format is no different from the above, and is simply a convenience due to the frequency of use of the Point type.
CREATE city:london SET centre = (-0.118092, 51.509865);
LineStringA GeoJSON LineString value for storing a geometric path.
CREATE city:london SET distance = { type: "LineString", coordinates: [[-0.118092, 51.509865],[0.1785278, 51.37692386]], };
PolygonA GeoJSON Polygon value for storing a geometric area.
CREATE city:london SET boundary = { type: "Polygon", coordinates: [[ [-0.38314819, 51.37692386], [0.1785278, 51.37692386], [0.1785278, 51.61460570], [-0.38314819, 51.61460570], [-0.38314819, 51.37692386] ]] };
MultiPointMultiPoints can be used to store multiple geometry points in a single value.
CREATE person:tobie SET locations = { type: "MultiPoint", coordinates: [ [10.0, 11.2], [10.5, 11.9] ], };
MultiLineStringA MultiLineString can be used to store multiple geometry lines in a single value.
CREATE travel:yellowstone SET routes = { type: "MultiLineString", coordinates: [ [ [10.0, 11.2], [10.5, 11.9] ], [ [11.0, 12.2], [11.5, 12.9], [12.0, 13.0] ] ] }
MultiPolygonMultiPolygons can be used to store multiple geometry polygons in a single value.
CREATE university:oxford SET locations = { type: "MultiPolygon", coordinates: [ [ [ [10.0, 11.2], [10.5, 11.9], [10.8, 12.0], [10.0, 11.2] ] ], [ [ [9.0, 11.2], [10.5, 11.9], [10.3, 13.0], [9.0, 11.2] ] ] ] };
CollectionCollections can be used to store multiple different geometry types in a single value.
CREATE university:oxford SET buildings = { type: "GeometryCollection", geometries: [ { type: "MultiPoint", coordinates: [ [10.0, 11.2], [10.5, 11.9] ], }, { type: "Polygon", coordinates: [[ [-0.38314819, 51.37692386], [0.1785278, 51.37692386], [0.1785278, 51.61460570], [-0.38314819, 51.61460570], [-0.38314819, 51.37692386] ]] }, { type: "MultiPolygon", coordinates: [ [ [ [10.0, 11.2], [10.5, 11.9], [10.8, 12.0], [10.0, 11.2] ] ], [ [ [9.0, 11.2], [10.5, 11.9], [10.3, 13.0], [9.0, 11.2] ] ] ] } ] };
The following example includes five records from an open database with cities worldwide that have of a population of at least 1000. The queries below create a city record from each entry that includes their name, location, and name. Next, it uses the geo::distance function to find their two closest neighbours, relating them via the close_to relation table. The final query can be viewed in traditional form to see each city’s neighbours, or on Surrealist’s graph view to see a visual representation of the network of closely linked cities.
DEFINE TABLE city SCHEMAFULL; DEFINE FIELD name ON city TYPE string; DEFINE FIELD location ON city TYPE point; FOR $city IN [{"geoname_id": "5881639", "name": "100 Mile House", "ascii_name": "100 Mile House", "feature_class": "P", "feature_code": "PPL", "country_code": "CA", "cou_name_en": "Canada", "country_code_2": null, "admin1_code": "02", "admin2_code": "5941", "admin3_code": "5941005", "admin4_code": null, "population": 1980, "elevation": null, "dem": 928, "timezone": "America/Vancouver", "modification_date": "2019-11-26", "label_en": "Canada", "coordinates": {"lon": -121.28594, "lat": 51.64982}},{"geoname_id": "5896969", "name": "Beaverlodge", "ascii_name": "Beaverlodge", "feature_class": "P", "feature_code": "PPL", "country_code": "CA", "cou_name_en": "Canada", "country_code_2": null, "admin1_code": "01", "admin2_code": "4819009", "admin3_code": null, "admin4_code": null, "population": 2219, "elevation": null, "dem": 723, "timezone": "America/Edmonton", "modification_date": "2024-02-28", "label_en": "Canada", "coordinates": {"lon": -119.43605, "lat": 55.21664}},{"geoname_id": "5911606", "name": "Burnaby", "ascii_name": "Burnaby", "feature_class": "P", "feature_code": "PPLA3", "country_code": "CA", "cou_name_en": "Canada", "country_code_2": null, "admin1_code": "02", "admin2_code": "5915", "admin3_code": "5915025", "admin4_code": null, "population": 202799, "elevation": null, "dem": 87, "timezone": "America/Vancouver", "modification_date": "2019-02-26", "label_en": "Canada", "coordinates": {"lon": -122.95263, "lat": 49.26636}},{"geoname_id": "5920996", "name": "Chertsey", "ascii_name": "Chertsey", "feature_class": "P", "feature_code": "PPL", "country_code": "CA", "cou_name_en": "Canada", "country_code_2": null, "admin1_code": "10", "admin2_code": "14", "admin3_code": "62047", "admin4_code": null, "population": 4836, "elevation": null, "dem": 251, "timezone": "America/Toronto", "modification_date": "2016-06-22", "label_en": "Canada", "coordinates": {"lon": -73.89095, "lat": 46.07109}},{"geoname_id": "5941905", "name": "Dorset Park", "ascii_name": "Dorset Park", "alternate_names": null, "feature_class": "P", "feature_code": "PPLX", "country_code": "CA", "cou_name_en": "Canada", "country_code_2": null, "admin1_code": "08", "admin2_code": "3520", "admin3_code": null, "admin4_code": null, "population": 25003, "elevation": null, "dem": 164, "timezone": "America/Toronto", "modification_date": "2020-05-02", "label_en": "Canada", "coordinates": {"lon": -79.28215, "lat": 43.75386}}] { CREATE type::record("city", <int>$city.geoname_id) SET location = <point>[$city.coordinates.lon, $city.coordinates.lat], name = $city.name; }; FOR $city IN SELECT * FROM city { LET $this_location = $city.location; LET $closest = (SELECT id, location, geo::distance($this_location, location) AS distance FROM city ORDER BY distance ASC LIMIT 3 ).filter(|$c| $c.distance != 0); FOR $closest IN $closest { RELATE $city->close_to->$closest SET distance = geo::distance($city.location, $closest.location); }; }; SELECT name, id, ->close_to->city AS neighbours FROM city;
You’ve now seen how to use geometries to store locations, paths, and polygonal areas in SurrealDB. For more advanced functionality, take a look at the operators and geo functions, which enable area, distance, and bearing geometric calculations, and the ability to detect whether geometries contain or intersect other geometry types.