sails-dynamodb
Advanced tools
Comparing version 0.11.4 to 0.12.0
696
index.js
@@ -64,3 +64,4 @@ /** | ||
// (aka model) that gets registered with this adapter. | ||
var _modelReferences = {}; | ||
var _collectionReferences = {}; | ||
var _vogelsReferences = {}; | ||
@@ -97,8 +98,13 @@ var _definedTables = {}; | ||
identity: 'sails-dynamodb', keyId: "id", indexPrefix: "-Index" | ||
identity: 'sails-dynamodb', | ||
pkFormat: 'string', | ||
keyId: 'id', | ||
// Set to true if this adapter supports (or requires) things like data types, validations, keys, etc. | ||
// If true, the schema for models using this adapter will be automatically synced when the server starts. | ||
// Not terribly relevant if your data store is not SQL/schemaful. | ||
, syncable: true, | ||
// This doesn't make sense for dynamo, where the schema parts are locked-down during table creation. | ||
syncable: false, | ||
@@ -109,3 +115,5 @@ | ||
defaults: { | ||
accessKeyId: null, secretAccessKey: null, region: 'us-west-1' | ||
accessKeyId: null, | ||
secretAccessKey: null, | ||
region: 'us-west-1', | ||
// For example: | ||
@@ -130,103 +138,95 @@ // port: 3306, | ||
// safe => Don't change anything (good for production DBs) | ||
, migrate: 'alter' | ||
// , schema: false | ||
}, _getModel: function (collectionName) { | ||
//Indices currently never change in dynamo | ||
migrate: 'safe', | ||
// schema: false | ||
}, | ||
_createModel: function (collectionName) { | ||
var collection = _collectionReferences[collectionName]; | ||
var collection = _modelReferences[collectionName]; | ||
//console.log("currenct collection.definition", collection.definition); | ||
//console.log(collection); | ||
/* | ||
currenct collection | ||
{ | ||
keyId: 'id', | ||
indexPrefix: '-Index', | ||
syncable: true, | ||
defaults: | ||
{ accessKeyId: null, | ||
secretAccessKey: null, | ||
region: 'us-west-1', | ||
migrate: 'alter', | ||
adapter: 'sails-dynamodb' }, | ||
_getModel: [Function], | ||
_getPrimaryKeys: [Function], | ||
registerCollection: [Function], | ||
teardown: [Function], | ||
define: [Function], | ||
describe: [Function], | ||
drop: [Function], | ||
find: [Function], | ||
_searchCondition: [Function], | ||
create: [Function], | ||
update: [Function], | ||
destroy: [Function], | ||
_setColumnType: [Function], | ||
_resultFormat: [Function], | ||
config: | ||
{ accessKeyId: null, | ||
secretAccessKey: null, | ||
region: 'us-west-1', | ||
migrate: 'alter', | ||
adapter: 'sails-dynamodb' }, | ||
definition: | ||
{ user_id: { primaryKey: true, unique: true }, | ||
name: { type: 'string', index: true }, | ||
password: { type: 'string', index: true }, | ||
email: { type: 'string', index: true }, | ||
activated: { type: 'boolean', defaultsTo: false }, | ||
activationToken: { type: 'string' }, | ||
isSocial: { type: 'boolean' }, | ||
socialActivated: { type: 'boolean' }, | ||
createdAt: { type: 'datetime', default: 'NOW' }, | ||
updatedAt: { type: 'datetime', default: 'NOW' } }, | ||
identity: 'user' } | ||
*/ | ||
var primaryKeys = _.where(collection.definition, {primaryKey: true}); | ||
//console.log("primaryKeys", primaryKeys); | ||
return Vogels.define(collectionName, function (schema) { | ||
//console.log("_getModel", collectionName); | ||
// Attrs with primaryKeys | ||
var primaryKeys = _.pick(collection.definition, function(attr) { return !!attr.primaryKey } ); | ||
var primaryKeyNames =_.keys(primaryKeys); | ||
if (primaryKeyNames.length < 1 || primaryKeyNames.length > 2) { | ||
throw new Error('Must have one or two primary key attributes.'); | ||
} | ||
// One primary key, then it's a hash | ||
if (primaryKeyNames.length == 1) { | ||
collection.definition[primaryKeyNames[0]].primaryKey = 'hash'; | ||
} | ||
// Vogels adds an 's'. So let's remove an 's'. | ||
var vogelsCollectionName = collectionName[collectionName.length-1] === 's' ? | ||
collectionName.slice(0, collectionName.length-1) : | ||
collectionName; | ||
var vogelsModel = Vogels.define(vogelsCollectionName, function (schema) { | ||
var columns = collection.definition; | ||
var primaryKeys = [] | ||
var indexes = []; | ||
var indices = {}; | ||
// set columns | ||
for (var columnName in columns) { | ||
var attributes = columns[columnName]; | ||
// console.log(columnName+":", attributes); | ||
if (typeof attributes !== "function") { | ||
// Add column to Vogel model | ||
adapter._setColumnType(schema, columnName, attributes); | ||
// search primarykey | ||
// if("primaryKey" in attributes)primaryKeys.push( columnName ); | ||
// search index | ||
if ("index" in attributes) indexes.push(columnName); | ||
} | ||
} | ||
// set primary key | ||
primaryKeys = adapter._getPrimaryKeys(collectionName); | ||
primaryKeys = _.difference(primaryKeys, ["id"]); // ignore "id" | ||
// console.log("collection.definition", collection.definition); | ||
if (primaryKeys.length < 1) | ||
schema.UUID(adapter.keyId, {hashKey: true}); | ||
else { | ||
if (!_.isUndefined(primaryKeys[0])) { | ||
adapter._setColumnType(schema, primaryKeys[0], columns[primaryKeys[0]], {hashKey: true}); | ||
if (!_.isUndefined(primaryKeys[1])) { | ||
adapter._setColumnType(schema, primaryKeys[1], columns[primaryKeys[1]], {rangeKey: true}); | ||
// Save set indices | ||
var index; | ||
var indexParts; | ||
var indexName; | ||
var indexType; | ||
if ("index" in attributes && attributes.index !== 'secondary') { | ||
index = attributes.index; | ||
indexParts = adapter._parseIndex(index, columnName); | ||
indexName = indexParts[0]; | ||
indexType = indexParts[1]; | ||
if (typeof indices[indexName] === 'undefined') { | ||
indices[indexName] = {}; | ||
} | ||
indices[indexName][indexType] = columnName; | ||
} | ||
} | ||
} | ||
// schema.String( primaryKey, {hashKey: true}); | ||
for (var i = 0; i < indexes.length; i++) { | ||
var key = indexes[i]; | ||
schema.globalIndex(key + adapter.indexPrefix, {hashKey: key}); | ||
// Set global secondary indices | ||
for (indexName in indices) { | ||
schema.globalIndex(indexName, indices[indexName]); | ||
} | ||
schema.Date('createdAt', {default: Date.now}); | ||
schema.Date('updatedAt', {default: Date.now}); | ||
}); | ||
}, _getPrimaryKeys: function (collectionName) { | ||
// Cache Vogels model | ||
_vogelsReferences[collectionName] = vogelsModel; | ||
return vogelsModel; | ||
}, | ||
_getModel: function(collectionName) { | ||
return _vogelsReferences[collectionName] || this._createModel(collectionName); | ||
}, | ||
_getPrimaryKeys: function (collectionName) { | ||
var lodash = _; | ||
var collection = _modelReferences[collectionName]; | ||
var collection = _collectionReferences[collectionName]; | ||
@@ -238,7 +238,11 @@ var maps = lodash.mapValues(collection.definition, "primaryKey"); | ||
}); | ||
var primaryKeys = lodash.keys(list); | ||
return primaryKeys; | ||
}, _keys: function (collectionName) { | ||
}, | ||
_keys: function (collectionName) { | ||
var lodash = _; | ||
var collection = _modelReferences[collectionName]; | ||
var collection = _collectionReferences[collectionName]; | ||
@@ -249,5 +253,7 @@ var list = lodash.pick(collection.definition, function (value, key) { | ||
return lodash.keys(list); | ||
}, _indexes: function (collectionName) { | ||
}, | ||
_indexes: function (collectionName) { | ||
var lodash = _; | ||
var collection = _modelReferences[collectionName]; | ||
var collection = _collectionReferences[collectionName]; | ||
@@ -258,4 +264,61 @@ var list = lodash.pick(collection.definition, function (value, key) { | ||
return lodash.keys(list); | ||
} | ||
}, | ||
// index: 'secondary' | ||
_getLocalIndices: function(collectionName) { | ||
}, | ||
// index: 'indexName-fieldType' (i.e. 'users-hash' and 'users-range') | ||
_getGlobalIndices: function(collectionName) { | ||
}, | ||
_parseIndex: function(index, columnName) { | ||
// Two helpers | ||
var stringEndsWith = function(str, needle) { | ||
if (str.indexOf(needle) !== -1 && | ||
str.indexOf(needle) === str.length-needle.length) { | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
var removeSuffixFromString = function(str, suffix) { | ||
if (stringEndsWith(str, suffix)) { | ||
return str.slice(0, str.length-suffix.length); | ||
} else { | ||
return str; | ||
} | ||
} | ||
var indexName; | ||
var indexType; | ||
if (index === true) { | ||
indexName = columnName; | ||
indexType = 'hashKey'; | ||
} else if (stringEndsWith(index, '-hash')) { | ||
indexName = removeSuffixFromString(index, '-hash'); | ||
indexType = 'hashKey'; | ||
} else if (stringEndsWith(index, '-range')) { | ||
indexName = removeSuffixFromString(index, '-range'); | ||
indexType = 'rangeKey'; | ||
} else { | ||
throw new Error('Index must be a hash or range.'); | ||
} | ||
return [indexName, indexType]; | ||
}, | ||
/** | ||
@@ -269,12 +332,11 @@ * | ||
* @return {[type]} [description] | ||
*/, registerConnection: function (connection, collections, cb) { | ||
//var sails = require("sails"); | ||
//console.log("load registerConnection"); | ||
//console.log("::connection",connection); | ||
//console.log("::collections",collections); | ||
*/ | ||
registerConnection: function (connection, collections, cb) { | ||
if (!connection.identity) return cb(Errors.IdentityMissing); | ||
if (connections[connection.identity]) return cb(Errors.IdentityDuplicate); | ||
var error = null; | ||
try { | ||
AWS.config.update({ | ||
@@ -285,11 +347,18 @@ "accessKeyId": connection.accessKeyId, | ||
}); | ||
} | ||
catch (e) { | ||
} catch (e) { | ||
e.message = e.message + ". Please make sure you added the right keys to your adapter config"; | ||
error = e; | ||
return cb(e) | ||
} | ||
// Keep a reference to this collection | ||
_modelReferences = collections; | ||
cb(error); | ||
} | ||
// Keep a reference to these collections | ||
_collectionReferences = collections; | ||
// Create Vogels models for the collections | ||
_.forOwn(collections, function(coll, collName) { | ||
adapter._createModel(collName); | ||
}); | ||
cb(); | ||
}, | ||
@@ -303,3 +372,4 @@ /** | ||
* @return {[type]} [description] | ||
*/, teardown: function (connection, cb) { | ||
*/ | ||
teardown: function (connection, cb) { | ||
cb(); | ||
@@ -326,3 +396,3 @@ }, | ||
// If you need to access your private data for this collection: | ||
var collection = _modelReferences[collectionName]; | ||
var collection = _collectionReferences[collectionName]; | ||
@@ -337,3 +407,3 @@ if (!_definedTables[collectionName]) { | ||
if (err) { | ||
sails.log.error('Error creating tables', err); | ||
//sails.log.error('Error creating tables', err); | ||
cb(err); | ||
@@ -369,3 +439,3 @@ } | ||
// If you need to access your private data for this collection: | ||
var collection = _modelReferences[collectionName]; | ||
var collection = _collectionReferences[collectionName]; | ||
//console.log("::collection.definition",collection.definition); | ||
@@ -394,3 +464,3 @@ | ||
else { | ||
sails.log.error('Error describe tables' + __filename, err); | ||
//sails.log.error('Error describe tables' + __filename, err); | ||
cb(err); | ||
@@ -422,3 +492,3 @@ } | ||
// If you need to access your private data for this collection: | ||
var collection = _modelReferences[collectionName]; | ||
var collection = _collectionReferences[collectionName]; | ||
//sails.log.error('drop: not supported') | ||
@@ -455,9 +525,10 @@ // Drop a "table" or "collection" schema from the data store | ||
find: function (connection, collectionName, options, cb) { | ||
sails.log.silly("adaptor::find", collectionName); | ||
sails.log.silly("::option", options); | ||
//sails.log.silly("adaptor::find", collectionName); | ||
//sails.log.silly("::option", options); | ||
var collection = _modelReferences[collectionName], | ||
var collection = _collectionReferences[collectionName], | ||
model = adapter._getModel(collectionName), | ||
query = null, | ||
hashKey = null; | ||
error; | ||
// Options object is normalized for you: | ||
@@ -475,6 +546,3 @@ // | ||
if (options && 'where' in options && _.isObject(options.where)) { | ||
var primaryKeys = adapter._getPrimaryKeys(collectionName), | ||
modelIndexes = adapter._indexes(collectionName), | ||
modelKeys = adapter._keys(collectionName); | ||
query = null; | ||
@@ -484,71 +552,61 @@ | ||
var wheres = _.keys(options.where); | ||
// compare both of keys | ||
var primaryQuery = _.intersection(primaryKeys, wheres); | ||
var indexQuery = _.intersection(modelIndexes, wheres); | ||
if (primaryQuery.length > 0 && wheres.length < 2) { | ||
hashKey = primaryKeys[0]; | ||
if (!_.isArray(options.where[hashKey])) { | ||
query = model.query(options.where[hashKey]); | ||
sails.log.silly('using PK ' + hashKey) | ||
options.where = _.without(options.where, hashKey); | ||
var indexing = adapter._whichIndex(collectionName, wheres); | ||
var hash = indexing.hash; | ||
var range = indexing.range; | ||
var indexName = indexing.index; | ||
var scanning = false; | ||
if (indexing) { | ||
query = model.query(options.where[hash]) | ||
delete options.where[hash]; | ||
if (indexName && indexName != 'primary') { | ||
query.usingIndex(indexName); | ||
} | ||
} | ||
else if (indexQuery.length > 0 && wheres.length < 2) { | ||
hashKey = indexQuery[0]; | ||
query = model.query(options.where[hashKey]).usingIndex(hashKey + adapter.indexPrefix); | ||
sails.log.silly('using index ' + wheres[0] + adapter.indexPrefix); | ||
delete options.where[hashKey]; | ||
} | ||
// scan mode | ||
if (!query) { | ||
if (range) { | ||
error = adapter._applyQueryFilter(query, 'where', range, options.where[range]); | ||
if (error) return cb(error); | ||
delete options.where[range]; | ||
} | ||
} else { | ||
scanning = true; | ||
query = model.scan(); | ||
sails.log.silly('using scan() '); | ||
} | ||
var queryOp = scanning ? 'where' : 'filter'; | ||
for (var key in options.where) { | ||
// Using startKey? | ||
if (key == 'startKey') { | ||
try { | ||
query.startKey(JSON.parse(options.where[key])); | ||
} | ||
catch (e) { | ||
} catch (e) { | ||
return cb("Wrong start key format :" + e.message); | ||
} | ||
continue; | ||
} else { | ||
error = adapter._applyQueryFilter(query, queryOp, key, options.where[key]); | ||
if (error) return cb(error); | ||
} | ||
if (modelKeys.indexOf(key) === -1) { | ||
return cb("Wrong attribute given : " + key); | ||
} | ||
var filter = _.keys(options.where[key])[0]; | ||
if (filter in filters) { | ||
try { | ||
query.where(key)[filter](filters[filter] ? options.where[key][filter] : null); | ||
} | ||
catch (e) { | ||
return cb(e.message); | ||
} | ||
} | ||
else { | ||
try { | ||
if (_.isString(options.where[key]) || _.isNumber(options.where[key])) { | ||
query.where(key).equals(options.where[key]); | ||
continue; | ||
} | ||
else if (_.isArray(options.where[key])) { | ||
query.where(key).in(options.where[key]); | ||
continue; | ||
} | ||
} | ||
catch (e) { | ||
return cb(e.message); | ||
} | ||
return cb("Wrong filter given :" + filter); | ||
} | ||
} | ||
} | ||
query = adapter._searchCondition(query, options, model); | ||
query.exec(function (err, res) { | ||
if (!err) { | ||
console.log("success", adapter._resultFormat(res)); | ||
//console.log("success", adapter._resultFormat(res)); | ||
adapter._valueDecode(collection.definition, res.attrs); | ||
@@ -558,3 +616,3 @@ cb(null, adapter._resultFormat(res)); | ||
else { | ||
sails.log.error('Error exec query:' + __filename, err); | ||
//sails.log.error('Error exec query:' + __filename, err); | ||
cb(err); | ||
@@ -566,3 +624,180 @@ } | ||
// cb(null, []); | ||
}/** | ||
}, | ||
_applyQueryFilter: function(query, op, key, condition) { | ||
try { | ||
if (_.isString(condition) || _.isNumber(condition)) { | ||
query[op](key).equals(condition); | ||
} else if (_.isArray(condition)) { | ||
query[op](key).in(condition); | ||
} else if (_.isObject(condition)) { | ||
var filter = _.keys(condition)[0]; | ||
if (filter in filters) { | ||
query[op](key)[filter](filters[filter] ? condition[filter] : null); | ||
} else { | ||
throw new Error("Wrong filter given :" + filter); | ||
} | ||
} else { | ||
throw new Error("Wrong filter given :" + filter); | ||
} | ||
} catch (e) { | ||
return e; | ||
} | ||
}, | ||
// Return {index: 'name', hash: 'field1', range:'field2'} | ||
// Primary hash and range > primary hash and secondary range > global secondary hash and range | ||
// > primary hash > global secondary hash > no index/primary | ||
_whichIndex: function(collectionName, fields) { | ||
var columns = _collectionReferences[collectionName].definition; | ||
var primaryHash = false; | ||
var primaryRange = false; | ||
var secondaryRange = false; | ||
var globalHash = false; | ||
var globalRange = false; | ||
var globalIndexName; | ||
// holds all index info from fields | ||
var indices = {}; | ||
// temps for loop | ||
var fieldName; | ||
var column; | ||
var indexInfo; | ||
var indexName; | ||
var indexType; | ||
for (var i = 0; i < fields.length; i++) { | ||
fieldName = fields[i]; | ||
column = columns[fieldName]; | ||
// set primary hash | ||
if (column.primaryKey && column.primaryKey === true || column.primaryKey === 'hash') { | ||
primaryHash = fieldName; | ||
continue; | ||
} | ||
// set primary range | ||
if (column.primaryKey && column.primaryKey === 'range') { | ||
primaryRange = fieldName; | ||
continue; | ||
} | ||
// set secondary range | ||
if (column.index && column.index === 'secondary') { | ||
secondaryRange = fieldName; | ||
continue; | ||
} | ||
// build global secondary hash info | ||
if (column.index && column.index !== 'secondary') { | ||
indexInfo = adapter._parseIndex(column.index, fieldName); | ||
indexName = indexInfo[0]; | ||
indexType = indexInfo[1]; | ||
if (typeof indices[indexName] === 'undefined') { | ||
indices[indexName] = {}; | ||
} | ||
indices[indexName][indexType] = fieldName; | ||
continue; | ||
} | ||
} | ||
// set global secondary hash info | ||
var indicesHashed; | ||
var indicesRanged; | ||
// pick out those with just a hash key | ||
var indicesHashed = _.pick(indices, function(ind) { | ||
return !!ind.hashKey && !ind.rangeKey; | ||
}); | ||
// pick out those with a hash and a range key | ||
var indicesRanged = _.pick(indices, function(ind) { | ||
return !!ind.hashKey && !!ind.rangeKey; | ||
}); | ||
// found a good ranged global secondary index? | ||
if (!_.isEmpty(indicesRanged)) { | ||
globalIndexName = Object.keys(indicesRanged)[0]; | ||
globalHash = indicesRanged[globalIndexName].hashKey; | ||
globalRange = indicesRanged[globalIndexName].rangeKey; | ||
} else if (!_.isEmpty(indicesHashed)) { | ||
globalIndexName = Object.keys(indicesHashed)[0]; | ||
globalHash = indicesHashed[globalIndexName].hashKey; | ||
} | ||
if (primaryHash && primaryRange) { | ||
return { | ||
index: 'primary', | ||
hash: primaryHash, | ||
range: primaryRange | ||
} | ||
} else if (primaryHash && secondaryRange) { | ||
return { | ||
index: secondaryRange+'Index', // per Vogels | ||
hash: primaryHash, | ||
range: secondaryRange | ||
} | ||
} else if (globalHash && globalRange) { | ||
return { | ||
index: globalIndexName, | ||
hash: globalHash, | ||
range: globalRange | ||
} | ||
} else if (primaryHash) { | ||
return { | ||
index: 'primary', | ||
hash: primaryHash | ||
} | ||
} else if (globalHash) { | ||
return { | ||
index: globalIndexName, | ||
hash: globalHash | ||
} | ||
} else { | ||
return false; | ||
} | ||
}, | ||
/** | ||
* search condition | ||
@@ -573,12 +808,18 @@ * @param query | ||
* @private | ||
*/, _searchCondition: function (query, options, model) { | ||
*/ | ||
_searchCondition: function (query, options, model) { | ||
if (!query) { | ||
query = model.scan(); | ||
} | ||
if (!options) { | ||
return query; | ||
} | ||
if ('sort' in options) { | ||
//according to http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html#DDB-Query-request-ScanIndexForward | ||
var sort = _.keys(options.sort)[0]; | ||
if (sort == 1) { | ||
@@ -591,10 +832,13 @@ query.ascending(); | ||
} | ||
if ('limit' in options) { | ||
query.limit(options.limit); | ||
} | ||
else { | ||
} else { | ||
query.loadAll(); | ||
} | ||
return query | ||
} | ||
return query; | ||
}, | ||
@@ -611,3 +855,3 @@ | ||
* @return {[type]} [description] | ||
*/, create: function (connection, collectionName, values, cb) { | ||
*/create: function (connection, collectionName, values, cb) { | ||
//sails.log.silly("adaptor::create", collectionName); | ||
@@ -620,3 +864,3 @@ //sails.log.silly("values", values); | ||
// If you need to access your private data for this collection: | ||
var collection = _modelReferences[collectionName]; | ||
var collection = _collectionReferences[collectionName]; | ||
adapter._valueEncode(collection.definition, values); | ||
@@ -627,3 +871,3 @@ | ||
if (err) { | ||
sails.log.error(__filename + ", create error:", err); | ||
//sails.log.error(__filename + ", create error:", err); | ||
cb(err); | ||
@@ -659,5 +903,6 @@ } | ||
var Model = adapter._getModel(collectionName); | ||
var primaryKeys = adapter._getPrimaryKeys(collectionName); | ||
// If you need to access your private data for this collection: | ||
var collection = _modelReferences[collectionName]; | ||
var collection = _collectionReferences[collectionName]; | ||
adapter._valueEncode(collection.definition, values); | ||
@@ -679,10 +924,34 @@ | ||
// (do both in a single query if you can-- it's faster) | ||
var updateValues = _.assign(options.where, values); | ||
// Move primary keys to values (Vogels-style) so rest of wheres can be used for expected clause. | ||
// Actually, seems like the primary key has to stay in the wheres so as not to create a new item. | ||
var primaryKeyName; | ||
for (var i = 0; i < primaryKeys.length; i++) { | ||
primaryKeyName = primaryKeys[i]; | ||
if (options.where[primaryKeyName]) { | ||
values[primaryKeyName] = options.where[primaryKeyName]; | ||
} | ||
} | ||
var vogelsOptions = !_.isEmpty(options.where) ? { expected: options.where } : {}; | ||
//console.log(updateValues); | ||
var current = Model.update(updateValues, function (err, res) { | ||
Model.update(values, vogelsOptions, function (err, res) { | ||
if (err) { | ||
sails.log.error('Error update data' + __filename, err); | ||
cb(err); | ||
} | ||
else { | ||
//sails.log.error('Error update data' + __filename, err); | ||
// Deal with AWS's funny way of telling us it couldnt update that item | ||
if (err.code == 'ConditionalCheckFailedException') { | ||
cb(null, []); | ||
} else { | ||
cb(err); | ||
} | ||
} else { | ||
// console.log('add model data',res.attrs); | ||
@@ -714,3 +983,3 @@ adapter._valueDecode(collection.definition, res.attrs); | ||
// If you need to access your private data for this collection: | ||
var collection = _modelReferences[collectionName]; | ||
var collection = _collectionReferences[collectionName]; | ||
@@ -731,3 +1000,3 @@ | ||
if (err) { | ||
sails.log.error('Error destory data' + __filename, err); | ||
//sails.log.error('Error destory data' + __filename, err); | ||
cb(err); | ||
@@ -744,3 +1013,3 @@ } | ||
cb(); | ||
} | ||
}, | ||
@@ -844,5 +1113,19 @@ | ||
* @private | ||
*/, _setColumnType: function (schema, name, attr, options) { | ||
*/ | ||
_setColumnType: function (schema, name, attr, options) { | ||
options = (typeof options !== 'undefined') ? options : {}; | ||
// Set primary key options | ||
if (attr.primaryKey === 'hash') { | ||
_.merge(options, {hashKey: true}); | ||
} else if (attr.primaryKey === 'range') { | ||
_.merge(options, {rangeKey: true}); | ||
} else if (attr.index === 'secondary') { | ||
_.merge(options, {secondaryIndex: true}); | ||
} | ||
// set columns | ||
@@ -879,2 +1162,13 @@ // console.log("name:", name); | ||
// case "binary": | ||
case "string": | ||
if (attr.autoIncrement) { | ||
schema.UUID(name, options); | ||
} else { | ||
schema.String(name, options); | ||
} | ||
break; | ||
default: | ||
@@ -881,0 +1175,0 @@ // console.log("Set String", name); |
{ | ||
"name": "sails-dynamodb", | ||
"version": "0.11.4", | ||
"version": "0.12.0", | ||
"description": "Amazon DynamoDB adapter for Sails / Waterline", | ||
@@ -26,3 +26,3 @@ "main": "index.js", | ||
"dependencies": { | ||
"vogels": "0.12.0", | ||
"vogels": "~0.12.0", | ||
"lodash": "", | ||
@@ -62,7 +62,46 @@ "async": "" | ||
"maintainers": [ | ||
"gadelkareem <gadelkareem@gmail.com>" | ||
"gadelkareem <gadelkareem@gmail.com>", | ||
"devinivy <devin@bigroomstudios.com>" | ||
], | ||
"directories": { | ||
"test": "test" | ||
} | ||
} | ||
}, | ||
"contributors": [ | ||
{ | ||
"name": "Waleed Gadelkareem", | ||
"email": "gadelkareem@gmail.com", | ||
"url": "https://github.com/gadelkareem", | ||
"contributions": 27, | ||
"additions": 1975, | ||
"deletions": 1842, | ||
"hireable": false | ||
}, | ||
{ | ||
"name": "Mike McNeil", | ||
"email": "customers@balderdash.co", | ||
"url": "https://github.com/mikermcneil", | ||
"contributions": 31, | ||
"additions": 1371, | ||
"deletions": 725, | ||
"hireable": true | ||
}, | ||
{ | ||
"name": "devin ivy", | ||
"email": "devin@bigroomstudios.com", | ||
"url": "https://github.com/devinivy", | ||
"contributions": 14, | ||
"additions": 586, | ||
"deletions": 245, | ||
"hireable": false | ||
}, | ||
{ | ||
"name": "dozo", | ||
"email": "", | ||
"url": "https://github.com/dohzoh", | ||
"contributions": 46, | ||
"additions": 943, | ||
"deletions": 517, | ||
"hireable": false | ||
} | ||
] | ||
} |
@@ -73,14 +73,61 @@ # sails-dynamodb | ||
### Pagination | ||
Support for Pagination is added as following: | ||
## Pagination | ||
Support for Pagination is added as following: | ||
1. First add a limit to current request | ||
``` | ||
/user?limit=2 | ||
``` | ||
2. Then get the last primaryKey value and send it as startKey in the next request | ||
``` | ||
/user?limit=2&startKey={"PrimaryKey": "2"} | ||
``` | ||
## Using DynamoDB Indexes | ||
Primary hash/range keys, local secondary indexes, and global secondary indexes are currently supported by this adapter, but their usage is always inferred from query conditions–`Model.find` will attempt to use the most optimal index using the following precedence: | ||
``` | ||
/user?limit=2 | ||
Primary hash and range > primary hash and secondary range > global secondary hash and range | ||
> primary hash > global secondary hash > no index/primary | ||
``` | ||
2. Then get the last primaryKey value and send it as startKey in the next request | ||
If an index is being used and there are additional query conditions, then results are compiled using DynamoDB's result filtering. If no index can be used for a query, then the adapter will perform a scan on the table for results. | ||
### Adding Indexes | ||
#### Primary hash and primary range | ||
``` | ||
/user?limit=2&startKey={"PrimaryKey": "2"} | ||
UserId: { | ||
type: 'integer', | ||
primaryKey: 'hash' | ||
}, | ||
GameTitle: { | ||
type: 'string', | ||
primaryKey: 'range' | ||
} | ||
``` | ||
#### Secondary range (local secondary index) | ||
The index name used for a local secondary index is the name of the field suffixed by "Index". In this case the index name is `TimeIndex`. | ||
``` | ||
Time: { | ||
type: 'datetime', | ||
index: 'secondary' | ||
} | ||
``` | ||
#### Global secondary index | ||
The index name used for a global secondary index is specified in the `index` property before the type of key (`hash` or `range`). In this case the index name is `GameTitleIndex`. | ||
``` | ||
GameTitle: { | ||
type: 'string', | ||
index: 'GameTitleIndex-hash' | ||
}, | ||
HighScore: { | ||
type: 'integer', | ||
index: 'GameTitleIndex-range' | ||
} | ||
``` | ||
## Update | ||
The `Model.update` method is currently expected to update exactly one item since DynamoDB only offers an [UpdateItem](http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html) endpoint. A complete primary key must be supplied. Any additional "where" conditions passed to `Model.update` are used to build a conditional expression for the update. Despite the fact the DynamoDB updates only one item, `Model.update` will always return an array of the (one or zero) updated items upon success. | ||
## Testing | ||
@@ -87,0 +134,0 @@ |
52143
9
1024
174
Updatedvogels@~0.12.0