Geo Library for Amazon DynamoDB
This project is an unofficial port of awslabs/dynamodb-geo, bringing creation and querying of geospatial data to Node JS developers using Amazon DynamoDB.
Features
- Box Queries: Return all of the items that fall within a pair of geo points that define a rectangle as projected onto a sphere.
- Radius Queries: Return all of the items that are within a given radius of a geo point.
- Basic CRUD Operations: Create, retrieve, update, and delete geospatial data items.
- Customizable: Access to raw request and result objects from the AWS SDK for javascript.
- Fully Typed: This port is written in typescript and declaration files are bundled into releases.
Installation
Using npm or yarn:
npm install --save dynamodb-geo
or yarn add dynamodb-geo
.
Getting started
First you'll need to import the AWS sdk and set up your DynamoDB connection:
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB({ endpoint: new AWS.Endpoint('http://localhost:8000') });
Next you must create an instance of GeoDataManagerConfiguration
for each geospatial table you wish to interact with. This is a container for various options (see API below), but you must always provide a DynamoDB
instance and a table name.
const ddbGeo = require('dynamodb-geo');
const config = new ddbGeo.GeoDataManagerConfiguration(ddb, 'MyGeoTable');
You may modify the config to change defaults.
config.longitudeFirst = true;
Finally, you should instantiate a manager to query and write to the table using this config object.
const myGeoTableManager = new ddbGeo.GeoDataManager(config);
Creating a table
GeoTableUtil
has a static method getCreateTableRequest
for helping you prepare a DynamoDB CreateTable request request, given a GeoDataManagerConfiguration
.
You can modify this request as desired before executing it using AWS's DynamoDB SDK.
Example:
const createTableInput = ddbGeo.GeoTableUtil.getCreateTableRequest(config);
createTableInput.ProvisionedThroughput.ReadCapacityUnits = 2;
console.log('Creating table with schema:');
console.dir(createTableInput, { depth: null });
ddb.createTable(createTableInput).promise()
.then(function () { return ddb.waitFor('tableExists', { TableName: config.tableName }).promise() })
.then(function () { console.log('Table created and ready!') });
Adding data
myGeoTableManager.putPoint({
RangeKeyValue: { S: '1234' },
GeoPoint: {
latitude: 51.51,
longitude: -0.13
},
PutItemInput: {
Item: {
country: { S: 'UK' },
capital: { S: 'London' }
}
}
}).promise()
.then(function() { console.log('Done!') });
See also DynamoDB PutItem request
Updating a specific point
Note that you cannot update the hash key, range key, geohash or geoJson. If you want to change these, you'll need to recreate the record.
You must specify a RangeKeyValue
, a GeoPoint
, and an UpdateItemInput
matching the DynamoDB UpdateItem request (TableName
and Key
are filled in for you).
myGeoTableManager.updatePoint({
RangeKeyValue: { S: '1234' },
GeoPoint: {
latitude: 51.51,
longitude: -0.13
},
UpdateItemInput: {
UpdateExpression: 'SET country = :newName',
ExpressionAttributeValues: {
':newName': { S: 'United Kingdom'}
}
}
}).promise()
.then(function() { console.log('Done!') });
Deleting data
TODO: Docs
Rectangular queries
TODO: Docs
Radius queries
Query by radius by specifying a CenterPoint
and RadiusInMeter
.
myGeoTableManager.queryRadius({
RadiusInMeter: 100000,
CenterPoint: {
latitude: 52.225730,
longitude: 0.149593
}
})
.then(console.log);
Batch operations
TODO: Docs (see the example for an example of a batch write)
Configuration reference
These are public properties of a GeoDataManagerConfiguration
instance. After creating the config object you may modify these properties.
consistentRead: boolean = false
Whether queries use the ConsistentRead
option (for strongly consistent reads) or not (for eventually consistent reads, at half the cost).
This can also be overridden for individual queries as a query config option.
longitudeFirst: boolean = true
This library will automatically add GeoJSON-style position data to your stored items. The GeoJSON standard uses [lon,lat]
ordering, but awslabs/dynamodb-geo uses [lat,lng]
.
This fork allows you to choose between awslabs/dynamodb-geo compatibility and GeoJSON standard compliance.
- Use
false
([lat, lon]
) for compatibility with awslabs/dynamodb-geo - Use
true
([lon, lat]
) for GeoJSON standard compliance. (default)
Note that this value should match the state of your existing data - if you change it you must update your database manually, or you'll end up with ambiguously mixed data.
geohashAttributeName: string = "geohash"
The name of the attribute storing the full 64-bit geohash. Its value is auto-generated based on item coordinates.
hashKeyAttributeName: string = "hashKey"
The name of the attribute storing the first hashKeyLength
digits (default 2) of the geo hash, used as the hash (aka partition) part of a hash/range primary key pair. Its value is auto-generated based on item coordinates.
hashKeyLength: number = 2
The number of digits (in base 10) of the 64-bit geo hash to use as the hash key.
rangeKeyAttributeName: string = "rangeKey"
The name of the attribute storing the range key, used as the range (aka sort) part of a hash/range key primary key pair. Its value must be specified by you (hash-range pairs must be unique).
geoJsonAttributeName: string = "geoJson"
The name of the attribute which will contain the longitude/latitude pair in a GeoJSON-style point (see also longitudeFirst
).
geohashIndexName: string = "geohash-index"
The name of the index to be created against the geohash. Only used for creating new tables.
Example
See the example on Github
Limitations
No composite key support
Currently, the library does not support composite keys. You may want to add tags such as restaurant, bar, and coffee shop, and search locations of a specific category; however, it is currently not possible. You need to create a table for each tag and store the items separately.
Queries retrieve all paginated data
Although low level DynamoDB Query requests return paginated results, this library automatically pages through the entire result set. When querying a large area with many points, a lot of Read Capacity Units may be consumed.
More Read Capacity Units
The library retrieves candidate Geo points from the cells that intersect the requested bounds. The library then post-processes the candidate data, filtering out the specific points that are outside the requested bounds. Therefore, the consumed Read Capacity Units will be higher than the final results dataset.
High memory consumption
Because all paginated Query
results are loaded into memory and processed, it may consume substantial amounts of memory for large datasets.
Dataset density limitation
The Geohash used in this library is roughly centimeter precision. Therefore, the library is not suitable if your dataset has much higher density.