UNPKG

@cyclic.sh/dynamodb

Version:

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

195 lines (159 loc) 5.42 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