API Docs for: 2.2.2
Show:

File: lib/commands/kv/riakobject.js

  1. 'use strict';
  2.  
  3. var utils = require('../../utils');
  4. var rpb = require('../../protobuf/riakprotobuf');
  5. var RpbContent = rpb.getProtoFor('RpbContent');
  6. var RpbPair = rpb.getProtoFor('RpbPair');
  7. var RpbLink = rpb.getProtoFor('RpbLink');
  8.  
  9. /**
  10. * Provides the RiakObject class.
  11. * @module KV
  12. */
  13.  
  14. /**
  15. * A class that encapsulates the metadata and value stored in Riak.
  16. *
  17. * While you can fetch and store regular JS objects with {{#crossLink "FetchValue"}}{{/crossLink}}
  18. * and {{#crossLink "StoreValue"}}{{/crossLink}}, if you want access to the associated
  19. * metadata stored in Riak with the value you'll want to use a RiakObject instead.
  20. *
  21. * @class RiakObject
  22. * @constructor
  23. */
  24. function RiakObject() {
  25. // default content type
  26. this.contentType = 'application/json';
  27. }
  28.  
  29. RiakObject.prototype = {
  30. /**
  31. * Set the key.
  32. * @method setKey
  33. * @param {String} key the key in Riak.
  34. * @chainable
  35. */
  36. setKey : function(key) {
  37. this.key = key;
  38. return this;
  39. },
  40. /**
  41. * Get the key.
  42. * @method getKey
  43. * @return {String} the key.
  44. */
  45. getKey : function() {
  46. return this.key;
  47. },
  48. /**
  49. * Set the bucket.
  50. * @method setBucket
  51. * @param {String} bucket the bucket in Riak.
  52. * @chainable
  53. */
  54. setBucket : function(bucket) {
  55. this.bucket = bucket;
  56. return this;
  57. },
  58. /**
  59. * Get the bucket.
  60. * @method getBucket
  61. * @return {String} the bucket.
  62. */
  63. getBucket : function() {
  64. return this.bucket;
  65. },
  66. /**
  67. * Set the bucket type.
  68. *
  69. * If this is not set 'default' is used.
  70. * @method setBucketType
  71. * @param {String} bucketType the bucket type in Riak.
  72. * @chainable
  73. */
  74. setBucketType : function(bucketType) {
  75. this.bucketType = bucketType;
  76. return this;
  77. },
  78. /**
  79. * Get the bucket type.
  80. * @method getBucketType
  81. * @return {String} the bucket type.
  82. */
  83. getBucketType : function() {
  84. return this.bucketType;
  85. },
  86. /**
  87. * Set the value.
  88. * @method setValue
  89. * @param {String|Buffer|Object} value the value stored in Riak.
  90. * @chainable
  91. */
  92. setValue : function(value) {
  93. this.value = value;
  94. return this;
  95. },
  96. /**
  97. * Get the value.
  98. *
  99. * This will either be a Buffer or a plain JS object.
  100. * @method getValue
  101. * @return {Buffer|Object} The value returned from Riak.
  102. */
  103. getValue : function() {
  104. return this.value;
  105. },
  106. /**
  107. * Set the content type.
  108. *
  109. * Due to Riak's HTTP API this is represented as a string suitable for
  110. * a HTTP Content-Type header.
  111. *
  112. * If not set, the default is 'application/json'
  113. * @method setContentType
  114. * @param {String} contentType the content type.
  115. * @chainable
  116. */
  117. setContentType : function(contentType) {
  118. this.contentType = contentType;
  119. return this;
  120. },
  121. /**
  122. * Get the content type.
  123. *
  124. * Due to Riak's HTTP API this is represented as a string suitable for
  125. * a HTTP Content-Type header.
  126. * @method getContentType
  127. * @return {String} the content type.
  128. */
  129. getContentType : function() {
  130. return this.contentType;
  131. },
  132. /**
  133. * Set the content encoding.
  134. *
  135. * @method setContentEncoding
  136. * @param {String} contentEncoding the content encoding
  137. * @chainable
  138. */
  139. setContentEncoding : function(contentEncoding) {
  140. this.contentEncoding = contentEncoding;
  141. return this;
  142. },
  143. /**
  144. * Get the content encoding
  145. *
  146. * @method getContentEncoding
  147. * @returns {String} the content encoding
  148. */
  149. getContentEncoding : function() {
  150. return this.contentEncoding;
  151. },
  152. /**
  153. * Set the user meta data.
  154. *
  155. * This is an array of key/value objects.
  156. * @method setUserMeta
  157. * @param {Object[]} userMeta
  158. * @param {String} userMeta.key usermeta key
  159. * @param {String} userMeta.value usermeat value
  160. * @chainable
  161. */
  162. setUserMeta : function(userMeta) {
  163. this.userMeta = userMeta;
  164. return this;
  165. },
  166. /**
  167. * Determine if any user metadata is present.
  168. * @method hasUserMeta
  169. * @return {Boolean} true if user meta data is present.
  170. */
  171. hasUserMeta : function() {
  172. return this.userMeta !== undefined;
  173. },
  174. /**
  175. * Get the user meta data
  176. *
  177. * This is an array of key/value objects
  178. * @method getUserMeta
  179. * @return {Object[]} array of key/value objects
  180. */
  181. getUserMeta : function() {
  182. return this.userMeta;
  183. },
  184. /**
  185. * Set an index, replacing the existing value if any.
  186. * @method setIndex
  187. * @param {String} indexName the index name
  188. * @param {String[]|Number[]} arrayOfKeys - the keys
  189. * @chainable
  190. */
  191. setIndex : function(indexName, arrayOfKeys) {
  192. if (this.indexes === undefined) {
  193. this.indexes = {};
  194. }
  195. this.indexes[indexName] = arrayOfKeys;
  196. return this;
  197. },
  198. /**
  199. * Determine if any indexes are present.
  200. * @method hasIndexes
  201. * @return {Boolean} true if indexes are present.
  202. */
  203. hasIndexes : function() {
  204. return this.indexes !== undefined;
  205. },
  206. /**
  207. * Get all indexes.
  208. *
  209. * @method getIndexes
  210. * @return {Object} an object whose fields are the index names holding arrays of keys
  211. */
  212. getIndexes : function() {
  213. return this.indexes;
  214. },
  215. /**
  216. * Get the keys for an index.
  217. * @method getIndex
  218. * @param {String} indexName the name of the index
  219. * @return {String[]} the keys
  220. */
  221. getIndex : function(indexName) {
  222. if (this.indexes !== undefined) {
  223. return this.indexes[indexName];
  224. } else {
  225. return undefined;
  226. }
  227. },
  228. /**
  229. * Add one or more keys to an index.
  230. *
  231. * If the index does not exist it will be created.
  232. * @method addToIndex
  233. * @param {String} indexName the index name
  234. * @param {String|Number} ...key 1 or more keys to add
  235. * @chainable
  236. */
  237. addToIndex : function(indexName, key) {
  238. if (this.indexes === undefined) {
  239. this.indexes = {};
  240. }
  241. if (!this.indexes.hasOwnProperty(indexName)) {
  242. this.indexes[indexName] = [];
  243. }
  244. for (var i = 1; i < arguments.length; i++) {
  245. this.indexes[indexName].push(arguments[i]);
  246. }
  247. return this;
  248. },
  249. /**
  250. * Determine if any links are present.
  251. *
  252. * Note that link walking is a deprecated feature in Riak 2.0.
  253. *
  254. * See: [Link Walking](http://docs.basho.com/riak/latest/dev/using/link-walking/)
  255. * @method hasLinks
  256. * @return {Boolean} true if there are any links.
  257. * @deprecated Link walking is a deprecated feature in Riak 2.0.
  258. */
  259. hasLinks : function() {
  260. return this.links !== undefined;
  261. },
  262. /**
  263. * Get the links.
  264. *
  265. * This is an array of objects representing links to other objects in Riak.
  266. *
  267. * Note that link walking is a deprecated feature in Riak 2.0.
  268. *
  269. * See: [Link Walking](http://docs.basho.com/riak/latest/dev/using/link-walking/)
  270. * @method getLinks
  271. * @return {Object[]} An array containing the links, or undefined if none exist.
  272. * @deprecated Link walking is a deprecated feature in Riak 2.0.
  273. */
  274. getLinks : function() {
  275. return this.links;
  276. },
  277. /**
  278. * Set the links.
  279. *
  280. * This is an array of objects representing links to other objects in Riak.
  281. *
  282. * Note that link walking is a deprecated feature in Riak 2.0.
  283. *
  284. * See: [Link Walking](http://docs.basho.com/riak/latest/dev/using/link-walking/)
  285. * @method setLinks
  286. * @param {Object[]} links An array of objects representing the links.
  287. * @param {String} links.bucket The bucket the linked object is in.
  288. * @param {String} links.key The key for the linked object.
  289. * @param {String} links.tag The identifier that describes the relationship you are wishing to capture with your link
  290. * @chainable
  291. * @deprecated Link walking is a deprecated feature in Riak 2.0.
  292. */
  293. setLinks : function(links) {
  294. this.links = links;
  295. return this;
  296. },
  297. /**
  298. * Set the vector clock.
  299. * @method setVClock
  300. * @param {Buffer} vclock the vclock retrieved from Riak
  301. * @chainable
  302. */
  303. setVClock : function(vclock) {
  304. this.vclock = vclock;
  305. return this;
  306. },
  307. /**
  308. * Get the vector clock.
  309. * @method getVClock
  310. * @return {Buffer} The vector clock retrieved from Riak.
  311. */
  312. getVClock : function() {
  313. return this.vclock;
  314. },
  315. /**
  316. * Returns whether or not this RiakObject is marked as being deleted (a tombstone)
  317. * @method getIsTombstone
  318. * @return {Boolean} true if this is a tombstone.
  319. */
  320. getIsTombstone : function() {
  321. return this.isTombstone;
  322. },
  323. /**
  324. * Returns the last modified time of this RiakObject.
  325. *
  326. * The timestamp is returned as a (Unix) epoch time.
  327. * @method getLastModified
  328. * @return {Number} the last modified time.
  329. */
  330. getLastModified : function() {
  331. return this.lastModified;
  332. }
  333. };
  334.  
  335. module.exports = RiakObject;
  336.  
  337. module.exports.isRiakObject = function (v) {
  338. return v instanceof RiakObject;
  339. };
  340.  
  341. module.exports.isIntIndex = function(indexName) {
  342. return indexName.indexOf('_int', indexName.length - 4) !== -1;
  343. };
  344.  
  345. module.exports.createFromRpbContent = function(rpbContent, convertToJs) {
  346. var ro = new RiakObject();
  347. var value = rpbContent.getValue();
  348. ro.isTombstone = rpbContent.getDeleted() ? true : false;
  349.  
  350. if (ro.isTombstone) {
  351. ro.value = {};
  352. } else {
  353. // ReturnHead will only retun metadata
  354. if (convertToJs && value) {
  355. ro.value = JSON.parse(value.toString('utf8'));
  356. } else if (value) {
  357. ro.value = value.toBuffer();
  358. }
  359. }
  360. if (rpbContent.getContentType()) {
  361. ro.contentType = rpbContent.getContentType().toString('utf8');
  362. }
  363.  
  364. if (rpbContent.getContentEncoding()) {
  365. ro.contentEncoding = rpbContent.getContentEncoding().toString('utf8');
  366. }
  367. if (rpbContent.getLastMod()) {
  368. var lm = rpbContent.getLastMod();
  369. var lmu = rpbContent.getLastModUsecs();
  370. ro.lastModified = ((lm * 1000) + (lmu / 1000));
  371. }
  372. // UserMeta
  373. var pbUsermeta = rpbContent.getUsermeta();
  374. var usermeta = new Array(pbUsermeta.length);
  375. var i;
  376. for (i = 0; i < pbUsermeta.length; i++) {
  377. usermeta[i] = { key: pbUsermeta[i].key.toString('utf8'),
  378. value: pbUsermeta[i].value.toString('utf8') };
  379. }
  380. ro.userMeta = usermeta;
  381. //indexes
  382. var pbIndexes = rpbContent.getIndexes();
  383. if (pbIndexes.length > 0) {
  384. var indexes = {};
  385. for (i = 0; i < pbIndexes.length; i++) {
  386. var indexName = pbIndexes[i].key.toString('utf8');
  387. if (!indexes.hasOwnProperty(indexName)) {
  388. indexes[indexName] = [];
  389. }
  390. var stringValue = pbIndexes[i].value.toString('utf8');
  391. if (RiakObject.isIntIndex(indexName)) {
  392. indexes[indexName].push(parseInt(stringValue));
  393. } else {
  394. indexes[indexName].push(stringValue);
  395. }
  396. }
  397. ro.indexes = indexes;
  398. }
  399. //links
  400. var pbLinks = rpbContent.getLinks();
  401. if (pbLinks.length) {
  402. var links = new Array(pbLinks.length);
  403. var link;
  404. for (i = 0; i < pbLinks.length; i++) {
  405. link = {};
  406. if (pbLinks[i].bucket) {
  407. link.bucket = pbLinks[i].bucket.toString('utf8');
  408. }
  409. if (pbLinks[i].key) {
  410. link.key = pbLinks[i].key.toString('utf8');
  411. }
  412. if (pbLinks[i].tag) {
  413. link.tag = pbLinks[i].tag.toString('utf8');
  414. }
  415. links[i] = link;
  416. }
  417. ro.links = links;
  418. }
  419. return ro;
  420. };
  421.  
  422. /**
  423. * Creates and returns a RpbContent protobuf from a value and meta.
  424. *
  425. * If the value is a JS Object it is converted using JSON.stringify().
  426. * @private
  427. * @method populateRpbContentFromRiakObject
  428. * @static
  429. * @param {RiakObject} ro The RiakObject
  430. * @return {Object} a RpbContent protobuf
  431. */
  432. module.exports.populateRpbContentFromRiakObject = function(ro) {
  433. var rpbContent = new RpbContent();
  434. if (utils.isString(ro.value)) {
  435. rpbContent.setValue(new Buffer(ro.value));
  436. } else if (Buffer.isBuffer(ro.value)) {
  437. rpbContent.setValue(ro.value);
  438. } else {
  439. rpbContent.setValue(new Buffer(JSON.stringify(ro.value)));
  440. }
  441. if (ro.getContentType()) {
  442. rpbContent.setContentType(new Buffer(ro.getContentType()));
  443. }
  444.  
  445. if (ro.getContentEncoding()) {
  446. rpbContent.setContentEncoding(new Buffer(ro.getContentEncoding()));
  447. }
  448.  
  449. var i, pair;
  450. if (ro.hasIndexes()) {
  451. var allIndexes = ro.getIndexes();
  452. for (var indexName in allIndexes) {
  453. var indexKeys = allIndexes[indexName];
  454. for (i = 0; i < indexKeys.length; i++) {
  455. pair = new RpbPair();
  456. pair.setKey(new Buffer(indexName));
  457. // The Riak API expects string values, even for _int indexes
  458. pair.setValue(new Buffer(indexKeys[i].toString()));
  459. rpbContent.indexes.push(pair);
  460. }
  461. }
  462. }
  463.  
  464. if (ro.hasUserMeta()) {
  465. var userMeta = ro.getUserMeta();
  466. for (i = 0; i < userMeta.length; i++) {
  467. pair = new RpbPair();
  468. pair.setKey(new Buffer(userMeta[i].key));
  469. pair.setValue(new Buffer(userMeta[i].value));
  470. rpbContent.usermeta.push(pair);
  471. }
  472. }
  473. if (ro.hasLinks()) {
  474. var links = ro.getLinks();
  475. var pbLink;
  476. for (i = 0; i < links.length; i++) {
  477. pbLink = new RpbLink();
  478. if (links[i].bucket) {
  479. pbLink.setBucket(new Buffer(links[i].bucket));
  480. }
  481. if (links[i].key) {
  482. pbLink.setKey(new Buffer(links[i].key));
  483. }
  484. if (links[i].tag) {
  485. pbLink.setTag(new Buffer(links[i].tag));
  486. }
  487. rpbContent.links.push(pbLink);
  488. }
  489. }
  490. return rpbContent;
  491. };
  492.  
  493.