UNPKG

dino

Version:

A simple DynamoDB object modeler for Node.js.

407 lines (313 loc) 10.9 kB
var _ = require('underscore'), connection = require('./connection'), collection = require('./collection'), helper = require('./helper'); var model = { connection: connection, set: function (key, val) { var self = this, attr, attrs; if (key === null || key === undefined) { return self; } if (_.isObject(key)) { attrs = key; } else { (attrs = {})[key] = val; } for (attr in attrs) { if (!attr) { continue; } self.attributes[attr] = !_.isUndefined(attrs[attr]) ? attrs[attr] : self.schema.get(attr).getDefaultValue(); } return self; }, get: function (attr) { return this.attributes[attr] }, getHash: function () { var self = this; return _.map(self.schema.hashKeyAttributes, function(attr){ return self.get(attr); }); }, getRange: function () { var self = this; return _.map(self.schema.rangeKeyAttributes, function(attr){ return self.get(attr); }); }, toJSON: function () { var self = this, schema = self.schema, json = {}, attrs = _.clone(self.attributes); _.each(attrs, function(val, attr){ var transformed = schema.get(attr).toJSON(val); if (transformed) json[attr] = transformed; }); return json; }, serialize: function () { var self = this, schema = self.schema, dynamoAttrs = {}, attrs = _.clone(self.attributes); if (schema.hashKeyAttributes.length > 1) { _.each(schema.hashKeyAttributes, function(key){ delete attrs[key]; }); dynamoAttrs[schema.hashKey] = schema.serializeHashAttribute(self.getHash()); } if (_.isArray(schema.rangeKeyAttributes) && schema.rangeKeyAttributes.length > 1) { _.each(schema.rangeKeyAttributes, function(key){ delete attrs[key]; }); dynamoAttrs[schema.rangeKey] = schema.serializeRangeAttribute(self.getRange()); } _.each(attrs, function(val, attr){ var transformed = schema.get(attr).serializeObject(val); if (transformed) { dynamoAttrs[attr] = transformed; } }); return dynamoAttrs; }, save: function (callback) { if (!_.isFunction(callback)) { callback = function () {}; } var self = this; self.connection.client.putItem({ TableName: self.schema.table, Item: self.serialize(), ReturnConsumedCapacity: 'TOTAL' }, function(err, data){ if (err) return callback(err); return callback(err, data.ConsumedCapacity.CapacityUnits); }); }, destroy: function (callback) { if (!_.isFunction(callback)) { callback = function () {}; } var self = this, schema = self.schema, match = {}; try { match[schema.hashKey] = self.getHash().join(schema.keyDelimiter); if (schema.hasRangeAttributes()) { match[schema.rangeKey] = self.getRange()[0]; } } catch (err) { return callback(err); } self.Model.destroy(match, callback); } }; var Model = { connection: connection, create: function (attributes) { attributes = attributes || {}; var self = this, instanceOwnProperties = { schema: self.schema, connection: self.connection, attributes: {}, Model: this }, reserved = ['options', 'schema', 'connection', 'client', 'attributes', 'Model']; _.each(self.options, function(val, key){ if (reserved.indexOf(key) >= 0) return; instanceOwnProperties[key] = val; }); _.each(self.schema.attributes, function(type, attr){ attributes[attr] = attributes[attr]; }); return _.bindAll(Object.create(model, helper.ownProperties(instanceOwnProperties)).set(attributes)); }, findOne: function (attrs, callback) { if (!_.isFunction(callback)) { callback = function () {}; } if (_.isEmpty(attrs)) { callback(new Error('You must specify match attributes')); } var self = this, schema = self.schema, params; if (!schema.hasRangeAttributes() && attrs[schema.hashKey]) { params = { TableName: schema.table, ReturnConsumedCapacity: 'TOTAL', ConsistentRead: true, Key: {} }; params.Key[schema.hashKey] = schema.serializeHashAttribute(schema.getHashFromMatch(attrs)); return self.connection.client.getItem(params, function(err, data){ if (err) return callback(err); callback(null, self.parse(data.Item), data.ConsumedCapacity.CapacityUnits); }); } self.find({ match: attrs }, function(err, collection, units){ if (err) return callback(err); if (collection.models.length < 1) return callback(new Error('Item does not exist')); if (collection.models.length > 1) return callback(new Error('You must specify range or secondary attributes')); callback(null, collection.models[0], units); }); }, find: function (options, callback) { if (!_.isFunction(callback)) { callback = function () {}; } var self = this, schema = self.schema, query; try { query = schema.generateQueryParams(options.match); if (_.isString(options.sortBy)) { if (schema.secondaryKeyAttributes.indexOf(options.sortBy) < 0) { throw new Error('You can only sort by Secondary attributes'); } query.IndexName = schema.table + '.' + options.sortBy; } if (_.isNumber(options.take)) { query.Limit = options.take; } if (_.isNumber(options.skip) && query.Limit) { query.Limit += options.skip; } } catch (err) { return callback(err); } self.connection.client.query(query, function(err, data){ if (err) return callback(err); try { data.Items = _.isNumber(options.skip) ? data.Items.slice(options.skip) : data.Items; } catch (err) { return callback(err); } callback(null, collection(_.map(data.Items, self.parse, self)), data.ConsumedCapacity.CapacityUnits); }); }, destroy: function (attrs, callback) { if (!_.isFunction(callback)) { callback = function () {}; } var self = this, schema = self.schema, params = { TableName: schema.table, ReturnConsumedCapacity: 'TOTAL', Key: {} }; try { params.Key[schema.hashKey] = schema.serializeHashAttribute(schema.getHashFromMatch(attrs)); } catch (err) { return callback(err); } if (!schema.hasRangeAttributes()) { return self.connection.client.deleteItem(params, function(err, data){ if (err) return callback(err); callback(err, data.ConsumedCapacity.CapacityUnits); }); } if (schema.isValidRangeMatch(attrs)) { params.Key[schema.rangeKey] = schema.serializeRangeAttribute(schema.getRangeFromMatch(attrs)); return self.connection.client.deleteItem(params, function(err, data){ if (err) return callback(err); callback(err, data.ConsumedCapacity.CapacityUnits); }); } if (schema.isValidSecondaryMatch(attrs)) { return self.findOne(attrs, function(e1, m, u1){ if (e1) return callback(e1); m.destroy(function(e2, u2){ if (e2) return callback(e2); callback(null, (u1 + u2)); }); }); } return callback(new Error('You must specify range or secondary attribute(s)')); }, parse: function (item_0) { var self = this, attrs = self.schema.getSerializedAttributes(), item = {}; _.each(attrs, function(type, attr){ var a = item_0[attr]; if (!a) { return; } if (attr === self.schema.hashKey) { _.extend(item, self.schema.deserializeHashAttribute(a)); return; } if (attr === self.schema.rangeKey) { _.extend(item, self.schema.deserializeRangeAttribute(a)); return; } item[attr] = type.deserializeObject(a); }); return self.create(item); } }; module.exports = exports = function (options) { options = options || {}; var ownProperties = { options: options, schema: options.schema }; if (options.client) { ownProperties.connection = { client: options.client }; } return _.bindAll(Object.create(Model, helper.ownProperties(ownProperties))); };