document_client.js 18 KB

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