UNPKG

@diegodeg58/cyclic.sh-dynamodb

Version:

SDK for interacting with Cyclic.sh (https://cyclic.sh) app AWS DynamoDB databases

195 lines (159 loc) 5.11 kB
const { QueryCommand, ScanCommand } = require("@aws-sdk/lib-dynamodb") const { docClient } = require('./ddb_client') const CyclicIndex = require('./cy_db_index') const CyclicItem = require('./cy_db_item') const { validate_strings } = require('./cy_db_utils') const { gen_expression } = require('./expressions') class CyclicCollection { /* * Constructs a CyclicCollection that can be used to interact with collection. * @arg {string} collection - name of the collection * @arg {object} props - optional */ constructor(collection, props = {}) { validate_strings(collection, 'Collection Name') this.collection = collection } /* * Constructs a new CyclicItem addressed by key inside of this collection * @arg {string} key - the key to assign to the new item * @returns {CyclicItem} - new item for the key in this collection */ item(key) { return new CyclicItem(this.collection, key) } /* * Retrieve data from dynamodb associated to key instead of this collection. * @arg {string} key - the key to lookup the item * @returns {CyclicItem} - retrieves the data associated with key from dynamodb */ async get(key) { let item = new CyclicItem(this.collection, key) return item.get() } async set(key, props, opts) { let item = new CyclicItem(this.collection, key) return item.set(props, opts) } async delete(key, props, opts) { let item = new CyclicItem(this.collection, key) return item.delete() } async filter(q = {}, segments = 3, next = null) { if (segments > 5) { segments = 5 } let scans = Array.from({ length: segments }, (_, index) => index + 1); let filter = gen_expression(q) filter.expression = `${filter.expression} AND cy_meta.#kc = :vcol` filter.attr_names[`#kc`] = 'c' filter.attr_vals[`:vcol`] = this.collection let r = { results: [] } let segment_results = await Promise.all(scans.map(s => { return this.parallel_scan(filter, s - 1, segments) })) segment_results.forEach(s => { s.results.forEach(sr => { r.results.push(sr) }) }) return r } async parallel_scan(filter, segment, total_segments, limit = 50000, next = undefined) { let results = [] do { var params = { TableName: process.env.CYCLIC_DB, Limit: limit, ScanIndexForward: false, Segment: segment, TotalSegments: total_segments, ExclusiveStartKey: next, FilterExpression: filter.expression, ExpressionAttributeNames: filter.attr_names, ExpressionAttributeValues: filter.attr_vals, }; let res = await docClient.send(new ScanCommand(params)) next = res.LastEvaluatedKey results = results.concat(res.Items) } while (next && results.length < limit) results = results.map(r => { return CyclicItem.from_dynamo(r) }) let result = { results, length: results.length } if (next) { result.next = next } return result; } async list(limit = 10000, next = undefined) { let results = [] do { var params = { TableName: process.env.CYCLIC_DB, Limit: limit, IndexName: 'keys_gsi', // KeyConditions:{ // keys_gsi:{ // ComparisonOperator:'EQ', // AttributeValueList: [this.collection] // } // }, KeyConditionExpression: 'keys_gsi = :keys_gsi', ExpressionAttributeValues: { ':keys_gsi': this.collection, }, ScanIndexForward: false, ExclusiveStartKey: next }; let res = await docClient.send(new QueryCommand(params)) // var res = await docClient.query(params).promise(); next = res.LastEvaluatedKey results = results.concat(res.Items) } while (next && results.length < limit) results = results.map(r => { return CyclicItem.from_dynamo(r) }) let result = { results } if (next) { result.next = next } return result; } async latest() { let params = { TableName: process.env.CYCLIC_DB, Limit: 1, IndexName: 'keys_gsi', KeyConditionExpression: 'keys_gsi = :keys_gsi', ExpressionAttributeValues: { ':keys_gsi': this.collection, }, // KeyConditions:{ // keys_gsi:{ // ComparisonOperator:'EQ', // AttributeValueList: [this.collection] // } // }, ScanIndexForward: false }; let res = await docClient.send(new QueryCommand(params)) if (!res.Items.length) { return null } return CyclicItem.from_dynamo(res.Items[0]) } index(name) { return new CyclicIndex(name, this.collection) } find(name, value) { let idx = new CyclicIndex(name, this.collection) return idx.find(value) } } module.exports = CyclicCollection