diff --git a/README.md b/README.md index 3dc41ad9836..8c5254a6b27 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ If you are not running this client on Google Compute Engine, you need a Google D * Create a new project or click on an existing project. * Enable billing if you haven't already. * On the "APIs & auth" tab, click APIs section and turn on the following. You may need to enable billing in order to use these services. - * Google Cloud Datastore API - * Google Cloud Storage - * Google Cloud Storage JSON API + * Google Cloud Datastore API + * Google Cloud Storage + * Google Cloud Storage JSON API * Google Cloud Pub/Sub * Once API access is enabled, switch back to "APIs & auth" section on the navigation panel and switch to "Credentials" page. * Click on "Create new client ID" to create a new **service account**. Once the account is created, click on "Generate new JSON key" to download @@ -39,18 +39,18 @@ your private key. The downloaded file contains credentials you'll need for authorization. * You'll the following for auth configuration: - * Developers Console project's ID (e.g. bamboo-shift-455) - * The path to the JSON key file. + * Developers Console project's ID (e.g. bamboo-shift-455) + * The path to the JSON key file. ## Developer's Guide * [Google Cloud Datastore](#google-cloud-datastore) - * [Configuration](#configuration) - * [Entities and Keys](#entities-and-keys) - * [Getting, Saving and Deleting Entities](#getting-saving-and-deleting-entities) - * [Querying](#querying) - * [Allocating IDs](#allocating-ids-id-generation) - * [Transactions](#transactions) + * [Configuration](#configuration) + * [Entities and Keys](#entities-and-keys) + * [Getting, Saving and Deleting Entities](#getting-saving-and-deleting-entities) + * [Querying](#querying) + * [Allocating IDs](#allocating-ids-id-generation) + * [Transactions](#transactions) * [Google Cloud Storage](#google-cloud-storage) * [Configuration](#configuration-1) * [Listing Files](#listing-files) @@ -83,8 +83,8 @@ Elsewhere, initiate with project ID and private key downloaded from Developer's ~~~~ js var gcloud = require('gcloud'), ds = new gcloud.datastore.Dataset({ - projectId: YOUR_PROJECT_ID, - keyFilename: '/path/to/the/key.json' + projectId: YOUR_PROJECT_ID, + keyFilename: '/path/to/the/key.json' }); ~~~~ @@ -97,13 +97,13 @@ TODO Get operations require a valid key to retrieve the key identified entity from Datastore. Skip to the "Querying" section if you'd like to learn more about querying against Datastore. ~~~~ js -ds.get(['Company', 123], function(err, entities) { +ds.get(['Company', 123], function(err, entity) {}); -}); // alternatively, you can retrieve multiple entities at once. -ds.getAll([key1, key2, ...], function(err, entities) { - -}); +ds.get([ + ['Company', 123], + ['Product', 'Computer'] +], function(err, entities) {}); ~~~~ You can insert arbitrary objects by providing an incomplete key during saving. If the key is not incomplete, the existing entity is updated or inserted with the provided key. @@ -111,31 +111,32 @@ You can insert arbitrary objects by providing an incomplete key during saving. I To learn more about keys and incomplete keys, skip to the Keys section. ~~~~ js -ds.save(['Company', null], obj, function(err, key) { - // First arg is an incomplete key for Company kind. - // console.log(key) will output ['Company', 599900452312]. +ds.save({ key: ['Company', null], data: {/*...*/} }, function(err, key) { + // First arg is an incomplete key for Company kind. + // console.log(key) will output ['Company', 599900452312]. }); // alternatively, you can save multiple entities at once. -ds.saveAll([key1, key2, key3], [obj1, obj2, obj3], function(err, keys) { - // if key1 was incomplete, keys[0] will return the generated key. +ds.save([ + { key: ['Company', 123], data: {/*...*/} }, + { key: ['Product', 'Computer'], data: {/*...*/} } +], function(err, keys) { + // if the first key was incomplete, keys[0] will return the generated key. }); ~~~~ Deletion requires the key of the entity to be deleted. ~~~~ js -ds.del(['Company', 599900452312], function(err) { +ds.delete(['Company', 599900452312], function(err) {}); -}); // alternatively, you can delete multiple entities of different // kinds at once. -ds.delAll([ - ['Company', 599900452312], - ['Company', 599900452315], +ds.delete([ + ['Company', 599900452312], + ['Company', 599900452315], ['Office', 'mtv'], - ['Company', 123, 'Employee', 'jbd']], function(err) { - -}); + ['Company', 123, 'Employee', 'jbd'] +], function(err) {}); ~~~~ #### Querying diff --git a/lib/datastore/index.js b/lib/datastore/index.js index de7d4889a04..c64c3099cf7 100644 --- a/lib/datastore/index.js +++ b/lib/datastore/index.js @@ -93,33 +93,18 @@ Transaction.prototype.finalize = function(callback) { }; /** - * Get retrieves the objects identified with the specified key in the + * Get retrieves the objects identified with the specified key(s) in the * current transaction. - * @param {Array} key + * @param {Array} keys * @param {Function} callback */ -Transaction.prototype.get = function(key, callback) { - this.getAll([key], function(err, results) { - if (err) { - return callback(err); - } - return callback(null, results[0]); - }); -}; - -/** - * Gets all objects identified with the specified list of keys - * in the current transaction. - * @param {Array} keys - * @param {Function} callback - */ -Transaction.prototype.getAll = function(keys, callback) { +Transaction.prototype.get = function(keys, callback) { + var isMultipleRequest = Array.isArray(keys[0]); + keys = isMultipleRequest ? keys : [keys]; callback = callback || util.noop; - keysProto = []; - keys.forEach(function(k) { - keysProto.push(entity.keyToKeyProto(this.id, k)); - }); - var req = { keys: keysProto }; + var req = { + keys: keys.map(entity.keyToKeyProto.bind(null, this.id)) + }; if (this.id) { req.transaction = this.id; } @@ -128,113 +113,76 @@ Transaction.prototype.getAll = function(keys, callback) { if (err) { return callback(err); } - - callback(null, entity.formatArray(resp.found)); + var response = entity.formatArray(resp.found); + callback(null, isMultipleRequest ? response : response[0]); }); }; /** - * Inserts or updates the specified the object in the current - * transaction. If the provided key is incomplete, inserts the object - * and returns the generated identifier. - * @param {Array} key - * @param {Object} obj - * @param {Function} callback - */ -Transaction.prototype.save = function(key, obj, callback) { - this.saveAll([key], [obj], function(err, keys) { - if (err || !keys) { - return callback(err); - } - if (keys[0]) { - return callback(err, keys[0]); - } - callback(); - }); -}; - -/** - * Inserts or upates the specified objects in the current transaction. + * Inserts or upates the specified object(s) in the current transaction. * If a key is incomplete, its associated object is inserted and * generated identifier is returned. - * @param {Array} keys - * @param {Array} objs + * @param {Array} entities * @param {Function} callback */ -Transaction.prototype.saveAll = function(keys, objs, callback) { - if (keys.length != objs.length) { - throw new Error('The length of the keys don\'t match the length of the objects'); - } +Transaction.prototype.save = function(entities, callback) { + var isMultipleRequest = Array.isArray(entities); + entities = isMultipleRequest ? entities : [entities]; var insertIndexes = []; + var keys = entities.map(function(entityObject) { + return entityObject.key; + }); var req = { mode: MODE_NON_TRANSACTIONAL, - mutation: { - upsert: [], - insertAutoId: [] - } + mutation: entities.reduce(function(acc, entityObject, index) { + var ent = entity.entityToEntityProto(entityObject.data); + ent.key = entity.keyToKeyProto(this.datasetId, entityObject.key); + if (entity.isKeyComplete(entityObject.key)) { + acc.upsert.push(ent); + } else { + insertIndexes.push(index); + acc.insertAutoId.push(ent); + } + return acc; + }, { upsert: [], insertAutoId: [] }) }; if (this.id) { req.transaction = this.id; req.mode = MODE_TRANSACTIONAL; } - for (var i = 0; i < keys.length; i++) { - var e = entity.entityToEntityProto(objs[i]); - e.key = entity.keyToKeyProto(this.datasetId, keys[i]); - if (entity.isKeyComplete(keys[i])) { - req.mutation.upsert.push(e); - } else { - insertIndexes.push(i); - req.mutation.insertAutoId.push(e); - } - } this.makeReq( 'commit', req, function(err, resp) { if (err || !resp) { return callback(err); } - resp.mutationResult.insertAutoIdKeys = resp.mutationResult.insertAutoIdKeys || []; - var i = 0; - resp.mutationResult.insertAutoIdKeys.forEach(function(item) { - keys[insertIndexes[i++]] = entity.keyFromKeyProto(item); + (resp.mutationResult.insertAutoIdKeys || []).forEach(function(key, index) { + keys[insertIndexes[index]] = entity.keyFromKeyProto(key); }); - callback(null, keys); + callback(null, isMultipleRequest ? keys : keys[0]); }); }; /** - * Deletes the entitiy identified with the specified key in the - * current transaction. - * @param {Array} key - * @param {Function} callback - */ -Transaction.prototype.del = function(key, callback) { - this.delAll([key], callback); -}; - -/** - * Deletes all entities identified with the specified list of keys + * Deletes all entities identified with the specified list of key(s) * in the current transaction. * @param {Array} keys * @param {Function} callback */ -Transaction.prototype.delAll = function(keys, callback) { - keysProto = []; - keys.forEach(function(k) { - keysProto.push(entity.keyToKeyProto(this.id, k)); - }); +Transaction.prototype.delete = function(keys, callback) { + var isMultipleRequest = Array.isArray(keys[0]); + keys = isMultipleRequest ? keys : [keys]; + callback = callback || util.noop; var req = { mode: MODE_NON_TRANSACTIONAL, mutation: { - 'delete': keysProto + delete: keys.map(entity.keyToKeyProto.bind(null, this.id)) } }; if (this.id) { req.transaction = this.id; req.mode = MODE_TRANSACTIONAL; } - this.makeReq('commit', req, function(err, resp) { - callback && callback(err); - }); + this.makeReq('commit', req, callback); }; /** @@ -350,24 +298,12 @@ Dataset.prototype.get = function(key, callback) { this.transaction.get(key, callback); }; -Dataset.prototype.getAll = function(keys, callback) { - this.transaction.getAll(keys, callback); -}; - Dataset.prototype.save = function(key, obj, callback) { this.transaction.save(key, obj, callback); }; -Dataset.prototype.saveAll = function(keys, objs, callback) { - this.transaction.saveAll(keys, objs, callback); -}; - -Dataset.prototype.del = function(key, callback) { - this.transaction.del(key, callback); -}; - -Dataset.prototype.delAll = function(keys, callback) { - this.transaction.delAll(keys, callback); +Dataset.prototype.delete = function(key, callback) { + this.transaction.delete(key, callback); }; Dataset.prototype.runQuery = function(q, callback) { diff --git a/regression/datastore.js b/regression/datastore.js index b2a4bd823b9..8173e60d9b9 100644 --- a/regression/datastore.js +++ b/regression/datastore.js @@ -36,13 +36,13 @@ describe('datastore', function() { }; var postKeyName = 'post1'; - ds.save(['Post', postKeyName], post, function(err, key) { + ds.save({ key: ['Post', postKeyName], data: post }, function(err, key) { if (err) return done(err); assert.equal(key[1], postKeyName); ds.get(['Post', postKeyName], function(err, entity) { if (err) return done(err); assert.deepEqual(entity.data, post); - ds.del(['Post', postKeyName], function(err) { + ds.delete(['Post', postKeyName], function(err) { if (err) return done(err); done(); }); @@ -62,13 +62,13 @@ describe('datastore', function() { }; var postKeyId = '123456789'; - ds.save(['Post', postKeyId], post, function(err, key) { + ds.save({ key: ['Post', postKeyId], data: post }, function(err, key) { if (err) return done(err); assert.equal(key[1], postKeyId); ds.get(['Post', postKeyId], function(err, entity) { if (err) return done(err); assert.deepEqual(entity.data, post); - ds.del(['Post', postKeyId], function(err) { + ds.delete(['Post', postKeyId], function(err) { if (err) return done(err); done(); }); @@ -86,14 +86,14 @@ describe('datastore', function() { wordCount: 400, rating: 5.0, }; - ds.save(['Post', null], post, function(err, key) { + ds.save({ key: ['Post', null], data: post }, function(err, key) { if (err) return done(err); assert(key[1]); var assignedId = key[1]; ds.get(['Post', assignedId], function(err, entity) { if (err) return done(err); assert.deepEqual(entity.data, post); - ds.del(['Post', assignedId], function(err) { + ds.delete(['Post', assignedId], function(err) { if (err) return done(err); done(); }); @@ -121,15 +121,18 @@ describe('datastore', function() { rating: 4.5, }; var key = ['Post', null]; - ds.saveAll([key, key], [post1, post2], function(err, keys) { + ds.save([ + { key: key, data: post1 }, + { key: key, data: post2 } + ], function(err, keys) { if (err) return done(err); assert.equal(keys.length,2); var firstKey = ['Post', keys[0][1]], secondKey = ['Post', keys[1][1]]; - ds.getAll([firstKey, secondKey], function(err, entities) { + ds.get([firstKey, secondKey], function(err, entities) { if (err) return done(err); assert.equal(entities.length, 2); - ds.delAll([firstKey, secondKey], function(err) { + ds.delete([firstKey, secondKey], function(err) { if (err) return done(err); done(); }); @@ -196,7 +199,12 @@ describe('datastore', function() { before(function(done) { - ds.saveAll(keys, characters, function(err, keys) { + ds.save(keys.map(function(key, index) { + return { + key: key, + data: characters[index] + }; + }), function(err, keys) { if (err) return done(err); done(); }); @@ -343,7 +351,7 @@ describe('datastore', function() { after(function(done) { - ds.delAll(keys, function(err) { + ds.delete(keys, function(err) { if (err) return done(err); done(); }); @@ -366,7 +374,7 @@ describe('datastore', function() { tDone(); return; } else { - ds.save(key, obj, function(err, keyRes) { + ds.save({ key: key, data: obj }, function(err, keyRes) { if (err) console.log(err); tDone(); return; @@ -378,7 +386,7 @@ describe('datastore', function() { ds.get(key, function(err, entity) { if (err) return done(err); assert.deepEqual(entity.data, obj); - ds.del(entity.key, function(err) { + ds.delete(entity.key, function(err) { if (err) return done(err); done(); }) diff --git a/test/datastore.js b/test/datastore.js index 5e496979b45..798e1134a8c 100644 --- a/test/datastore.js +++ b/test/datastore.js @@ -108,8 +108,9 @@ describe('Dataset', function() { assert.equal(proto.keys.length, 1); callback(null, mockResp_get); }; - ds.getAll([ - ['Kind', 123]], function(err, entities) { + ds.get([ + ['Kind', 123] + ], function(err, entities) { var entity = entities[0]; var properties = entity.data; assert.deepEqual(entity.key, ['Kind', 5732568548769792]); @@ -129,7 +130,7 @@ describe('Dataset', function() { assert.equal(!!proto.mutation.delete, true); callback(); }; - ds.del(['Kind', 123], done); + ds.delete(['Kind', 123], done); }); it('should multi delete by keys', function(done) { @@ -139,19 +140,11 @@ describe('Dataset', function() { assert.equal(proto.mutation.delete.length, 2); callback(); }; - ds.delAll([ + ds.delete([ ['Kind', 123], ['Kind', 345] ], done); }); - it('should throw if number of keys dont match the number of objs', function() { - assert.throws(function() { - var ds = new datastore.Dataset({ projectId: 'test' }); - ds.saveAll([ - ['Kind', 123], ['Kind', 456]], [{}], function(){}); - }, /The length of the keys/); - }); - it('should save with incomplete key', function(done) { var ds = new datastore.Dataset({ projectId: 'test' }); ds.transaction.makeReq = function(method, proto, callback) { @@ -159,7 +152,7 @@ describe('Dataset', function() { assert.equal(proto.mutation.insertAutoId.length, 1); callback(); }; - ds.save(['Kind', 123, null], {}, done); + ds.save({ key: ['Kind', 123, null], data: {} }, done); }); it('should save with keys', function(done) { @@ -170,8 +163,10 @@ describe('Dataset', function() { assert.equal(proto.mutation.upsert[0].properties.k.stringValue, 'v'); callback(); }; - ds.saveAll([ - ['Kind', 123], ['Kind', 456]], [{k: 'v'}, {k: 'v'}], done); + ds.save([ + { key: ['Kind', 123], data: { k: 'v' } }, + { key: ['Kind', 456], data: { k: 'v' } } + ], done); }); it('should produce proper allocate IDs req protos', function(done) {