{ TableName: 'dynamo-client-integration-test' } { __type: 'com.amazon.dynamodb.v20111205#ResourceNotFoundException', message: 'Requested resource not found: Table: dynamo-client-integration-test not found' } { TableName: 'dynamo-client-integration-test' } { __type: 'com.amazon.dynamodb.v20111205#ResourceNotFoundException', message: 'Requested resource not found: Table: dynamo-client-integration-test not found' } { TableName: 'dynamo-client-integration-test', LocalSecondaryIndexes: [ { IndexName: 'postIx', KeySchema: [Object], Projection: [Object] } ], KeySchema: [ { AttributeName: 'forumName', KeyType: 'HASH' }, { AttributeName: 'subject', KeyType: 'RANGE' } ], AttributeDefinitions: [ { AttributeName: 'forumName', AttributeType: 'S' }, { AttributeName: 'subject', AttributeType: 'S' }, { AttributeName: 'lastPostTime', AttributeType: 'S' } ], ProvisionedThroughput: { ReadCapacityUnits: 1, WriteCapacityUnits: 1 } } { TableDescription: { AttributeDefinitions: [ [Object], [Object], [Object] ], CreationDateTime: 1370223503, KeySchema: [ [Object], [Object] ], ProvisionedThroughput: { NumberOfDecreasesToday: 0, ReadCapacityUnits: 1, WriteCapacityUnits: 1 }, TableName: 'dynamo-client-integration-test', TableStatus: 'CREATING', ItemCount: 0, TableSizeBytes: 0, LocalSecondaryIndexes: [ [Object] ] } } { TableName: 'dynamo-client-integration-test' } { Table: { AttributeDefinitions: [ [Object], [Object], [Object] ], CreationDateTime: 1370223503, KeySchema: [ [Object], [Object] ], ProvisionedThroughput: { NumberOfDecreasesToday: 0, ReadCapacityUnits: 1, WriteCapacityUnits: 1 }, TableName: 'dynamo-client-integration-test', TableStatus: 'ACTIVE', ItemCount: 0, TableSizeBytes: 0, LocalSecondaryIndexes: [ [Object] ] } } { TableName: 'dynamo-client-integration-test' } { Count: 0, ScannedCount: 0, Items: [] } { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'b' }, lastPostTime: { S: '2013-06-03T01:38:23.047Z' } } } {} { TableName: 'dynamo-client-integration-test' } { Count: 1, ScannedCount: 1, Items: [ { forumName: [Object], subject: [Object], lastPostTime: [Object] } ] } { RequestItems: { 'dynamo-client-integration-test': [ [Object] ] } } { UnprocessedItems: {} } { ConsistentRead: true, TableName: 'dynamo-client-integration-test', Key: { forumName: { S: 'a' }, subject: { S: 'b' } } } {} { TableName: 'dynamo-client-integration-test' } { Count: 0, ScannedCount: 0, Items: [] } { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'b' }, lastPostTime: { S: '2013-06-03T01:38:23.102Z' } } } {} { ConsistentRead: true, TableName: 'dynamo-client-integration-test', Key: { forumName: { S: 'a' }, subject: { S: 'b' } } } { Item: { forumName: { S: 'a' }, subject: { S: 'b' }, lastPostTime: { S: '2013-06-03T01:38:23.102Z' } } } { TableName: 'dynamo-client-integration-test' } { Count: 1, ScannedCount: 1, Items: [ { forumName: [Object], subject: [Object], lastPostTime: [Object] } ] } { RequestItems: { 'dynamo-client-integration-test': [ [Object] ] } } { UnprocessedItems: {} } { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'a' }, lastPostTime: { S: '2013-06-03T01:38:23.138Z' } } } {} { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'b' }, lastPostTime: { S: '2013-06-03T01:38:23.138Z' } } } {} { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'c' }, lastPostTime: { S: '2013-06-03T01:38:23.138Z' } } } {} { ConsistentRead: true, TableName: 'dynamo-client-integration-test', KeyConditions: { forumName: { ComparisonOperator: 'EQ', AttributeValueList: [Object] }, subject: { ComparisonOperator: 'GT', AttributeValueList: [Object] } } } { Count: 2, Items: [ { forumName: [Object], subject: [Object], lastPostTime: [Object] }, { forumName: [Object], subject: [Object], lastPostTime: [Object] } ] } { ConsistentRead: true, Select: 'COUNT', TableName: 'dynamo-client-integration-test', KeyConditions: { forumName: { ComparisonOperator: 'EQ', AttributeValueList: [Object] }, subject: { ComparisonOperator: 'GT', AttributeValueList: [Object] } } } { Count: 2 } { TableName: 'dynamo-client-integration-test' } { Count: 3, ScannedCount: 3, Items: [ { forumName: [Object], subject: [Object], lastPostTime: [Object] }, { forumName: [Object], subject: [Object], lastPostTime: [Object] }, { forumName: [Object], subject: [Object], lastPostTime: [Object] } ] } { RequestItems: { 'dynamo-client-integration-test': [ [Object], [Object], [Object] ] } } { UnprocessedItems: {} } { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'a' }, lastPostTime: { S: '2013-06-03T01:38:23.223Z' } } } {} { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'b' }, lastPostTime: { S: '2013-06-03T01:38:23.224Z' } } } {} { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'c' }, lastPostTime: { S: '2013-06-03T01:38:23.225Z' } } } {} { IndexName: 'postIx', ConsistentRead: true, TableName: 'dynamo-client-integration-test', KeyConditions: { forumName: { ComparisonOperator: 'EQ', AttributeValueList: [Object] }, lastPostTime: { ComparisonOperator: 'GT', AttributeValueList: [Object] } } } { Count: 2, Items: [ { forumName: [Object], subject: [Object], lastPostTime: [Object] }, { forumName: [Object], subject: [Object], lastPostTime: [Object] } ] } { IndexName: 'postIx', ConsistentRead: true, Select: 'COUNT', TableName: 'dynamo-client-integration-test', KeyConditions: { forumName: { ComparisonOperator: 'EQ', AttributeValueList: [Object] }, lastPostTime: { ComparisonOperator: 'GT', AttributeValueList: [Object] } } } { Count: 2 } { TableName: 'dynamo-client-integration-test' } { Count: 3, ScannedCount: 3, Items: [ { forumName: [Object], subject: [Object], lastPostTime: [Object] }, { forumName: [Object], subject: [Object], lastPostTime: [Object] }, { forumName: [Object], subject: [Object], lastPostTime: [Object] } ] } { RequestItems: { 'dynamo-client-integration-test': [ [Object], [Object], [Object] ] } } { UnprocessedItems: {} } { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'a' }, lastPostTime: { S: '2013-06-03T01:38:23.309Z' } } } {} { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'b' }, lastPostTime: { S: '2013-06-03T01:38:23.309Z' } } } {} { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'c' }, lastPostTime: { S: '2013-06-03T01:38:23.309Z' } } } {} { TableName: 'dynamo-client-integration-test', ScanFilter: { forumName: { ComparisonOperator: 'EQ', AttributeValueList: [Object] }, subject: { ComparisonOperator: 'GT', AttributeValueList: [Object] } } } { Count: 2, ScannedCount: 3, Items: [ { forumName: [Object], subject: [Object], lastPostTime: [Object] }, { forumName: [Object], subject: [Object], lastPostTime: [Object] } ] } { Select: 'COUNT', TableName: 'dynamo-client-integration-test', ScanFilter: { forumName: { ComparisonOperator: 'EQ', AttributeValueList: [Object] }, subject: { ComparisonOperator: 'GT', AttributeValueList: [Object] } } } { Count: 2, ScannedCount: 3 } { TableName: 'dynamo-client-integration-test' } { Count: 3, ScannedCount: 3, Items: [ { forumName: [Object], subject: [Object], lastPostTime: [Object] }, { forumName: [Object], subject: [Object], lastPostTime: [Object] }, { forumName: [Object], subject: [Object], lastPostTime: [Object] } ] } { RequestItems: { 'dynamo-client-integration-test': [ [Object], [Object], [Object] ] } } { UnprocessedItems: {} } { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'a' }, lastPostTime: { S: '2013-06-03T01:38:23.387Z' } } } {} { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'b' }, lastPostTime: { S: '2013-06-03T01:38:23.388Z' } } } {} { TableName: 'dynamo-client-integration-test', Item: { forumName: { S: 'a' }, subject: { S: 'c' }, lastPostTime: { S: '2013-06-03T01:38:23.389Z' } } } {} { Segment: 0, TotalSegments: 3, TableName: 'dynamo-client-integration-test', ScanFilter: { forumName: { ComparisonOperator: 'EQ', AttributeValueList: [Object] }, lastPostTime: { ComparisonOperator: 'GT', AttributeValueList: [Object] } } } { Segment: 1, TotalSegments: 3, TableName: 'dynamo-client-integration-test', ScanFilter: { forumName: { ComparisonOperator: 'EQ', AttributeValueList: [Object] }, lastPostTime: { ComparisonOperator: 'GT', AttributeValueList: [Object] } } } { Segment: 2, TotalSegments: 3, TableName: 'dynamo-client-integration-test', ScanFilter: { forumName: { ComparisonOperator: 'EQ', AttributeValueList: [Object] }, lastPostTime: { ComparisonOperator: 'GT', AttributeValueList: [Object] } } } { Count: 0, ScannedCount: 1, Items: [] } { Count: 1, ScannedCount: 1, Items: [ { forumName: [Object], subject: [Object], lastPostTime: [Object] } ] } { Count: 1, ScannedCount: 1, Items: [ { forumName: [Object], subject: [Object], lastPostTime: [Object] } ] } { Segment: 0, TotalSegments: 3, Select: 'COUNT', TableName: 'dynamo-client-integration-test', ScanFilter: { forumName: { ComparisonOperator: 'EQ', AttributeValueList: [Object] }, lastPostTime: { ComparisonOperator: 'GT', AttributeValueList: [Object] } } } { Segment: 1, TotalSegments: 3, Select: 'COUNT', TableName: 'dynamo-client-integration-test', ScanFilter: { forumName: { ComparisonOperator: 'EQ', AttributeValueList: [Object] }, lastPostTime: { ComparisonOperator: 'GT', AttributeValueList: [Object] } } } { Segment: 2, TotalSegments: 3, Select: 'COUNT', TableName: 'dynamo-client-integration-test', ScanFilter: { forumName: { ComparisonOperator: 'EQ', AttributeValueList: [Object] }, lastPostTime: { ComparisonOperator: 'GT', AttributeValueList: [Object] } } } { Count: 1, ScannedCount: 1 } { Count: 0, ScannedCount: 1 } { Count: 1, ScannedCount: 1 } { TableName: 'dynamo-client-integration-test' } { Count: 3, ScannedCount: 3, Items: [ { forumName: [Object], subject: [Object], lastPostTime: [Object] }, { forumName: [Object], subject: [Object], lastPostTime: [Object] }, { forumName: [Object], subject: [Object], lastPostTime: [Object] } ] } { RequestItems: { 'dynamo-client-integration-test': [ [Object], [Object], [Object] ] } } { UnprocessedItems: {} } {} { TableNames: [ 'dynamo-client-integration-test' ] } { TableName: 'dynamo-client-integration-test' } { Count: 0, ScannedCount: 0, Items: [] } { TableName: 'dynamo-client-integration-test', ProvisionedThroughput: { ReadCapacityUnits: 2, WriteCapacityUnits: 2 } } { TableDescription: { AttributeDefinitions: [ [Object], [Object], [Object] ], CreationDateTime: 1370223503, KeySchema: [ [Object], [Object] ], ProvisionedThroughput: { NumberOfDecreasesToday: 0, ReadCapacityUnits: 2, WriteCapacityUnits: 2, LastIncreaseDateTime: 1370223503 }, TableName: 'dynamo-client-integration-test', TableStatus: 'UPDATING', ItemCount: 0, TableSizeBytes: 0, LocalSecondaryIndexes: [ [Object] ] } } { TableName: 'dynamo-client-integration-test' } { Count: 0, ScannedCount: 0, Items: [] } { TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Value: [Object] } } } {} { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { Attributes: { lastId: { N: '1' } } } { TableName: 'dynamo-client-integration-test' } { Count: 1, ScannedCount: 1, Items: [ { forumName: [Object], subject: [Object], lastId: [Object] } ] } { RequestItems: { 'dynamo-client-integration-test': [ [Object] ] } } { UnprocessedItems: {} } { TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Value: [Object] } } } {} { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { Attributes: { lastId: { N: '1' } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { Attributes: { lastId: { N: '2' } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { Attributes: { lastId: { N: '3' } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { Attributes: { lastId: { N: '4' } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { Attributes: { lastId: { N: '5' } } } { TableName: 'dynamo-client-integration-test' } { Count: 1, ScannedCount: 1, Items: [ { forumName: [Object], subject: [Object], lastId: [Object] } ] } { RequestItems: { 'dynamo-client-integration-test': [ [Object] ] } } { UnprocessedItems: {} } { TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Value: [Object] } } } {} { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { ReturnValues: 'UPDATED_NEW', TableName: 'dynamo-client-integration-test', Key: { forumName: { S: '0' }, subject: { S: '0' } }, AttributeUpdates: { lastId: { Action: 'ADD', Value: [Object] } } } { Attributes: { lastId: { N: '1' } } } { Attributes: { lastId: { N: '2' } } } { Attributes: { lastId: { N: '3' } } } { Attributes: { lastId: { N: '4' } } } { Attributes: { lastId: { N: '5' } } } { Attributes: { lastId: { N: '6' } } } { Attributes: { lastId: { N: '7' } } } { Attributes: { lastId: { N: '8' } } } { Attributes: { lastId: { N: '9' } } } { Attributes: { lastId: { N: '10' } } } { Attributes: { lastId: { N: '11' } } } { Attributes: { lastId: { N: '12' } } } { Attributes: { lastId: { N: '14' } } } { Attributes: { lastId: { N: '13' } } } { Attributes: { lastId: { N: '15' } } } { Attributes: { lastId: { N: '16' } } } { Attributes: { lastId: { N: '17' } } } { Attributes: { lastId: { N: '18' } } } { Attributes: { lastId: { N: '19' } } } { Attributes: { lastId: { N: '20' } } } { TableName: 'dynamo-client-integration-test' } { Table: { AttributeDefinitions: [ [Object], [Object], [Object] ], CreationDateTime: 1370223503, KeySchema: [ [Object], [Object] ], ProvisionedThroughput: { NumberOfDecreasesToday: 0, ReadCapacityUnits: 2, WriteCapacityUnits: 2, LastIncreaseDateTime: 1370223503 }, TableName: 'dynamo-client-integration-test', TableStatus: 'ACTIVE', ItemCount: 1, TableSizeBytes: 0, LocalSecondaryIndexes: [ [Object] ] } } { TableName: 'dynamo-client-integration-test' } { TableDescription: { AttributeDefinitions: [ [Object], [Object], [Object] ], CreationDateTime: 1370223503, KeySchema: [ [Object], [Object] ], ProvisionedThroughput: { NumberOfDecreasesToday: 0, ReadCapacityUnits: 2, WriteCapacityUnits: 2, LastIncreaseDateTime: 1370223503 }, TableName: 'dynamo-client-integration-test', TableStatus: 'DELETING', ItemCount: 1, TableSizeBytes: 0, LocalSecondaryIndexes: [ [Object] ] } } { TableName: 'dynamo-client-integration-test' } { __type: 'com.amazon.dynamodb.v20111205#ResourceNotFoundException', message: 'Requested resource not found: Table: dynamo-client-integration-test not found' } Coverage

Coverage

95%
484
463
21

/Users/michael/github/dynamo-table/index.js

95%
484
463
21
LineHitsSource
11var dynamo
2
31try {
41 dynamo = require('dynamo-client')
5} catch (e) {
6 // Assume consumer will pass in client
7}
8
91module.exports = function(name, options) {
1095 return new DynamoTable(name, options)
11}
121module.exports.DynamoTable = DynamoTable
13
141function DynamoTable(name, options) {
1597 if (!name) throw new Error('Table must have a name')
1693 options = options || {}
1793 this.name = name
1893 this.client = options.client
1993 if (!this.client) {
2035 if (!dynamo) throw new Error('dynamo-client module is not installed')
2135 this.client = dynamo.createClient(options.region, options.credentials)
22 }
2393 this.mappings = options.mappings || {}
2493 this.key = options.key || Object.keys(options.keyTypes || this.mappings).slice(0, 2)
2597 if (!Array.isArray(this.key)) this.key = [this.key]
26152 if (!this.key.length) this.key = ['id']
2793 this.keyTypes = options.keyTypes || {}
2893 this.readCapacity = options.readCapacity
2993 this.writeCapacity = options.writeCapacity
3093 this.indexes = options.indexes
3193 this.scanSegments = options.scanSegments
3293 this.preMapFromDb = options.preMapFromDb
3393 this.postMapFromDb = options.postMapFromDb
3493 this.preMapToDb = options.preMapToDb
3593 this.postMapToDb = options.postMapToDb
36}
37
381DynamoTable.prototype.mapAttrToDb = function(val, key, jsObj) {
39639 var mapping = this.mappings[key]
40639 if (mapping) {
41182 if (typeof mapping.to === 'function') return mapping.to(val, key, jsObj)
42189 if (mapping === 'json') return {S: JSON.stringify(val)}
43173 if (val == null || val === '') return
44169 switch (mapping) {
45135 case 'S': return {S: String(val)}
462 case 'N': return {N: String(val)}
471 case 'B': return {B: val.toString('base64')}
481 case 'SS': return {SS: typeof val[0] === 'string' ? val : val.map(function(x) { return String(x) })}
494 case 'NS': return {NS: val.map(function(x) { return String(x) })}
504 case 'BS': return {BS: val.map(function(x) { return x.toString('base64') })}
515 case 'bignum': return Array.isArray(val) ? {NS: val} : {N: val}
5219 case 'isodate': return {S: val.toISOString()}
531 case 'timestamp': return {N: String(+val)}
541 case 'mapS': return {SS: Object.keys(val)}
551 case 'mapN': return {NS: Object.keys(val)}
561 case 'mapB': return {BS: Object.keys(val)}
57 }
58 }
59468 if (val == null || val === '') return
60448 switch (typeof val) {
6180 case 'string': return {S: val}
622 case 'boolean': return {S: String(val)}
63350 case 'number': return {N: String(val)}
64 }
6516 if (Buffer.isBuffer(val)) {
667 if (!val.length) return
673 return {B: val.toString('base64')}
68 }
6911 if (Array.isArray(val)) {
7011 if (!val.length) return
719 if (typeof val[0] === 'string') return {SS: val}
7216 if (typeof val[0] === 'number') return {NS: val.map(function(x) { return String(x) })}
7310 if (Buffer.isBuffer(val[0])) return {BS: val.map(function(x) { return x.toString('base64') })}
74 }
752 return {S: JSON.stringify(val)}
76}
77
781DynamoTable.prototype.mapAttrFromDb = function(val, key, dbItem) {
79421 var mapping = this.mappings[key]
80421 if (mapping) {
81101 if (typeof mapping.from === 'function') return mapping.from(val, key, dbItem)
8297 switch (mapping) {
8351 case 'S': return val.S
840 case 'N': return +val.N
851 case 'B': return new Buffer(val.B, 'base64')
861 case 'SS': return val.SS
874 case 'NS': return val.NS.map(function(x) { return +x })
884 case 'BS': return val.BS.map(function(x) { return new Buffer(x, 'base64') })
899 case 'json': return JSON.parse(val.S)
905 case 'bignum': return val.N != null ? val.N : val.NS
9124 case 'isodate': return new Date(val.S)
921 case 'timestamp': return new Date(val.N)
93 case 'mapS':
94 case 'mapN':
95 case 'mapB':
963 return (val.SS || val.NS || val.BS).reduce(function(mapObj, val) {
979 mapObj[val] = 1
989 return mapObj
99 }, {})
100 }
101 }
102322 if (val.S != null) {
10341 if (val.S === 'true') return true
10440 if (val.S === 'false') return false
10538 if (val.S[0] === '{' || val.S[0] === '[')
1066 try { return JSON.parse(val.S) } catch (e) {}
10735 return val.S
108 }
109555 if (val.N != null) return +val.N
11011 if (val.B != null) return new Buffer(val.B, 'base64')
1119 if (val.SS != null) return val.SS
11212 if (val.NS != null) return val.NS.map(function(x) { return +x })
11311 if (val.BS != null) return val.BS.map(function(x) { return new Buffer(x, 'base64') })
1141 throw new Error('Unknown DynamoDB type: ' + JSON.stringify(val))
115}
116
1171DynamoTable.prototype.mapToDb = function(jsObj) {
11878 if (this.preMapToDb) jsObj = this.preMapToDb(jsObj)
11978 var self = this,
120 dbItem = jsObj != null ? {} : null
121
12278 if (dbItem != null && jsObj != null) {
12377 Object.keys(jsObj).forEach(function(key) {
124176 var dbAttr = self.mapAttrToDb(jsObj[key], key, jsObj)
125176 if (!self._isEmpty(dbAttr))
126172 dbItem[key] = dbAttr
127 })
128 }
12978 if (this.postMapToDb) dbItem = this.postMapToDb(dbItem)
13078 return dbItem
131}
132
1331DynamoTable.prototype.mapFromDb = function(dbItem) {
134268 if (this.preMapFromDb) dbItem = this.preMapFromDb(dbItem)
135268 var self = this,
136 jsObj = dbItem != null ? {} : null
137
138268 if (dbItem != null && jsObj != null) {
139266 Object.keys(dbItem).forEach(function(key) {
140349 var jsAttr = self.mapAttrFromDb(dbItem[key], key, dbItem)
141348 if (typeof jsAttr !== 'undefined')
142347 jsObj[key] = jsAttr
143 })
144 }
145267 if (this.postMapFromDb) jsObj = this.postMapFromDb(jsObj)
146267 return jsObj
147}
148
1491DynamoTable.prototype.resolveKey = function(key) {
150288 var self = this
151288 if (arguments.length > 1)
1521 key = [].slice.call(arguments)
153287 else if (typeof key !== 'object' || Buffer.isBuffer(key))
154231 key = [key]
155
156288 if (Array.isArray(key)) {
157240 return key.reduce(function(dbKey, val, ix) {
158245 dbKey[self.key[ix]] = self.mapAttrToDb(val, self.key[ix])
159245 return dbKey
160 }, {})
161 }
16248 return Object.keys(key).reduce(function(dbKey, attr) {
16395 dbKey[attr] = self.mapAttrToDb(key[attr], attr)
16495 return dbKey
165 }, {})
166}
167
1681DynamoTable.prototype._isEmpty = function(attr) {
169188 return attr == null || attr.S === '' || attr.N === '' || attr.B === '' ||
170 attr.SS === '[]' || attr.NS === '[]' || attr.BS === '[]'
171}
172
1731DynamoTable.prototype._getKeyType = function(attr) {
17436 var type = this.keyTypes[attr] || this.mappings[attr] || 'S'
17536 switch (type) {
176 case 'N':
177 case 'S':
178 case 'B':
17934 return type
180 case 'json':
181 case 'isodate':
1821 return 'S'
183 case 'bignum':
184 case 'timestamp':
1851 return 'N'
186 }
1870 throw new Error('Unsupported key type (' + type + ') for attr ' + attr)
188}
189
1901DynamoTable.prototype.get = function(key, options, cb) {
19117 if (!cb) { cb = options; options = {} }
1929 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
1939 options = this._getDefaultOptions(options)
1949 var self = this
195
1969 options.Key = options.Key || this.resolveKey(key)
1979 this.client.request('GetItem', options, function(err, data) {
19810 if (err) return cb(err)
1998 cb(null, self.mapFromDb(data.Item))
200 })
201}
202
2031DynamoTable.prototype.put = function(jsObj, options, cb) {
20445 if (!cb) { cb = options; options = {} }
20515 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
20615 options.TableName = options.TableName || this.name
207
20815 options.Item = options.Item || this.mapToDb(jsObj)
20915 this.client.request('PutItem', options, cb)
210}
211
2121DynamoTable.prototype.delete = function(key, options, cb) {
2133 if (!cb) { cb = options; options = {} }
2141 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
2151 options.TableName = options.TableName || this.name
216
2171 options.Key = options.Key || this.resolveKey(key)
2181 this.client.request('DeleteItem', options, cb)
219}
220
2211DynamoTable.prototype.update = function(key, actions, options, cb) {
22258 if (!cb) { cb = options; options = {} }
22344 if (!cb) { cb = actions; actions = key; key = null }
22439 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
22537 options = this._getDefaultOptions(options)
22637 var self = this, pick, attrUpdates
227
228 // If actions is a string or array, then it's a whitelist for attributes to update
22939 if (typeof actions === 'string') actions = [actions]
23046 if (Array.isArray(actions)) { pick = actions; actions = key; key = null }
231
232 // If key is null, assume actions has a full object to put so clone it (without keys)
23337 if (key == null) {
2348 key = this.key.map(function(attr) { return actions[attr] })
2354 pick = pick || Object.keys(actions)
2364 actions = {put: pick.reduce(function(attrsObj, attr) {
23715 if (!~self.key.indexOf(attr)) attrsObj[attr] = actions[attr]
2388 return attrsObj
239 }, {})}
240 }
241
242 // If we have some attributes that are not actions (put, add, delete), then throw
24376 if (Object.keys(actions).some(function(attr) { return !~['put', 'add', 'delete'].indexOf(attr) }))
2440 throw new Error('actions must only contain put/add/delete attributes')
245
24637 options.Key = options.Key || this.resolveKey(key)
24737 attrUpdates = options.AttributeUpdates = options.AttributeUpdates || {}
248
24937 if (actions.put != null) {
2508 Object.keys(actions.put).forEach(function(attr) {
25112 attrUpdates[attr] = attrUpdates[attr] || {Value: self.mapAttrToDb(actions.put[attr], attr)}
25212 if (self._isEmpty(attrUpdates[attr].Value)) {
2534 attrUpdates[attr].Action = 'DELETE' // "empty" attributes should actually be deleted
2544 delete attrUpdates[attr].Value
255 }
256 })
257 }
258
25937 if (actions.add != null) {
26029 Object.keys(actions.add).forEach(function(attr) {
26129 attrUpdates[attr] = attrUpdates[attr] ||
262 {Action: 'ADD', Value: self.mapAttrToDb(actions.add[attr], attr)}
263 })
264 }
265
26637 if (actions.delete != null) {
2673 if (!Array.isArray(actions.delete)) actions.delete = [actions.delete]
2682 actions.delete.forEach(function(attr) {
2693 if (typeof attr === 'string') {
2702 attrUpdates[attr] = attrUpdates[attr] || {Action: 'DELETE'}
271 } else {
2721 Object.keys(attr).forEach(function(setKey) {
2731 attrUpdates[setKey] = attrUpdates[setKey] ||
274 {Action: 'DELETE', Value: self.mapAttrToDb(attr[setKey], setKey)}
275 })
276 }
277 })
278 }
279
28037 if (!Object.keys(attrUpdates).length) return process.nextTick(cb)
281
28237 this.client.request('UpdateItem', options, cb)
283}
284
2851DynamoTable.prototype.query = function(conditions, options, cb) {
28614 if (!cb) { cb = options; options = {} }
2878 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
2888 options = this._getDefaultOptions(options)
2898 var self = this, nonKeys
290
2918 options.KeyConditions = options.KeyConditions || this.conditions(conditions)
2928 if (!options.IndexName) {
29316 nonKeys = Object.keys(options.KeyConditions).filter(function(attr) { return !~self.key.indexOf(attr) })
2946 if (nonKeys.length) {
295 // we have a non-key attribute, must find an IndexName
2962 this.resolveIndexes(this.indexes).forEach(function(index) {
2971 if (index.key === nonKeys[0])
2981 options.IndexName = index.name
299 })
3002 options.IndexName = options.IndexName || nonKeys[0]
301 }
302 }
3038 this._listRequest('Query', options, cb)
304}
305
3061DynamoTable.prototype.scan = function(conditions, options, cb) {
30769 if (!cb) { cb = options; options = {} }
30863 if (!cb) { cb = conditions; conditions = null }
30937 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
31037 options = this._getDefaultOptions(options)
31137 var totalSegments, segment, allItems
312
31345 if (conditions != null && !options.ScanFilter) options.ScanFilter = this.conditions(conditions)
31437 options.TotalSegments = options.TotalSegments || this.scanSegments
315
31637 if (options.Segment == null && options.TotalSegments) {
3175 totalSegments = options.TotalSegments
3185 allItems = new Array(totalSegments)
3195 for (segment = 0; segment < totalSegments; segment++)
32015 this.scan(null, cloneWithSegment(options, segment), checkDone(segment))
321 } else {
32232 this._listRequest('Scan', options, cb)
323 }
32437 function cloneWithSegment(options, segment) {
32515 return Object.keys(options).reduce(function(clone, key) {
32651 clone[key] = options[key]
32751 return clone
328 }, {Segment: segment})
329 }
33037 function checkDone(segment) {
33115 return function (err, items) {
33215 if (err) return cb(err)
33315 allItems[segment] = items
33415 if (!--totalSegments) {
3355 if (options.Select === 'COUNT')
3366 return cb(null, allItems.reduce(function(sum, count) { return sum + count }))
3373 cb(null, [].concat.apply([], allItems))
338 }
339 }
340 }
341}
342
343// http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html
3441DynamoTable.MAX_GET = 100
3451DynamoTable.prototype.batchGet = function(keys, options, tables, cb) {
34613 if (!cb) { cb = tables; tables = [] }
34711 if (!cb) { cb = options; options = {} }
3485 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
3495 var self = this,
350 onlyThis = false,
351 tablesByName = {},
352 allKeys, numRequests, allResults, i, j, key, requestItems, requestItem, opt
353
3545 if (keys && keys.length) {
3555 tables.unshift({table: this, keys: keys, options: options})
3565 onlyThis = tables.length === 1
357 }
3585 allKeys = tables.map(function(tableObj) {
3596 var table = tableObj.table, keys = tableObj.keys, options = tableObj.options
3606 tablesByName[table.name] = table
3616 if (Array.isArray(options))
3620 options = {AttributesToGet: options}
3636 else if (typeof options === 'string')
3642 options = {AttributesToGet: [options]}
3656 return keys.map(function(key) {
366215 var dbKey = table.resolveKey(key)
367215 dbKey._table = table.name
368215 dbKey._options = options || {}
369215 return dbKey
370 })
371 })
3725 allKeys = [].concat.apply([], allKeys)
3735 numRequests = Math.ceil(allKeys.length / DynamoTable.MAX_GET)
3745 allResults = new Array(numRequests)
375
3765 for (i = 0; i < allKeys.length; i += DynamoTable.MAX_GET) {
3777 requestItems = {}
3787 for (j = i; j < i + DynamoTable.MAX_GET && j < allKeys.length; j++) {
379215 key = allKeys[j]
380215 requestItem = requestItems[key._table] = (requestItems[key._table] || {})
381215 for (opt in key._options)
3826 requestItem[opt] = key._options[opt]
383215 requestItem.Keys = requestItem.Keys || []
384215 requestItem.Keys.push(key)
385215 delete key._table
386215 delete key._options
387 }
3887 batchRequest(requestItems, checkDone(i / DynamoTable.MAX_GET))
389 }
390
3915 function batchRequest(requestItems, results, cb) {
39223 if (!cb) { cb = results; results = {} }
3939 self.client.request('BatchGetItem', {RequestItems: requestItems}, function(err, data) {
3949 if (err) return cb(err)
3959 for (var name in data.Responses) {
39610 results[name] = (results[name] || []).concat(
397 data.Responses[name].map(tablesByName[name].mapFromDb.bind(tablesByName[name])))
398 }
3999 if (Object.keys(data.UnprocessedKeys || {}).length)
4002 return batchRequest(data.UnprocessedKeys, results, cb)
4017 cb(null, results)
402 })
403 }
404
4055 function checkDone(ix) {
4067 return function (err, results) {
4077 if (err) return cb(err)
4087 allResults[ix] = results
4097 if (!--numRequests) {
4105 var merged = {}
4115 allResults.forEach(function(results) {
4127 for (var name in results)
4138 merged[name] = (merged[name] || []).concat(results[name])
414 })
4155 cb(null, onlyThis ? merged[self.name] : merged)
416 }
417 }
418 }
419}
420
421// http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html
4221DynamoTable.MAX_WRITE = 25
4231DynamoTable.prototype.batchWrite = function(operations, tables, cb) {
42440 if (!cb) { cb = tables; tables = [] }
42514 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
42614 var self = this,
427 allOperations, numRequests, i, j, requestItems, operation
428
42914 if (operations && Object.keys(operations).length)
43014 tables.unshift({table: this, operations: operations})
431
43214 allOperations = tables.map(function(tableObj) {
43315 var table = tableObj.table, operations = tableObj.operations || [], ops
43420 if (Array.isArray(operations)) operations = {puts: operations, deletes: []}
43515 ops = (operations.puts || []).map(function(jsObj) {
43660 return {PutRequest: {Item: table.mapToDb(jsObj)}, _table: table.name}
437 })
43815 return ops.concat((operations.deletes || []).map(function(key) {
43920 return {DeleteRequest: {Key: table.resolveKey(key)}, _table: table.name}
440 }))
441 })
44214 allOperations = [].concat.apply([], allOperations)
44314 numRequests = Math.ceil(allOperations.length / DynamoTable.MAX_WRITE)
444
44514 for (i = 0; i < allOperations.length; i += DynamoTable.MAX_WRITE) {
44616 requestItems = {}
44716 for (j = i; j < i + DynamoTable.MAX_WRITE && j < allOperations.length; j++) {
44880 operation = allOperations[j]
44980 requestItems[operation._table] = requestItems[operation._table] || []
45080 requestItems[operation._table].push(operation)
45180 delete operation._table
452 }
45316 batchRequest(requestItems, checkDone)
454 }
455
45614 function batchRequest(requestItems, cb) {
45718 self.client.request('BatchWriteItem', {RequestItems: requestItems}, function(err, data) {
45818 if (err) return cb(err)
45918 if (Object.keys(data.UnprocessedItems || {}).length)
4602 return batchRequest(data.UnprocessedItems, cb)
46116 cb()
462 })
463 }
464
46514 function checkDone(err) {
46616 if (err) return cb(err)
46716 if (!--numRequests)
46814 cb()
469 }
470}
471
4721DynamoTable.prototype.createTable = function(readCapacity, writeCapacity, indexes, options, cb) {
47343 if (!cb) { cb = options; options = {} }
47427 if (!cb) { cb = indexes; indexes = this.indexes }
47525 if (!cb) { cb = writeCapacity; writeCapacity = this.writeCapacity || 1 }
47625 if (!cb) { cb = readCapacity; readCapacity = this.readCapacity || 1 }
47715 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
47815 options.TableName = options.TableName || this.name
47915 var self = this,
480 attrMap = this.key.reduce(function(namesObj, attr) {
48125 namesObj[attr] = true
48225 return namesObj
483 }, {})
484
48515 if (indexes && !options.LocalSecondaryIndexes) {
48610 options.LocalSecondaryIndexes = this.resolveIndexes(indexes).map(function(index) {
48713 var lsi = {
488 IndexName: index.name,
489 KeySchema: [self.key[0], index.key].map(function(attr, ix) {
49026 return { AttributeName: attr, KeyType: ix === 0 ? 'HASH' : 'RANGE' }
491 }),
492 }
49313 if (typeof index.projection === 'string')
49412 lsi.Projection = {ProjectionType: index.projection}
4951 else if (Array.isArray(index.projection))
4961 lsi.Projection = {ProjectionType: 'INCLUDE', NonKeyAttributes: index.projection}
497
49813 attrMap[index.key] = true
499
50013 return lsi
501 })
502 }
50315 options.KeySchema = options.KeySchema || this.key.map(function(attr, ix) {
50425 return {AttributeName: attr, KeyType: ix === 0 ? 'HASH' : 'RANGE'}
505 })
50615 options.AttributeDefinitions = options.AttributeDefinitions || Object.keys(attrMap).map(function(attr) {
50736 return {AttributeName: attr, AttributeType: self._getKeyType(attr)}
508 })
50915 options.ProvisionedThroughput = options.ProvisionedThroughput || {
510 ReadCapacityUnits: readCapacity,
511 WriteCapacityUnits: writeCapacity,
512 }
51315 this.client.request('CreateTable', options, function(err, data) {
51415 if (err) return cb(err)
51515 cb(null, data.TableDescription)
516 })
517}
518
5191DynamoTable.prototype.describeTable = function(options, cb) {
52018 if (!cb) { cb = options; options = {} }
5216 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
5226 options.TableName = options.TableName || this.name
523
5246 this.client.request('DescribeTable', options, function(err, data) {
5259 if (err) return cb(err)
5263 cb(null, data.Table)
527 })
528}
529
5301DynamoTable.prototype.updateTable = function(readCapacity, writeCapacity, options, cb) {
5316 if (!cb) { cb = options; options = {} }
5322 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
5332 options.TableName = options.TableName || this.name
534
5352 options.ProvisionedThroughput = options.ProvisionedThroughput || {
536 ReadCapacityUnits: readCapacity,
537 WriteCapacityUnits: writeCapacity,
538 }
5392 this.client.request('UpdateTable', options, function(err, data) {
5402 if (err) return cb(err)
5412 cb(null, data.TableDescription)
542 })
543}
544
5451DynamoTable.prototype.deleteTable = function(options, cb) {
5464 if (!cb) { cb = options; options = {} }
5472 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
5482 options.TableName = options.TableName || this.name
549
5502 this.client.request('DeleteTable', options, function(err, data) {
5512 if (err) return cb(err)
5522 cb(null, data.TableDescription)
553 })
554}
555
556// TODO: Support ExclusiveStartTableName/LastEvaluatedTableName
5571DynamoTable.prototype.listTables = function(options, cb) {
5586 if (!cb) { cb = options; options = {} }
5592 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
560
5612 this.client.request('ListTables', options, function(err, data) {
5622 if (err) return cb(err)
5632 cb(null, data.TableNames)
564 })
565}
566
5671DynamoTable.prototype.deleteTableAndWait = function(options, cb) {
5687 if (!cb) { cb = options; options = {} }
5693 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
5703 var self = this
5713 this.describeTable(function(err, data) {
5725 if (err && err.name === 'ResourceNotFoundException') return cb()
5731 if (err) return cb(err)
574
5751 if (data.TableStatus !== 'ACTIVE') return setTimeout(self.deleteTableAndWait.bind(self, options, cb), 1000)
576
5771 self.deleteTable(options, function(err) {
5781 if (err) return cb(err)
5791 self.deleteTableAndWait(options, cb)
580 })
581 })
582}
583
5841DynamoTable.prototype.createTableAndWait = function(readCapacity, writeCapacity, indexes, options, cb) {
5854 if (!cb) { cb = options; options = {} }
5864 if (!cb) { cb = indexes; indexes = this.indexes }
5874 if (!cb) { cb = writeCapacity; writeCapacity = this.writeCapacity || 1 }
5884 if (!cb) { cb = readCapacity; readCapacity = this.readCapacity || 1 }
5892 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
5902 var self = this
5912 this.describeTable(function(err, data) {
5922 if (err && err.name === 'ResourceNotFoundException') {
5931 return self.createTable(readCapacity, writeCapacity, indexes, options, function(err) {
5941 if (err) return cb(err)
5951 self.createTableAndWait(readCapacity, writeCapacity, indexes, options, cb)
596 })
597 }
5981 if (err) return cb(err)
599
6002 if (data.TableStatus === 'ACTIVE') return cb()
6010 if (data.TableStatus !== 'CREATING') return cb(new Error(data.TableStatus))
602
6030 setTimeout(self.createTableAndWait.bind(self, readCapacity, writeCapacity, indexes, options, cb), 1000)
604 })
605}
606
6071DynamoTable.prototype.increment = function(key, attr, incrAmt, options, cb) {
60828 var self = this, actions
60984 if (!cb) { cb = options; options = {} }
61082 if (!cb) { cb = incrAmt; incrAmt = 1 }
61128 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
612
61328 options.ReturnValues = options.ReturnValues || 'UPDATED_NEW'
61428 actions = {add: {}}
61528 actions.add[attr] = incrAmt
61628 this.update(key, actions, options, function(err, data) {
61728 if (err) return cb(err)
61828 var newVal = (data.Attributes != null ? data.Attributes[attr] : null)
61928 if (newVal == null) return cb()
62028 cb(null, self.mapAttrFromDb(newVal, attr))
621 })
622}
623
6241DynamoTable.prototype._listRequest = function(operation, items, options, cb) {
625164 if (!cb) { cb = options; options = items; items = [] }
62644 if (typeof cb !== 'function') throw new Error('Last parameter must be a callback function')
62744 var self = this
628
62944 this.client.request(operation, options, function(err, data) {
63044 if (err) return cb(err)
63155 if (options.Select === 'COUNT') return cb(null, data.Count)
632
63373 items = items.concat(data.Items.map(function(item) { return self.mapFromDb(item) }))
63433 if (data.LastEvaluatedKey != null && (!options.Limit || options.Limit !== (data.ScannedCount || data.Count))) {
6354 options.ExclusiveStartKey = data.LastEvaluatedKey
6364 return self._listRequest(operation, items, options, cb)
637 }
63829 cb(null, items)
639 })
640}
641
6421DynamoTable.prototype.conditions = function(conditionExprObj) {
64316 var self = this
64416 return Object.keys(conditionExprObj).reduce(function(condObj, attr) {
64530 condObj[attr] = self.condition(attr, conditionExprObj[attr])
64630 return condObj
647 }, {})
648}
649
6501DynamoTable.prototype.condition = function(key, conditionExpr) {
65130 var self = this,
652 type = typeof conditionExpr,
653 comparison, attrVals, cond
654
65530 if (conditionExpr === null) {
6560 comparison = 'NULL'
65730 } else if (conditionExpr === 'notNull' || conditionExpr === 'NOT_NULL') {
6580 comparison = 'NOT_NULL'
65930 } else if (type === 'string' || type === 'number' || Buffer.isBuffer(conditionExpr)) {
66016 comparison = 'EQ'
66116 attrVals = [conditionExpr]
66214 } else if (Array.isArray(conditionExpr)) {
6630 comparison = 'IN'
6640 attrVals = conditionExpr
665 } else {
66614 comparison = Object.keys(conditionExpr)[0]
66714 attrVals = conditionExpr[comparison]
66828 if (!Array.isArray(attrVals)) attrVals = [attrVals]
66914 comparison = this.comparison(comparison)
670 }
67130 cond = {ComparisonOperator: comparison}
67230 if (attrVals != null)
67360 cond.AttributeValueList = attrVals.map(function(val) { return self.mapAttrToDb(val, key) })
67430 return cond
675}
676
6771DynamoTable.prototype.comparison = function(comparison) {
67814 switch (comparison) {
6790 case '=': return 'EQ'
6800 case '==': return 'EQ'
6810 case '!=': return 'NE'
6820 case '<=': return 'LE'
6830 case '<': return 'LT'
68414 case '>': return 'GT'
6850 case '>=': return 'GE'
6860 case '>=<=': return 'BETWEEN'
687 case 'beginsWith':
688 case 'startsWith':
6890 return 'BEGINS_WITH'
690 case 'notContains':
691 case 'doesNotContain':
6920 return 'NOT_CONTAINS'
693 }
6940 return comparison.toUpperCase()
695}
696
697// indexes:
698// [attr1/name1, attr2/name2]
699// {name: attr1}
700// {name: {key: attr1, projection: 'KEYS_ONLY'}}
701// {name: {key: attr1, projection: [attr1, attr2]}}
702// [{name: name1, key: attr1}, {name: name2, key: attr2}]
7031DynamoTable.prototype.resolveIndexes = function(indexes) {
70413 if (!indexes) return []
70511 if (!Array.isArray(indexes)) {
7067 indexes = Object.keys(indexes).map(function(name) {
7078 var index = indexes[name]
70814 if (typeof index === 'string') index = {key: index}
7098 return {name: name, key: index.key, projection: index.projection}
710 })
711 }
71211 return indexes.map(function(index) {
71318 if (typeof index === 'string') index = {name: index, key: index}
71414 index.projection = index.projection || 'ALL'
71514 return index
716 })
717}
718
7191DynamoTable.prototype._getDefaultOptions = function(options) {
72091 if (options == null)
7210 options = {}
72291 else if (Array.isArray(options))
7231 options = {AttributesToGet: options}
72490 else if (typeof options === 'string')
7251 options = {AttributesToGet: [options]}
72691 options.TableName = options.TableName || this.name
72791 return options
728}
729
730