document_client.js (15858B)
1 var AWS = require('../core'); 2 var Translator = require('./translator'); 3 var DynamoDBSet = require('./set'); 4 5 /** 6 * The document client simplifies working with items in Amazon DynamoDB 7 * by abstracting away the notion of attribute values. This abstraction 8 * annotates native JavaScript types supplied as input parameters, as well 9 * as converts annotated response data to native JavaScript types. 10 * 11 * ## Marshalling Input and Unmarshalling Response Data 12 * 13 * The document client affords developers the use of native JavaScript types 14 * instead of `AttributeValue`s to simplify the JavaScript development 15 * experience with Amazon DynamoDB. JavaScript objects passed in as parameters 16 * are marshalled into `AttributeValue` shapes required by Amazon DynamoDB. 17 * Responses from DynamoDB are unmarshalled into plain JavaScript objects 18 * by the `DocumentClient`. The `DocumentClient`, does not accept 19 * `AttributeValue`s in favor of native JavaScript types. 20 * 21 * | JavaScript Type | DynamoDB AttributeValue | 22 * |:----------------------------------------------------------------------:|-------------------------| 23 * | String | S | 24 * | Number | N | 25 * | Boolean | BOOL | 26 * | null | NULL | 27 * | Array | L | 28 * | Object | M | 29 * | Buffer, File, Blob, ArrayBuffer, DataView, and JavaScript typed arrays | B | 30 * 31 * ## Support for Sets 32 * 33 * The `DocumentClient` offers a convenient way to create sets from 34 * JavaScript Arrays. The type of set is inferred from the first element 35 * in the array. DynamoDB supports string, number, and binary sets. To 36 * learn more about supported types see the 37 * [Amazon DynamoDB Data Model Documentation](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DataModel.html) 38 * For more information see {AWS.DynamoDB.DocumentClient.createSet} 39 * 40 */ 41 AWS.DynamoDB.DocumentClient = AWS.util.inherit({ 42 43 /** 44 * @api private 45 */ 46 operations: { 47 batchGetItem: 'batchGet', 48 batchWriteItem: 'batchWrite', 49 putItem: 'put', 50 getItem: 'get', 51 deleteItem: 'delete', 52 updateItem: 'update', 53 scan: 'scan', 54 query: 'query' 55 }, 56 57 /** 58 * Creates a DynamoDB document client with a set of configuration options. 59 * 60 * @option options params [map] An optional map of parameters to bind to every 61 * request sent by this service object. 62 * @option options service [AWS.DynamoDB] An optional pre-configured instance 63 * of the AWS.DynamoDB service object to use for requests. The object may 64 * bound parameters used by the document client. 65 * @see AWS.DynamoDB.constructor 66 * 67 */ 68 constructor: function DocumentClient(options) { 69 var self = this; 70 self.options = options || {}; 71 self.configure(self.options); 72 }, 73 74 /** 75 * @api private 76 */ 77 configure: function configure(options) { 78 var self = this; 79 self.service = options.service; 80 self.bindServiceObject(options); 81 self.attrValue = 82 self.service.api.operations.putItem.input.members.Item.value.shape; 83 }, 84 85 /** 86 * @api private 87 */ 88 bindServiceObject: function bindServiceObject(options) { 89 var self = this; 90 options = options || {}; 91 92 if (!self.service) { 93 self.service = new AWS.DynamoDB(options); 94 } else { 95 var config = AWS.util.copy(self.service.config); 96 self.service = new self.service.constructor.__super__(config); 97 self.service.config.params = 98 AWS.util.merge(self.service.config.params || {}, options.params); 99 } 100 }, 101 102 /** 103 * Returns the attributes of one or more items from one or more tables 104 * by delegating to `AWS.DynamoDB.batchGetItem()`. 105 * 106 * Supply the same parameters as {AWS.DynamoDB.batchGetItem} with 107 * `AttributeValue`s substituted by native JavaScript types. 108 * 109 * @see AWS.DynamoDB.batchGetItem 110 * @example Get items from multiple tables 111 * var params = { 112 * RequestItems: { 113 * 'Table-1': { 114 * Keys: [ 115 * { 116 * HashKey: 'haskey', 117 * NumberRangeKey: 1 118 * } 119 * ] 120 * }, 121 * 'Table-2': { 122 * Keys: [ 123 * { foo: 'bar' }, 124 * ] 125 * } 126 * } 127 * }; 128 * 129 * var docClient = new AWS.DynamoDB.DocumentClient(); 130 * 131 * docClient.batchGet(params, function(err, data) { 132 * if (err) console.log(err); 133 * else console.log(data); 134 * }); 135 * 136 */ 137 batchGet: function(params, callback) { 138 var self = this; 139 var request = self.service.batchGetItem(params); 140 self.setupRequest(request); 141 self.setupResponse(request); 142 if (typeof callback === 'function') { 143 request.send(callback); 144 } 145 return request; 146 }, 147 148 /** 149 * Puts or deletes multiple items in one or more tables by delegating 150 * to `AWS.DynamoDB.batchWriteItem()`. 151 * 152 * Supply the same parameters as {AWS.DynamoDB.batchWriteItem} with 153 * `AttributeValue`s substituted by native JavaScript types. 154 * 155 * @see AWS.DynamoDB.batchWriteItem 156 * @example Write to and delete from a table 157 * var params = { 158 * RequestItems: { 159 * 'Table-1': [ 160 * { 161 * DeleteRequest: { 162 * Key: { HashKey: 'someKey' } 163 * } 164 * }, 165 * { 166 * PutRequest: { 167 * Item: { 168 * HashKey: 'anotherKey', 169 * NumAttribute: 1, 170 * BoolAttribute: true, 171 * ListAttribute: [1, 'two', false], 172 * MapAttribute: { foo: 'bar' } 173 * } 174 * } 175 * } 176 * ] 177 * } 178 * }; 179 * 180 * var docClient = new AWS.DynamoDB.DocumentClient(); 181 * 182 * docClient.batchWrite(params, function(err, data) { 183 * if (err) console.log(err); 184 * else console.log(data); 185 * }); 186 * 187 */ 188 batchWrite: function(params, callback) { 189 var self = this; 190 var request = self.service.batchWriteItem(params); 191 self.setupRequest(request); 192 self.setupResponse(request); 193 if (typeof callback === 'function') { 194 request.send(callback); 195 } 196 return request; 197 }, 198 199 /** 200 * Deletes a single item in a table by primary key by delegating to 201 * `AWS.DynamoDB.deleteItem()` 202 * 203 * Supply the same parameters as {AWS.DynamoDB.deleteItem} with 204 * `AttributeValue`s substituted by native JavaScript types. 205 * 206 * @see AWS.DynamoDB.deleteItem 207 * @example Delete an item from a table 208 * var params = { 209 * TableName : 'Table', 210 * Key: { 211 * HashKey: 'hashkey', 212 * NumberRangeKey: 1 213 * } 214 * }; 215 * 216 * var docClient = new AWS.DynamoDB.DocumentClient(); 217 * 218 * docClient.delete(params, function(err, data) { 219 * if (err) console.log(err); 220 * else console.log(data); 221 * }); 222 * 223 */ 224 delete: function(params, callback) { 225 var self = this; 226 var request = self.service.deleteItem(params); 227 self.setupRequest(request); 228 self.setupResponse(request); 229 if (typeof callback === 'function') { 230 request.send(callback); 231 } 232 return request; 233 }, 234 235 /** 236 * Returns a set of attributes for the item with the given primary key 237 * by delegating to `AWS.DynamoDB.getItem()`. 238 * 239 * Supply the same parameters as {AWS.DynamoDB.getItem} with 240 * `AttributeValue`s substituted by native JavaScript types. 241 * 242 * @see AWS.DynamoDB.getItem 243 * @example Get an item from a table 244 * var params = { 245 * TableName : 'Table', 246 * Key: { 247 * HashKey: 'hashkey' 248 * } 249 * }; 250 * 251 * var docClient = new AWS.DynamoDB.DocumentClient(); 252 * 253 * docClient.get(params, function(err, data) { 254 * if (err) console.log(err); 255 * else console.log(data); 256 * }); 257 * 258 */ 259 get: function(params, callback) { 260 var self = this; 261 var request = self.service.getItem(params); 262 self.setupRequest(request); 263 self.setupResponse(request); 264 if (typeof callback === 'function') { 265 request.send(callback); 266 } 267 return request; 268 }, 269 270 /** 271 * Creates a new item, or replaces an old item with a new item by 272 * delegating to `AWS.DynamoDB.putItem()`. 273 * 274 * Supply the same parameters as {AWS.DynamoDB.putItem} with 275 * `AttributeValue`s substituted by native JavaScript types. 276 * 277 * @see AWS.DynamoDB.putItem 278 * @example Create a new item in a table 279 * var params = { 280 * TableName : 'Table', 281 * Item: { 282 * HashKey: 'haskey', 283 * NumAttribute: 1, 284 * BoolAttribute: true, 285 * ListAttribute: [1, 'two', false], 286 * MapAttribute: { foo: 'bar'}, 287 * NullAttribute: null 288 * } 289 * }; 290 * 291 * var docClient = new AWS.DynamoDB.DocumentClient(); 292 * 293 * docClient.put(params, function(err, data) { 294 * if (err) console.log(err); 295 * else console.log(data); 296 * }); 297 * 298 */ 299 put: function put(params, callback) { 300 var self = this; 301 var request = self.service.putItem(params); 302 self.setupRequest(request); 303 self.setupResponse(request); 304 if (typeof callback === 'function') { 305 request.send(callback); 306 } 307 return request; 308 }, 309 310 /** 311 * Edits an existing item's attributes, or adds a new item to the table if 312 * it does not already exist by delegating to `AWS.DynamoDB.updateItem()`. 313 * 314 * Supply the same parameters as {AWS.DynamoDB.updateItem} with 315 * `AttributeValue`s substituted by native JavaScript types. 316 * 317 * @see AWS.DynamoDB.updateItem 318 * @example Update an item with expressions 319 * var params = { 320 * TableName: 'Table', 321 * Key: { HashKey : 'hashkey' }, 322 * UpdateExpression: 'set #a = :x + :y', 323 * ConditionExpression: '#a < :MAX', 324 * ExpressionAttributeNames: {'#a' : 'Sum'}, 325 * ExpressionAttributeValues: { 326 * ':x' : 20, 327 * ':y' : 45, 328 * ':MAX' : 100, 329 * } 330 * }; 331 * 332 * var docClient = new AWS.DynamoDB.DocumentClient(); 333 * 334 * docClient.update(params, function(err, data) { 335 * if (err) console.log(err); 336 * else console.log(data); 337 * }); 338 * 339 */ 340 update: function(params, callback) { 341 var self = this; 342 var request = self.service.updateItem(params); 343 self.setupRequest(request); 344 self.setupResponse(request); 345 if (typeof callback === 'function') { 346 request.send(callback); 347 } 348 return request; 349 }, 350 351 /** 352 * Returns one or more items and item attributes by accessing every item 353 * in a table or a secondary index. 354 * 355 * Supply the same parameters as {AWS.DynamoDB.scan} with 356 * `AttributeValue`s substituted by native JavaScript types. 357 * 358 * @see AWS.DynamoDB.scan 359 * @example Scan the table with a filter expression 360 * var params = { 361 * TableName : 'Table', 362 * FilterExpression : 'Year = :this_year', 363 * ExpressionAttributeValues : {':this_year' : 2015} 364 * }; 365 * 366 * var docClient = new AWS.DynamoDB.DocumentClient(); 367 * 368 * docClient.scan(params, function(err, data) { 369 * if (err) console.log(err); 370 * else console.log(data); 371 * }); 372 * 373 */ 374 scan: function(params, callback) { 375 var self = this; 376 var request = self.service.scan(params); 377 self.setupRequest(request); 378 self.setupResponse(request); 379 if (typeof callback === 'function') { 380 request.send(callback); 381 } 382 return request; 383 }, 384 385 /** 386 * Directly access items from a table by primary key or a secondary index. 387 * 388 * Supply the same parameters as {AWS.DynamoDB.query} with 389 * `AttributeValue`s substituted by native JavaScript types. 390 * 391 * @see AWS.DynamoDB.query 392 * @example Query an index 393 * var params = { 394 * TableName: 'Table', 395 * IndexName: 'Index', 396 * KeyConditionExpression: 'HashKey = :hkey and RangeKey > :rkey', 397 * ExpressionAttributeValues: { 398 * ':hkey': 'key', 399 * ':rkey': 2015 400 * } 401 * }; 402 * 403 * var docClient = new AWS.DynamoDB.DocumentClient(); 404 * 405 * docClient.query(params, function(err, data) { 406 * if (err) console.log(err); 407 * else console.log(data); 408 * }); 409 * 410 */ 411 query: function(params, callback) { 412 var self = this; 413 var request = self.service.query(params); 414 self.setupRequest(request); 415 self.setupResponse(request); 416 if (typeof callback === 'function') { 417 request.send(callback); 418 } 419 return request; 420 }, 421 422 /** 423 * Creates a set of elements inferring the type of set from 424 * the type of the first element. Amazon DynamoDB currently supports 425 * the number sets, string sets, and binary sets. For more information 426 * about DynamoDB data types see the documentation on the 427 * [Amazon DynamoDB Data Model](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DataModel.html#DataModel.DataTypes). 428 * 429 * @param list [Array] Collection to represent your DynamoDB Set 430 * @param options [map] 431 * * **validate** [Boolean] set to true if you want to validate the type 432 * of each element in the set. Defaults to `false`. 433 * @example Creating a number set 434 * var docClient = new AWS.DynamoDB.DocumentClient(); 435 * 436 * var params = { 437 * Item: { 438 * hashkey: 'hashkey' 439 * numbers: docClient.createSet([1, 2, 3]); 440 * } 441 * }; 442 * 443 * docClient.put(params, function(err, data) { 444 * if (err) console.log(err); 445 * else console.log(data); 446 * }); 447 * 448 */ 449 createSet: function(list, options) { 450 options = options || {}; 451 return new DynamoDBSet(list, options); 452 }, 453 454 /** 455 * @api private 456 */ 457 getTranslator: function() { 458 return new Translator({attrValue: this.attrValue}); 459 }, 460 461 /** 462 * @api private 463 */ 464 setupRequest: function setupRequest(request) { 465 var self = this; 466 var translator = self.getTranslator(); 467 var operation = request.operation; 468 var inputShape = request.service.api.operations[operation].input; 469 request._events.validate.unshift(function(req) { 470 req.rawParams = AWS.util.copy(req.params); 471 req.params = translator.translateInput(req.rawParams, inputShape); 472 }); 473 }, 474 475 /** 476 * @api private 477 */ 478 setupResponse: function setupResponse(request) { 479 var self = this; 480 var translator = self.getTranslator(); 481 var outputShape = self.service.api.operations[request.operation].output; 482 request.on('extractData', function(response) { 483 response.data = translator.translateOutput(response.data, outputShape); 484 }); 485 486 var response = request.response; 487 response.nextPage = function(cb) { 488 var resp = this; 489 var req = resp.request; 490 var config; 491 var service = req.service; 492 var operation = req.operation; 493 try { 494 config = service.paginationConfig(operation, true); 495 } catch (e) { resp.error = e; } 496 497 if (!resp.hasNextPage()) { 498 if (cb) cb(resp.error, null); 499 else if (resp.error) throw resp.error; 500 return null; 501 } 502 503 var params = AWS.util.copy(req.rawParams); 504 if (!resp.nextPageTokens) { 505 return cb ? cb(null, null) : null; 506 } else { 507 var inputTokens = config.inputToken; 508 if (typeof inputTokens === 'string') inputTokens = [inputTokens]; 509 for (var i = 0; i < inputTokens.length; i++) { 510 params[inputTokens[i]] = resp.nextPageTokens[i]; 511 } 512 return self[operation](params, cb); 513 } 514 }; 515 } 516 517 }); 518 519 module.exports = AWS.DynamoDB.DocumentClient;