UNPKG

18.3 kBJavaScriptView Raw
1var AWS = require('../core');
2var Translator = require('./translator');
3var 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 */
41AWS.DynamoDB.DocumentClient = AWS.util.inherit({
42
43 /**
44 * Creates a DynamoDB document client with a set of configuration options.
45 *
46 * @option options params [map] An optional map of parameters to bind to every
47 * request sent by this service object.
48 * @option options service [AWS.DynamoDB] An optional pre-configured instance
49 * of the AWS.DynamoDB service object to use for requests. The object may
50 * bound parameters used by the document client.
51 * @option options convertEmptyValues [Boolean] set to true if you would like
52 * the document client to convert empty values (0-length strings, binary
53 * buffers, and sets) to be converted to NULL types when persisting to
54 * DynamoDB.
55 * @see AWS.DynamoDB.constructor
56 *
57 */
58 constructor: function DocumentClient(options) {
59 var self = this;
60 self.options = options || {};
61 self.configure(self.options);
62 },
63
64 /**
65 * @api private
66 */
67 configure: function configure(options) {
68 var self = this;
69 self.service = options.service;
70 self.bindServiceObject(options);
71 self.attrValue = options.attrValue =
72 self.service.api.operations.putItem.input.members.Item.value.shape;
73 },
74
75 /**
76 * @api private
77 */
78 bindServiceObject: function bindServiceObject(options) {
79 var self = this;
80 options = options || {};
81
82 if (!self.service) {
83 self.service = new AWS.DynamoDB(options);
84 } else {
85 var config = AWS.util.copy(self.service.config);
86 self.service = new self.service.constructor.__super__(config);
87 self.service.config.params =
88 AWS.util.merge(self.service.config.params || {}, options.params);
89 }
90 },
91
92 /**
93 * @api private
94 */
95 makeServiceRequest: function(operation, params, callback) {
96 var self = this;
97 var request = self.service[operation](params);
98 self.setupRequest(request);
99 self.setupResponse(request);
100 if (typeof callback === 'function') {
101 request.send(callback);
102 }
103 return request;
104 },
105
106 /**
107 * @api private
108 */
109 serviceClientOperationsMap: {
110 batchGet: 'batchGetItem',
111 batchWrite: 'batchWriteItem',
112 delete: 'deleteItem',
113 get: 'getItem',
114 put: 'putItem',
115 query: 'query',
116 scan: 'scan',
117 update: 'updateItem',
118 transactGet: 'transactGetItems',
119 transactWrite: 'transactWriteItems'
120 },
121
122 /**
123 * Returns the attributes of one or more items from one or more tables
124 * by delegating to `AWS.DynamoDB.batchGetItem()`.
125 *
126 * Supply the same parameters as {AWS.DynamoDB.batchGetItem} with
127 * `AttributeValue`s substituted by native JavaScript types.
128 *
129 * @see AWS.DynamoDB.batchGetItem
130 * @example Get items from multiple tables
131 * var params = {
132 * RequestItems: {
133 * 'Table-1': {
134 * Keys: [
135 * {
136 * HashKey: 'haskey',
137 * NumberRangeKey: 1
138 * }
139 * ]
140 * },
141 * 'Table-2': {
142 * Keys: [
143 * { foo: 'bar' },
144 * ]
145 * }
146 * }
147 * };
148 *
149 * var documentClient = new AWS.DynamoDB.DocumentClient();
150 *
151 * documentClient.batchGet(params, function(err, data) {
152 * if (err) console.log(err);
153 * else console.log(data);
154 * });
155 *
156 */
157 batchGet: function(params, callback) {
158 var operation = this.serviceClientOperationsMap['batchGet'];
159 return this.makeServiceRequest(operation, params, callback);
160 },
161
162 /**
163 * Puts or deletes multiple items in one or more tables by delegating
164 * to `AWS.DynamoDB.batchWriteItem()`.
165 *
166 * Supply the same parameters as {AWS.DynamoDB.batchWriteItem} with
167 * `AttributeValue`s substituted by native JavaScript types.
168 *
169 * @see AWS.DynamoDB.batchWriteItem
170 * @example Write to and delete from a table
171 * var params = {
172 * RequestItems: {
173 * 'Table-1': [
174 * {
175 * DeleteRequest: {
176 * Key: { HashKey: 'someKey' }
177 * }
178 * },
179 * {
180 * PutRequest: {
181 * Item: {
182 * HashKey: 'anotherKey',
183 * NumAttribute: 1,
184 * BoolAttribute: true,
185 * ListAttribute: [1, 'two', false],
186 * MapAttribute: { foo: 'bar' }
187 * }
188 * }
189 * }
190 * ]
191 * }
192 * };
193 *
194 * var documentClient = new AWS.DynamoDB.DocumentClient();
195 *
196 * documentClient.batchWrite(params, function(err, data) {
197 * if (err) console.log(err);
198 * else console.log(data);
199 * });
200 *
201 */
202 batchWrite: function(params, callback) {
203 var operation = this.serviceClientOperationsMap['batchWrite'];
204 return this.makeServiceRequest(operation, params, callback);
205 },
206
207 /**
208 * Deletes a single item in a table by primary key by delegating to
209 * `AWS.DynamoDB.deleteItem()`
210 *
211 * Supply the same parameters as {AWS.DynamoDB.deleteItem} with
212 * `AttributeValue`s substituted by native JavaScript types.
213 *
214 * @see AWS.DynamoDB.deleteItem
215 * @example Delete an item from a table
216 * var params = {
217 * TableName : 'Table',
218 * Key: {
219 * HashKey: 'hashkey',
220 * NumberRangeKey: 1
221 * }
222 * };
223 *
224 * var documentClient = new AWS.DynamoDB.DocumentClient();
225 *
226 * documentClient.delete(params, function(err, data) {
227 * if (err) console.log(err);
228 * else console.log(data);
229 * });
230 *
231 */
232 delete: function(params, callback) {
233 var operation = this.serviceClientOperationsMap['delete'];
234 return this.makeServiceRequest(operation, params, callback);
235 },
236
237 /**
238 * Returns a set of attributes for the item with the given primary key
239 * by delegating to `AWS.DynamoDB.getItem()`.
240 *
241 * Supply the same parameters as {AWS.DynamoDB.getItem} with
242 * `AttributeValue`s substituted by native JavaScript types.
243 *
244 * @see AWS.DynamoDB.getItem
245 * @example Get an item from a table
246 * var params = {
247 * TableName : 'Table',
248 * Key: {
249 * HashKey: 'hashkey'
250 * }
251 * };
252 *
253 * var documentClient = new AWS.DynamoDB.DocumentClient();
254 *
255 * documentClient.get(params, function(err, data) {
256 * if (err) console.log(err);
257 * else console.log(data);
258 * });
259 *
260 */
261 get: function(params, callback) {
262 var operation = this.serviceClientOperationsMap['get'];
263 return this.makeServiceRequest(operation, params, callback);
264 },
265
266 /**
267 * Creates a new item, or replaces an old item with a new item by
268 * delegating to `AWS.DynamoDB.putItem()`.
269 *
270 * Supply the same parameters as {AWS.DynamoDB.putItem} with
271 * `AttributeValue`s substituted by native JavaScript types.
272 *
273 * @see AWS.DynamoDB.putItem
274 * @example Create a new item in a table
275 * var params = {
276 * TableName : 'Table',
277 * Item: {
278 * HashKey: 'haskey',
279 * NumAttribute: 1,
280 * BoolAttribute: true,
281 * ListAttribute: [1, 'two', false],
282 * MapAttribute: { foo: 'bar'},
283 * NullAttribute: null
284 * }
285 * };
286 *
287 * var documentClient = new AWS.DynamoDB.DocumentClient();
288 *
289 * documentClient.put(params, function(err, data) {
290 * if (err) console.log(err);
291 * else console.log(data);
292 * });
293 *
294 */
295 put: function(params, callback) {
296 var operation = this.serviceClientOperationsMap['put'];
297 return this.makeServiceRequest(operation, params, callback);
298 },
299
300 /**
301 * Edits an existing item's attributes, or adds a new item to the table if
302 * it does not already exist by delegating to `AWS.DynamoDB.updateItem()`.
303 *
304 * Supply the same parameters as {AWS.DynamoDB.updateItem} with
305 * `AttributeValue`s substituted by native JavaScript types.
306 *
307 * @see AWS.DynamoDB.updateItem
308 * @example Update an item with expressions
309 * var params = {
310 * TableName: 'Table',
311 * Key: { HashKey : 'hashkey' },
312 * UpdateExpression: 'set #a = :x + :y',
313 * ConditionExpression: '#a < :MAX',
314 * ExpressionAttributeNames: {'#a' : 'Sum'},
315 * ExpressionAttributeValues: {
316 * ':x' : 20,
317 * ':y' : 45,
318 * ':MAX' : 100,
319 * }
320 * };
321 *
322 * var documentClient = new AWS.DynamoDB.DocumentClient();
323 *
324 * documentClient.update(params, function(err, data) {
325 * if (err) console.log(err);
326 * else console.log(data);
327 * });
328 *
329 */
330 update: function(params, callback) {
331 var operation = this.serviceClientOperationsMap['update'];
332 return this.makeServiceRequest(operation, params, callback);
333 },
334
335 /**
336 * Returns one or more items and item attributes by accessing every item
337 * in a table or a secondary index.
338 *
339 * Supply the same parameters as {AWS.DynamoDB.scan} with
340 * `AttributeValue`s substituted by native JavaScript types.
341 *
342 * @see AWS.DynamoDB.scan
343 * @example Scan the table with a filter expression
344 * var params = {
345 * TableName : 'Table',
346 * FilterExpression : 'Year = :this_year',
347 * ExpressionAttributeValues : {':this_year' : 2015}
348 * };
349 *
350 * var documentClient = new AWS.DynamoDB.DocumentClient();
351 *
352 * documentClient.scan(params, function(err, data) {
353 * if (err) console.log(err);
354 * else console.log(data);
355 * });
356 *
357 */
358 scan: function(params, callback) {
359 var operation = this.serviceClientOperationsMap['scan'];
360 return this.makeServiceRequest(operation, params, callback);
361 },
362
363 /**
364 * Directly access items from a table by primary key or a secondary index.
365 *
366 * Supply the same parameters as {AWS.DynamoDB.query} with
367 * `AttributeValue`s substituted by native JavaScript types.
368 *
369 * @see AWS.DynamoDB.query
370 * @example Query an index
371 * var params = {
372 * TableName: 'Table',
373 * IndexName: 'Index',
374 * KeyConditionExpression: 'HashKey = :hkey and RangeKey > :rkey',
375 * ExpressionAttributeValues: {
376 * ':hkey': 'key',
377 * ':rkey': 2015
378 * }
379 * };
380 *
381 * var documentClient = new AWS.DynamoDB.DocumentClient();
382 *
383 * documentClient.query(params, function(err, data) {
384 * if (err) console.log(err);
385 * else console.log(data);
386 * });
387 *
388 */
389 query: function(params, callback) {
390 var operation = this.serviceClientOperationsMap['query'];
391 return this.makeServiceRequest(operation, params, callback);
392 },
393
394 /**
395 * Synchronous write operation that groups up to 10 action requests
396 *
397 * Supply the same parameters as {AWS.DynamoDB.transactWriteItems} with
398 * `AttributeValue`s substituted by native JavaScript types.
399 *
400 * @see AWS.DynamoDB.transactWriteItems
401 * @example Get items from multiple tables
402 * var params = {
403 * TransactItems: [{
404 * Put: {
405 * TableName : 'Table0',
406 * Item: {
407 * HashKey: 'haskey',
408 * NumAttribute: 1,
409 * BoolAttribute: true,
410 * ListAttribute: [1, 'two', false],
411 * MapAttribute: { foo: 'bar'},
412 * NullAttribute: null
413 * }
414 * }
415 * }, {
416 * Update: {
417 * TableName: 'Table1',
418 * Key: { HashKey : 'hashkey' },
419 * UpdateExpression: 'set #a = :x + :y',
420 * ConditionExpression: '#a < :MAX',
421 * ExpressionAttributeNames: {'#a' : 'Sum'},
422 * ExpressionAttributeValues: {
423 * ':x' : 20,
424 * ':y' : 45,
425 * ':MAX' : 100,
426 * }
427 * }
428 * }]
429 * };
430 *
431 * documentClient.transactWrite(params, function(err, data) {
432 * if (err) console.log(err);
433 * else console.log(data);
434 * });
435 */
436 transactWrite: function(params, callback) {
437 var operation = this.serviceClientOperationsMap['transactWrite'];
438 return this.makeServiceRequest(operation, params, callback);
439 },
440
441 /**
442 * Atomically retrieves multiple items from one or more tables (but not from indexes)
443 * in a single account and region.
444 *
445 * Supply the same parameters as {AWS.DynamoDB.transactGetItems} with
446 * `AttributeValue`s substituted by native JavaScript types.
447 *
448 * @see AWS.DynamoDB.transactGetItems
449 * @example Get items from multiple tables
450 * var params = {
451 * TransactItems: [{
452 * Get: {
453 * TableName : 'Table0',
454 * Key: {
455 * HashKey: 'hashkey0'
456 * }
457 * }
458 * }, {
459 * Get: {
460 * TableName : 'Table1',
461 * Key: {
462 * HashKey: 'hashkey1'
463 * }
464 * }
465 * }]
466 * };
467 *
468 * documentClient.transactGet(params, function(err, data) {
469 * if (err) console.log(err);
470 * else console.log(data);
471 * });
472 */
473 transactGet: function(params, callback) {
474 var operation = this.serviceClientOperationsMap['transactGet'];
475 return this.makeServiceRequest(operation, params, callback);
476 },
477
478 /**
479 * Creates a set of elements inferring the type of set from
480 * the type of the first element. Amazon DynamoDB currently supports
481 * the number sets, string sets, and binary sets. For more information
482 * about DynamoDB data types see the documentation on the
483 * [Amazon DynamoDB Data Model](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DataModel.html#DataModel.DataTypes).
484 *
485 * @param list [Array] Collection to represent your DynamoDB Set
486 * @param options [map]
487 * * **validate** [Boolean] set to true if you want to validate the type
488 * of each element in the set. Defaults to `false`.
489 * @example Creating a number set
490 * var documentClient = new AWS.DynamoDB.DocumentClient();
491 *
492 * var params = {
493 * Item: {
494 * hashkey: 'hashkey'
495 * numbers: documentClient.createSet([1, 2, 3]);
496 * }
497 * };
498 *
499 * documentClient.put(params, function(err, data) {
500 * if (err) console.log(err);
501 * else console.log(data);
502 * });
503 *
504 */
505 createSet: function(list, options) {
506 options = options || {};
507 return new DynamoDBSet(list, options);
508 },
509
510 /**
511 * @api private
512 */
513 getTranslator: function() {
514 return new Translator(this.options);
515 },
516
517 /**
518 * @api private
519 */
520 setupRequest: function setupRequest(request) {
521 var self = this;
522 var translator = self.getTranslator();
523 var operation = request.operation;
524 var inputShape = request.service.api.operations[operation].input;
525 request._events.validate.unshift(function(req) {
526 req.rawParams = AWS.util.copy(req.params);
527 req.params = translator.translateInput(req.rawParams, inputShape);
528 });
529 },
530
531 /**
532 * @api private
533 */
534 setupResponse: function setupResponse(request) {
535 var self = this;
536 var translator = self.getTranslator();
537 var outputShape = self.service.api.operations[request.operation].output;
538 request.on('extractData', function(response) {
539 response.data = translator.translateOutput(response.data, outputShape);
540 });
541
542 var response = request.response;
543 response.nextPage = function(cb) {
544 var resp = this;
545 var req = resp.request;
546 var config;
547 var service = req.service;
548 var operation = req.operation;
549 try {
550 config = service.paginationConfig(operation, true);
551 } catch (e) { resp.error = e; }
552
553 if (!resp.hasNextPage()) {
554 if (cb) cb(resp.error, null);
555 else if (resp.error) throw resp.error;
556 return null;
557 }
558
559 var params = AWS.util.copy(req.rawParams);
560 if (!resp.nextPageTokens) {
561 return cb ? cb(null, null) : null;
562 } else {
563 var inputTokens = config.inputToken;
564 if (typeof inputTokens === 'string') inputTokens = [inputTokens];
565 for (var i = 0; i < inputTokens.length; i++) {
566 params[inputTokens[i]] = resp.nextPageTokens[i];
567 }
568 return self[operation](params, cb);
569 }
570 };
571 }
572
573});
574
575/**
576 * @api private
577 */
578module.exports = AWS.DynamoDB.DocumentClient;