ArangoDB v2.8 reached End of Life (EOL) and is no longer supported.
This documentation is outdated. Please see the most recent version here: Try latest
Geo Indexes
Introduction to Geo Indexes
This is an introduction to ArangoDB’s geo indexes.
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.
Accessing Geo Indexes from the Shell
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:
arangosh> db.geo.ensureIndex({ type: "geo", fields: [ "loc" ] });
{
"id" : "geo/1901414529",
"type" : "geo1",
"fields" : [
"loc"
],
"geoJson" : false,
"constraint" : false,
"unique" : false,
"ignoreNull" : true,
"sparse" : true,
"isNewlyCreated" : true,
"code" : 201
}
arangosh> for (i = -90; i <= 90; i += 10) {
........> for (j = -180; j <= 180; j += 10) {
........> db.geo.save({ name : "Name/" + i + "/" + j, loc: [ i, j ] });
........> }
........> }
arangosh> db.geo.count();
703
arangosh> db.geo.near(0, 0).limit(3).toArray();
[
{
"_id" : "geo/1970817153",
"_key" : "1970817153",
"_rev" : "1970817153",
"name" : "Name/0/0",
"loc" : [
0,
0
]
},
{
"_id" : "geo/1971013761",
"_key" : "1971013761",
"_rev" : "1971013761",
"name" : "Name/0/10",
"loc" : [
0,
10
]
},
{
"_id" : "geo/1963542657",
"_key" : "1963542657",
"_rev" : "1963542657",
"name" : "Name/-10/0",
"loc" : [
-10,
0
]
}
]
arangosh> db.geo.near(0, 0).count();
null
arangosh> db.geo.ensureIndex({ type: "geo", fields: [ "loc" ] });
arangosh> for (i = -90; i <= 90; i += 10) {
........> for (j = -180; j <= 180; j += 10) {
........> db.geo.save({ name : "Name/" + i + "/" + j, loc: [ i, j ] });
........> }
........> }
arangosh> db.geo.count();
arangosh> db.geo.near(0, 0).limit(3).toArray();
arangosh> db.geo.near(0, 0).count();
Create a geo index for a hash array attribute:
arangosh> db.geo2.ensureIndex({ type: "geo", fields: [ "location.latitude", "location.longitude" ] });
{
"id" : "geo2/2041006209",
"type" : "geo2",
"fields" : [
"location.latitude",
"location.longitude"
],
"constraint" : false,
"unique" : false,
"ignoreNull" : true,
"sparse" : true,
"isNewlyCreated" : true,
"code" : 201
}
arangosh> 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 } });
........> }
........> }
arangosh> db.geo2.near(0, 0).limit(3).toArray();
[
{
"_id" : "geo2/2110408833",
"_key" : "2110408833",
"_rev" : "2110408833",
"location" : {
"latitude" : 0,
"longitude" : 0
},
"name" : "Name/0/0"
},
{
"_id" : "geo2/2110605441",
"_key" : "2110605441",
"_rev" : "2110605441",
"location" : {
"latitude" : 0,
"longitude" : 10
},
"name" : "Name/0/10"
},
{
"_id" : "geo2/2103134337",
"_key" : "2103134337",
"_rev" : "2103134337",
"location" : {
"latitude" : -10,
"longitude" : 0
},
"name" : "Name/-10/0"
}
]
arangosh> db.geo2.ensureIndex({ type: "geo", fields: [ "location.latitude", "location.longitude" ] });
arangosh> 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 } });
........> }
........> }
arangosh> db.geo2.near(0, 0).limit(3).toArray();
collection.geo(location-attribute)
Looks up a geo index defined on attribute location_attribute.
Returns a geo index object if an index was found. The near
or
within
operators can then be used to execute a geo-spatial query on
this particular index.
This is useful for collections with multiple defined geo indexes.
collection.geo(location_attribute, true)
Looks up a geo index on a compound attribute location_attribute.
Returns a geo index object if an index was found. The near
or
within
operators can then be used to execute a geo-spatial query on
this particular index.
collection.geo(latitude_attribute, longitude_attribute)
Looks up a geo index defined on the two attributes latitude_attribute
and longitude-attribute.
Returns a geo index object if an index was found. The near
or
within
operators can then be used to execute a geo-spatial query on
this particular index.
Note: the geo simple query helper function is deprecated as of ArangoDB
2.6. The function may be removed in future versions of ArangoDB. The preferred
way for running geo queries is to use their AQL equivalents.
Examples
Assume you have a location stored as list in the attribute home
and a destination stored in the attribute work. Then you can use the
geo
operator to select which geo-spatial attributes (and thus which
index) to use in a near
query.
arangosh> for (i = -90; i <= 90; i += 10) {
........> for (j = -180; j <= 180; j += 10) {
........> db.complex.save({ name : "Name/" + i + "/" + j,
........> home : [ i, j ],
........> work : [ -i, -j ] });
........> }
........> }
........>
arangosh> db.complex.near(0, 170).limit(5);
[ArangoError 1570: no suitable geo index found for geo restriction on 'complex']
arangosh> db.complex.ensureIndex({ type: "geo", fields: [ "home" ] });
{
"id" : "complex/2318682241",
"type" : "geo1",
"fields" : [
"home"
],
"geoJson" : false,
"constraint" : false,
"unique" : false,
"ignoreNull" : true,
"sparse" : true,
"isNewlyCreated" : true,
"code" : 201
}
arangosh> db.complex.near(0, 170).limit(5).toArray();
[
{
"_id" : "complex/2252753025",
"_key" : "2252753025",
"_rev" : "2252753025",
"name" : "Name/0/170",
"home" : [
0,
170
],
"work" : [
0,
-170
]
},
{
"_id" : "complex/2252949633",
"_key" : "2252949633",
"_rev" : "2252949633",
"name" : "Name/0/180",
"home" : [
0,
180
],
"work" : [
0,
-180
]
},
{
"_id" : "complex/2245871745",
"_key" : "2245871745",
"_rev" : "2245871745",
"name" : "Name/0/-180",
"home" : [
0,
-180
],
"work" : [
0,
180
]
},
{
"_id" : "complex/2260027521",
"_key" : "2260027521",
"_rev" : "2260027521",
"name" : "Name/10/170",
"home" : [
10,
170
],
"work" : [
-10,
-170
]
},
{
"_id" : "complex/2245478529",
"_key" : "2245478529",
"_rev" : "2245478529",
"name" : "Name/-10/170",
"home" : [
-10,
170
],
"work" : [
10,
-170
]
}
]
arangosh> db.complex.geo("work").near(0, 170).limit(5);
[ArangoError 1570: no suitable geo index found for geo restriction on 'complex']
arangosh> db.complex.ensureIndex({ type: "geo", fields: [ "work" ] });
{
"id" : "complex/2319206529",
"type" : "geo1",
"fields" : [
"work"
],
"geoJson" : false,
"constraint" : false,
"unique" : false,
"ignoreNull" : true,
"sparse" : true,
"isNewlyCreated" : true,
"code" : 201
}
arangosh> db.complex.geo("work").near(0, 170).limit(5).toArray();
[
{
"_id" : "complex/2246068353",
"_key" : "2246068353",
"_rev" : "2246068353",
"name" : "Name/0/-170",
"home" : [
0,
-170
],
"work" : [
0,
170
]
},
{
"_id" : "complex/2245871745",
"_key" : "2245871745",
"_rev" : "2245871745",
"name" : "Name/0/-180",
"home" : [
0,
-180
],
"work" : [
0,
180
]
},
{
"_id" : "complex/2252949633",
"_key" : "2252949633",
"_rev" : "2252949633",
"name" : "Name/0/180",
"home" : [
0,
180
],
"work" : [
0,
-180
]
},
{
"_id" : "complex/2253342849",
"_key" : "2253342849",
"_rev" : "2253342849",
"name" : "Name/10/-170",
"home" : [
10,
-170
],
"work" : [
-10,
170
]
},
{
"_id" : "complex/2238793857",
"_key" : "2238793857",
"_rev" : "2238793857",
"name" : "Name/-10/-170",
"home" : [
-10,
-170
],
"work" : [
10,
170
]
}
]
arangosh> for (i = -90; i <= 90; i += 10) {
........> for (j = -180; j <= 180; j += 10) {
........> db.complex.save({ name : "Name/" + i + "/" + j,
........> home : [ i, j ],
........> work : [ -i, -j ] });
........> }
........> }
........>
arangosh> db.complex.near(0, 170).limit(5);
arangosh> db.complex.ensureIndex({ type: "geo", fields: [ "home" ] });
arangosh> db.complex.near(0, 170).limit(5).toArray();
arangosh> db.complex.geo("work").near(0, 170).limit(5);
arangosh> db.complex.ensureIndex({ type: "geo", fields: [ "work" ] });
arangosh> db.complex.geo("work").near(0, 170).limit(5).toArray();
collection.near(latitude, longitude)
The returned list is sorted according to the distance, with the nearest
document to the coordinate (latitude, longitude) coming first.
If there are near documents of equal distance, documents are chosen randomly
from this set until the limit is reached. It is possible to change the limit
using the limit operator.
In order to use the near operator, a geo index must be defined for the
collection. This index also defines which attribute holds the coordinates
for the document. If you have more then one geo-spatial index, you can use
the geo operator to select a particular index.
Note: near
does not support negative skips.
// However, you can still use limit
followed to skip.
collection.near(latitude, longitude).limit(limit)
Limits the result to limit documents instead of the default 100.
Note: Unlike with multiple explicit limits, limit
will raise
the implicit default limit imposed by within
.
collection.near(latitude, longitude).distance()
This will add an attribute distance
to all documents returned, which
contains the distance between the given point and the document in meters.
collection.near(latitude, longitude).distance(name)
This will add an attribute name to all documents returned, which
contains the distance between the given point and the document in meters.
Note: the near simple query function is deprecated as of ArangoDB 2.6.
The function may be removed in future versions of ArangoDB. The preferred
way for retrieving documents from a collection using the near operator is
to use the AQL NEAR function in an AQL query as follows:
FOR doc IN NEAR(@@collection, @latitude, @longitude, @limit)
RETURN doc
Examples
To get the nearest two locations:
arangosh> db.geo.ensureIndex({ type: "geo", fields: [ "loc" ] });
{
"id" : "geo/26298497",
"type" : "geo1",
"fields" : [
"loc"
],
"geoJson" : false,
"constraint" : false,
"unique" : false,
"ignoreNull" : true,
"sparse" : true,
"isNewlyCreated" : true,
"code" : 201
}
arangosh> for (var i = -90; i <= 90; i += 10) {
........> for (var j = -180; j <= 180; j += 10) {
........> db.geo.save({
........> name : "Name/" + i + "/" + j,
........> loc: [ i, j ] });
........> } }
arangosh> db.geo.near(0, 0).limit(2).toArray();
[
{
"_id" : "geo/95701121",
"_key" : "95701121",
"_rev" : "95701121",
"name" : "Name/0/0",
"loc" : [
0,
0
]
},
{
"_id" : "geo/88426625",
"_key" : "88426625",
"_rev" : "88426625",
"name" : "Name/-10/0",
"loc" : [
-10,
0
]
}
]
arangosh> db.geo.ensureIndex({ type: "geo", fields: [ "loc" ] });
arangosh> for (var i = -90; i <= 90; i += 10) {
........> for (var j = -180; j <= 180; j += 10) {
........> db.geo.save({
........> name : "Name/" + i + "/" + j,
........> loc: [ i, j ] });
........> } }
arangosh> db.geo.near(0, 0).limit(2).toArray();
If you need the distance as well, then you can use the distance
operator:
arangosh> db.geo.ensureIndex({ type: "geo", fields: [ "loc" ] });
{
"id" : "geo/165431425",
"type" : "geo1",
"fields" : [
"loc"
],
"geoJson" : false,
"constraint" : false,
"unique" : false,
"ignoreNull" : true,
"sparse" : true,
"isNewlyCreated" : true,
"code" : 201
}
arangosh> for (var i = -90; i <= 90; i += 10) {
........> for (var j = -180; j <= 180; j += 10) {
........> db.geo.save({
........> name : "Name/" + i + "/" + j,
........> loc: [ i, j ] });
........> } }
arangosh> db.geo.near(0, 0).distance().limit(2).toArray();
[
{
"_id" : "geo/234834049",
"_key" : "234834049",
"_rev" : "234834049",
"name" : "Name/0/0",
"loc" : [
0,
0
],
"distance" : 0
},
{
"_id" : "geo/227559553",
"_key" : "227559553",
"_rev" : "227559553",
"name" : "Name/-10/0",
"loc" : [
-10,
0
],
"distance" : 1111949.2664455874
}
]
arangosh> db.geo.ensureIndex({ type: "geo", fields: [ "loc" ] });
arangosh> for (var i = -90; i <= 90; i += 10) {
........> for (var j = -180; j <= 180; j += 10) {
........> db.geo.save({
........> name : "Name/" + i + "/" + j,
........> loc: [ i, j ] });
........> } }
arangosh> db.geo.near(0, 0).distance().limit(2).toArray();
collection.within(latitude, longitude, radius)
This will find all documents within a given radius around the coordinate
(latitude, longitude). The returned array is sorted by distance,
beginning with the nearest document.
In order to use the within operator, a geo index must be defined for the
collection. This index also defines which attribute holds the coordinates
for the document. If you have more then one geo-spatial index, you can use
the geo
operator to select a particular index.
collection.within(latitude, longitude, radius).distance()
This will add an attribute _distance
to all documents returned, which
contains the distance between the given point and the document in meters.
collection.within(latitude, longitude, radius).distance(name)
This will add an attribute name to all documents returned, which
contains the distance between the given point and the document in meters.
Note: the within simple query function is deprecated as of ArangoDB 2.6.
The function may be removed in future versions of ArangoDB. The preferred
way for retrieving documents from a collection using the within operator is
to use the AQL WITHIN function in an AQL query as follows:
FOR doc IN WITHIN(@@collection, @latitude, @longitude, @radius, @distanceAttributeName)
RETURN doc
Examples
To find all documents within a radius of 2000 km use:
arangosh> for (var i = -90; i <= 90; i += 10) {
........> for (var j = -180; j <= 180; j += 10) {
........> db.geo.save({ name : "Name/" + i + "/" + j, loc: [ i, j ] }); } }
arangosh> db.geo.within(0, 0, 2000 * 1000).distance().toArray();
[
{
"_id" : "geo/373966977",
"_key" : "373966977",
"_rev" : "373966977",
"name" : "Name/0/0",
"loc" : [
0,
0
],
"distance" : 0
},
{
"_id" : "geo/366692481",
"_key" : "366692481",
"_rev" : "366692481",
"name" : "Name/-10/0",
"loc" : [
-10,
0
],
"distance" : 1111949.2664455874
},
{
"_id" : "geo/374163585",
"_key" : "374163585",
"_rev" : "374163585",
"name" : "Name/0/10",
"loc" : [
0,
10
],
"distance" : 1111949.2664455874
},
{
"_id" : "geo/381241473",
"_key" : "381241473",
"_rev" : "381241473",
"name" : "Name/10/0",
"loc" : [
10,
0
],
"distance" : 1111949.2664455874
},
{
"_id" : "geo/373770369",
"_key" : "373770369",
"_rev" : "373770369",
"name" : "Name/0/-10",
"loc" : [
0,
-10
],
"distance" : 1111949.2664455874
},
{
"_id" : "geo/366889089",
"_key" : "366889089",
"_rev" : "366889089",
"name" : "Name/-10/10",
"loc" : [
-10,
10
],
"distance" : 1568520.5567985761
},
{
"_id" : "geo/381438081",
"_key" : "381438081",
"_rev" : "381438081",
"name" : "Name/10/10",
"loc" : [
10,
10
],
"distance" : 1568520.5567985761
},
{
"_id" : "geo/366495873",
"_key" : "366495873",
"_rev" : "366495873",
"name" : "Name/-10/-10",
"loc" : [
-10,
-10
],
"distance" : 1568520.5567985761
},
{
"_id" : "geo/381044865",
"_key" : "381044865",
"_rev" : "381044865",
"name" : "Name/10/-10",
"loc" : [
10,
-10
],
"distance" : 1568520.5567985761
}
]
arangosh> for (var i = -90; i <= 90; i += 10) {
........> for (var j = -180; j <= 180; j += 10) {
........> db.geo.save({ name : "Name/" + i + "/" + j, loc: [ i, j ] }); } }
arangosh> db.geo.within(0, 0, 2000 * 1000).distance().toArray();
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.