UNPKG

dino

Version:

A simple DynamoDB object modeler for Node.js.

399 lines (315 loc) 11.2 kB
var _ = require('underscore'), connection = require('./connection'), helper = require('./helper'); var methods = { createTable: function (options, callback) { if (arguments.length === 1) { if (_.isFunction(options)) { callback = options; options = {}; } } else if (arguments.length === 0) { options = {}; callback = function () {}; } var self = this, params = { TableName: self.table, ProvisionedThroughput: { ReadCapacityUnits: options.readUnits || 1, WriteCapacityUnits: options.writeUnits || 1 }, AttributeDefinitions: [ { AttributeName: self.hashKey, AttributeType: self.get(self.hashKey).key } ], KeySchema:[ { AttributeName: self.hashKey, KeyType: 'HASH' } ] }, client = options.client || connection.client; if (self.hasRangeAttributes()) { params.AttributeDefinitions.push({ AttributeName: self.rangeKey, AttributeType: self.get(self.rangeKey).key }); params.KeySchema.push({ AttributeName: self.rangeKey, KeyType: 'RANGE' }); } if (self.hasSecondaryAttributes()) { params.LocalSecondaryIndexes = []; _.each(self.secondaryKeyAttributes, function(attr){ params.AttributeDefinitions.push({ AttributeName: attr, AttributeType: self.get(attr).key }); params.LocalSecondaryIndexes.push({ IndexName: self.table + '.' + attr, KeySchema:[ { AttributeName: self.hashKey, KeyType: 'HASH' }, { AttributeName: attr, KeyType: 'RANGE' } ], Projection: { ProjectionType: 'KEYS_ONLY' } }); }); } client.createTable(params, function(err){ if (err) return callback(err); callback(null); }); }, add: function (key, val) { var self = this, attr, attrs; if (key === null || key === undefined) { return self; } if (typeof key === 'object') { attrs = key; } else { (attrs = {})[key] = val; } for (attr in attrs) { self.attributes[attr] = Object.create(attrs[attr]); } return self; }, get: function (attr) { return this.attributes[attr]; }, serializeKeyAttribute: function (val, attrs) { var self = this, isArr = _.isArray(val); if (!isArr) { return self.get(attrs[0]).serializeObject(val); } return { S: _.map(attrs, function(attr, i){ return self.get(attr).serialize(val[i]); }).join(self.keyDelimiter) }; }, serializeHashAttribute: function (val) { return this.serializeKeyAttribute(val, this.hashKeyAttributes); }, serializeRangeAttribute: function (val) { return this.serializeKeyAttribute(val, this.rangeKeyAttributes); }, deserializeKeyAttribute: function (val, attrs) { var self = this, obj = {}; val = (attrs.length > 1) ? val.S.split(self.keyDelimiter) : [val.S]; _.each(attrs, function(attr, i){ obj[attr] = self.get(attr).deserialize(val[i]); }); return obj; }, deserializeHashAttribute: function (val) { return this.deserializeKeyAttribute(val, this.hashKeyAttributes); }, deserializeRangeAttribute: function (val) { return this.deserializeKeyAttribute(val, this.rangeKeyAttributes); }, getSerializedAttributes: function () { var self = this, attrs = _.clone(self.attributes); if (self.hashKeyAttributes.length > 1) { _.each(self.hashKeyAttributes, function(attr){ delete attrs[attr]; }); attrs[self.hashKey] = {}; } if (_.isArray(self.rangeKeyAttributes) && self.rangeKeyAttributes.length > 1) { _.each(self.rangeKeyAttributes, function(attr){ delete attrs[attr]; }); attrs[self.rangeKey] = {}; } return attrs; }, generateQueryParams: function (attrs) { if (_.isEmpty(attrs)) { throw new Error('You must specify query attribute(s)'); } var self = this, params = { TableName: self.table, ReturnConsumedCapacity: 'TOTAL', ScanIndexForward: false, KeyConditions: {} }, hashAttrCount = 0, hashAttrs = {}, rangeAttrCount = 0, rangeAttrs = {}, secondaryAttr; _.each(attrs, function(val, attr){ if (self.hashKeyAttributes.indexOf(attr) >= 0) { hashAttrCount++; return hashAttrs[attr] = val; } if (self.rangeKeyAttributes.indexOf(attr) >= 0) { rangeAttrCount++; return rangeAttrs[attr] = val; } if (self.secondaryKeyAttributes.indexOf(attr) >= 0) { return secondaryAttr = attr; } }); if (hashAttrCount !== self.hashKeyAttributes.length) { throw new Error('You must specify attribute(s): ' + self.hashKeyAttributes.join(', ')); } hashAttrs = _.map(hashAttrs, function(val){ return val; }); params.KeyConditions[self.hashKey] = { AttributeValueList: [ self.serializeHashAttribute(hashAttrs.length > 1 ? hashAttrs : hashAttrs[0]) ], ComparisonOperator: 'EQ' } if (rangeAttrCount > 0) { if (rangeAttrCount !== self.rangeKeyAttributes.length) { throw new Error('You must specify attribute(s): ' + self.rangeKeyAttributes.join(', ')); } rangeAttrs = _.map(rangeAttrs, function(val){ return val; }); params.KeyConditions[self.rangeKey] = { AttributeValueList: [ self.serializeRangeAttribute(rangeAttrs.length > 1 ? rangeAttrs : rangeAttrs[0]) ], ComparisonOperator: 'EQ' } return params; } if (secondaryAttr) { params.KeyConditions[secondaryAttr] = { AttributeValueList: [ self.serializeKeyAttribute(attrs[secondaryAttr], [secondaryAttr]) ], ComparisonOperator: 'EQ' }; params.IndexName = self.table + '.' + secondaryAttr; params.ScanIndexForward = false; params.Select = 'ALL_ATTRIBUTES'; } return params; }, validateKeyMatch: function (attrs, matchAgainst, allowSingleMatch) { if (_.isEmpty(attrs)) { throw new Error('You must specify attributes'); } var self = this, count = 0; for (attr in attrs) { if (matchAgainst.indexOf(attr) >= 0) { count++; } } if (allowSingleMatch && count > 0) { return; } if (count !== matchAgainst.length) { throw new Error('You must specify attribute(s): ' + matchAgainst.join(', ')); } }, validateHashMatch: function (attrs) { this.validateKeyMatch(attrs, this.hashKeyAttributes); }, validateRangeMatch: function (attrs) { this.validateKeyMatch(attrs, this.rangeKeyAttributes); }, validateSecondaryMatch: function (attrs) { this.validateKeyMatch(attrs, this.secondaryKeyAttributes, true); }, isValidRangeMatch: function (attrs) { var self = this; try { self.validateRangeMatch(attrs); } catch (err) { return false; } return true; }, isValidSecondaryMatch: function (attrs) { var self = this; try { self.validateSecondaryMatch(attrs); } catch (err) { return false; } return true; }, getValFromMatch: function (attrs, matchAgainst) { var self = this, val = []; for (attr in attrs) { if (matchAgainst.indexOf(attr) >= 0) { val.push(attrs[attr]); } } return val; }, getHashFromMatch: function (attrs) { return this.getValFromMatch(attrs, this.hashKeyAttributes); }, getRangeFromMatch: function (attrs) { return this.getValFromMatch(attrs, this.rangeKeyAttributes); }, hasRangeAttributes: function () { return _.isArray(this.rangeKeyAttributes) && this.rangeKeyAttributes.length > 0; }, hasSecondaryAttributes: function () { return _.isArray(this.secondaryKeyAttributes) && this.secondaryKeyAttributes.length > 0; }, }; module.exports = exports = function (options) { options = options || {}; options.key = options.key || { hash: '', range: null }; var delimiter = options.keyDelimiter || '#', hashKeyAttributes = helper.normalizeKey(options.key.hash), rangeKeyAttributes = helper.normalizeKey(options.key.range), secondaryKeyAttributes = helper.normalizeKey(options.key.secondary), ownProperties = helper.ownProperties({ attributes: {}, table: options.table || '', hashKeyAttributes: hashKeyAttributes, rangeKeyAttributes: rangeKeyAttributes, secondaryKeyAttributes: secondaryKeyAttributes, hashKey: hashKeyAttributes.join(delimiter), rangeKey: rangeKeyAttributes ? rangeKeyAttributes.join(delimiter) : null, keyDelimiter: delimiter }); return _.bindAll(Object.create(methods, ownProperties).add(options.attributes)); };