Using KEYS_ONLY Global Secondary Indexes
It is common to define a Global Secondary Index using a KEYS_ONLY
projection. This ProjectionType
helps keep indexes as small as possible which can reduce cost. Unfortunately, because ElectroDB abstracts away key management, querying this index type with ElectroDB requires unique considerations.
Example Setup
Example Setup
Table Definition
{ "TableName": "electro", "KeySchema":[ { "AttributeName":"pk", "KeyType":"HASH" }, { "AttributeName":"sk", "KeyType":"RANGE" } ], "AttributeDefinitions":[ { "AttributeName":"pk", "AttributeType":"S" }, { "AttributeName":"sk", "AttributeType":"S" }, { "AttributeName":"gsi1pk", "AttributeType":"S" }, { "AttributeName":"gsi1sk", "AttributeType":"S" } ], "GlobalSecondaryIndexes":[ { "IndexName":"gsi1pk-gsi1sk-index", "KeySchema":[ { "AttributeName":"gsi1pk", "KeyType":"HASH" }, { "AttributeName":"gsi1sk", "KeyType":"RANGE" } ], "Projection":{ "ProjectionType":"ALL" } } ], "BillingMode":"PAY_PER_REQUEST" }
Example Entity
import DynamoDB from "aws-sdk/clients/dynamodb"; import { Entity } from 'electrodb'; const client = new DynamoDB.DocumentClient(); const table = 'electro'; const assets = new Entity({ model: { entity: 'assets', version: '1', service: 'inventory' }, attributes: { assetId: { type: 'string' }, accountId: { type: 'string' }, name: { type: 'string' }, description: { type: 'string' }, city: { type: 'string' }, county: { type: 'string' }, state: { type: 'string' }, zip: { type: 'string' } }, indexes: { assets: { pk: { field: 'pk', composite: ['accountId'] }, sk: { field: 'sk', composite: ['assetId'] } }, locations: { collection: 'geographics', index: 'gsi1pk-gsi1sk-index', pk: { field: 'gsi1pk', composite: ['state'], }, sk: { field: 'gsi1sk', composite: ['county', 'city', 'zip'], } } } }, { table, client });
Hydrating Queries
The execution option hydrate
can be used instruct ElectroDB to perform a query
followed by an immediate batchGet
to retrieve each individual item. Hydrate is available when performing query
, match
, or find
operations.
_Note: Without hydration, a
KEYS_ONLY
index will likely fail. This is because ElectroDB adds additional attribute filters to queries to aid with entity isolation. These attributes won’t be present on aKEYS_ONLY
index, so DynamoDB will throw. To circumvent this, checkout the Returning Keys section below.
// the locations index (gsi1pk-gsi1sk-index) is a KEYS_ONLY projection
const { data, cursor } = await assets.query
.locations({ state: "Georgia" })
.go({ hydrate: true });
Returning Keys
If you do not wish to “hydrate” your query response, you still have ways you can retrieve the keys returned by DynamoDB. The first mechanism is to use the query execution options { ignoreOwnership: true, data: 'includeKeys' }
.
Note: When using typescript these options do not impact the typing of the returned object so this approach may require casting
Include Keys
The query execution options { ignoreOwnership: true, data: 'includeKeys' }
allow you to return keys on the data
array. The option ignoreOwnership
is also used here to limit filters applied to query. ElectroDB filters items that it cannot identify as belonging to an Entity; KEYS_ONLY
items are then indistinguishable from unidentifiable items.
// The elements in the `data` array are just the keys of the index, despite the typing saying otherwise.
const { data, cursor } = await assets.query
.locations({ state: "Georgia" })
.go({ ignoreOwnership: true, data: "includeKeys" });
Raw Responses
The query execution option { data: 'raw' }
will return the raw response from DynamoDB. This is one way you can access the results of a KEYS_ONLY
index.
// result is the actual DynamoDB response, despite the typing saying otherwise.
const result: any = await assets.query
.locations({ state: "Georgia" })
.go({ data: "raw" });
const { Items, LastEvaluatedKey } = result;