6.4 KiB
Geo Indexes
Introduction to Geo Indexes
This is an introduction to ArangoDB's geo indexes.
AQL's geographic features are described in Geo functions.
ArangoDB uses Hilbert curves to implement geo-spatial indexes. See this blog for details.
A geo-spatial index assumes that the latitude is between -90 and 90 degree and the longitude is between -180 and 180 degree. A geo index will ignore all documents which do not fulfill these requirements.
Currently, geo indexes are not yet supported with the RocksDB storage engine. Thus the functions NEAR()
, WITHIN()
and WITHIN_RECTANGLE()
will be unavailable when using this storage engine. To use geo indexes, please use the MMFiles storage engine for the time being.
Accessing Geo Indexes from the Shell
ensures that a geo index exists
collection.ensureIndex({ type: "geo", fields: [ "location" ] })
Creates a geo-spatial index on all documents using location as path to the coordinates. The value of the attribute has to be an array with at least two numeric values. The array must contain the latitude (first value) and the longitude (second value).
All documents, which do not have the attribute path or have a non-conforming value in it are excluded from the index.
A geo index is implicitly sparse, and there is no way to control its sparsity.
In case that the index was successfully created, an object with the index details, including the index-identifier, is returned.
To create a geo on an array attribute that contains longitude first, set the
geoJson attribute to true
. This corresponds to the format described in
positions
collection.ensureIndex({ type: "geo", fields: [ "location" ], geoJson: true })
To create a geo-spatial index on all documents using latitude and longitude as separate attribute paths, two paths need to be specified in the fields array:
collection.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] })
In case that the index was successfully created, an object with the index details, including the index-identifier, is returned.
Examples
Create a geo index for an array attribute:
@startDocuBlockInline geoIndexCreateForArrayAttribute1
@EXAMPLE_ARANGOSH_OUTPUT{geoIndexCreateForArrayAttribute1}
~db._create("geo")
db.geo.ensureIndex({ type: "geo", fields: [ "loc" ] });
| for (i = -90; i <= 90; i += 10) {
| for (j = -180; j <= 180; j += 10) {
| db.geo.save({ name : "Name/" + i + "/" + j, loc: [ i, j ] });
| }
}
db.geo.count();
db.geo.near(0, 0).limit(3).toArray();
db.geo.near(0, 0).count();
~db._drop("geo")
@END_EXAMPLE_ARANGOSH_OUTPUT
@endDocuBlock geoIndexCreateForArrayAttribute1
Create a geo index for a hash array attribute:
@startDocuBlockInline geoIndexCreateForArrayAttribute2
@EXAMPLE_ARANGOSH_OUTPUT{geoIndexCreateForArrayAttribute2}
~db._drop("geo2")
~db._create("geo2")
db.geo2.ensureIndex({ type: "geo", fields: [ "location.latitude", "location.longitude" ] });
| for (i = -90; i <= 90; i += 10) {
| for (j = -180; j <= 180; j += 10) {
| db.geo2.save({ name : "Name/" + i + "/" + j, location: { latitude : i, longitude : j } });
| }
}
db.geo2.near(0, 0).limit(3).toArray();
~db._drop("geo2")
@END_EXAMPLE_ARANGOSH_OUTPUT
@endDocuBlock geoIndexCreateForArrayAttribute2
Use GeoIndex with AQL SORT statement:
@startDocuBlockInline geoIndexSortOptimization
@EXAMPLE_ARANGOSH_OUTPUT{geoIndexSortOptimization}
~db._create("geoSort")
db.geoSort.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] });
| for (i = -90; i <= 90; i += 10) {
| for (j = -180; j <= 180; j += 10) {
| db.geoSort.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j });
| }
}
var query = "FOR doc in geoSort SORT DISTANCE(doc.latitude, doc.longitude, 0, 0) LIMIT 5 RETURN doc"
db._explain(query, {}, {colors: false});
db._query(query);
~db._drop("geoSort")
@END_EXAMPLE_ARANGOSH_OUTPUT
@endDocuBlock geoIndexSortOptimization
Use GeoIndex with AQL FILTER statement:
@startDocuBlockInline geoIndexFilterOptimization
@EXAMPLE_ARANGOSH_OUTPUT{geoIndexFilterOptimization}
~db._create("geoFilter")
db.geoFilter.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] });
| for (i = -90; i <= 90; i += 10) {
| for (j = -180; j <= 180; j += 10) {
| db.geoFilter.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j });
| }
}
var query = "FOR doc in geoFilter FILTER DISTANCE(doc.latitude, doc.longitude, 0, 0) < 2000 RETURN doc"
db._explain(query, {}, {colors: false});
db._query(query);
~db._drop("geoFilter")
@END_EXAMPLE_ARANGOSH_OUTPUT
@endDocuBlock geoIndexFilterOptimization
@startDocuBlock collectionGeo
@startDocuBlock collectionNear
@startDocuBlock collectionWithin
ensures that a geo index exists
collection.ensureIndex({ type: "geo", fields: [ "location" ] })
Since ArangoDB 2.5, this method is an alias for ensureGeoIndex since geo indexes are always sparse, meaning that documents that do not contain the index attributes or have non-numeric values in the index attributes will not be indexed. ensureGeoConstraint is deprecated and ensureGeoIndex should be used instead.
The index does not provide a unique
option because of its limited usability.
It would prevent identical coordinates from being inserted only, but even a
slightly different location (like 1 inch or 1 cm off) would be unique again and
not considered a duplicate, although it probably should. The desired threshold
for detecting duplicates may vary for every project (including how to calculate
the distance even) and needs to be implemented on the application layer as needed.
You can write a Foxx service for this purpose and make use
of the AQL geo functions to find nearby
coordinates supported by a geo index.