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 | * 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 | */
|
578 | module.exports = AWS.DynamoDB.DocumentClient;
|