diff --git a/lib/datastore/request.js b/lib/datastore/request.js index f38fe1e1b4a..0ca5561ccef 100644 --- a/lib/datastore/request.js +++ b/lib/datastore/request.js @@ -166,6 +166,8 @@ DatastoreRequest.prototype.get = function(keys, callback) { * * @param {object|object[]} entities - Datastore key object(s). * @param {Key} entities.key - Datastore key object. + * @param {string=} entities.method - Optional method to explicity use for save. + * The choices include 'insert', 'update', 'upsert' and 'auto_insert_id'. * @param {object|object[]} entities.data - Data to save with the provided key. * If you provide an array of objects, you must use the explicit syntax: * `name` for the name of the property and `value` for its value. You may @@ -209,7 +211,7 @@ DatastoreRequest.prototype.get = function(keys, callback) { * //- * var companyKey = dataset.key(['Company', 123]); * var productKey = dataset.key(['Product', 'Computer']); - * + * * dataset.save([ * { * key: companyKey, @@ -224,6 +226,21 @@ DatastoreRequest.prototype.get = function(keys, callback) { * } * } * ], function(err) {}); + * + * //- + * // Explicitly attempt to 'insert' a specific entity. + * //- + * var userKey = dataset.key(['User', 'chilts']); + * + * dataset.save([ + * { + * key: userKey, + * method: 'insert', // force the method to 'insert' + * data: { + * fullName: 'Andrew Chilton' + * } + * } + * ], function(err) {}); */ DatastoreRequest.prototype.save = function(entities, callback) { var isMultipleRequest = Array.isArray(entities); @@ -234,6 +251,8 @@ DatastoreRequest.prototype.save = function(entities, callback) { var req = { mutation: entities.reduce(function(acc, entityObject, index) { var ent = {}; + var method = entityObject.method; + delete entityObject.method; if (Array.isArray(entityObject.data)) { ent.property = entityObject.data.map(function(data) { @@ -252,15 +271,35 @@ DatastoreRequest.prototype.save = function(entities, callback) { ent.key = entity.keyToKeyProto(entityObject.key); - if (entity.isKeyComplete(entityObject.key)) { - acc.upsert.push(ent); + if (method) { + switch (method) { + case 'insert': + acc.insert.push(ent); + break; + case 'update': + acc.update.push(ent); + break; + case 'upsert': + acc.upsert.push(ent); + break; + case 'insert_auto_id': + insertIndexes.push(index); + acc.insert_auto_id.push(ent); + break; + } } else { - insertIndexes.push(index); - acc.insert_auto_id.push(ent); + if (entity.isKeyComplete(entityObject.key)) { + acc.upsert.push(ent); + } else { + insertIndexes.push(index); + acc.insert_auto_id.push(ent); + } } return acc; }, { + insert: [], + update: [], upsert: [], insert_auto_id: [] }) diff --git a/regression/datastore.js b/regression/datastore.js index d64b849a8cd..e0636ec80ed 100644 --- a/regression/datastore.js +++ b/regression/datastore.js @@ -119,6 +119,38 @@ describe('datastore', function() { }); }); + it('should fail explicitly set second insert on save', function(done) { + var postKey = ds.key('Post'); + + ds.save({ key: postKey, data: post }, function(err) { + assert.ifError(err); + + // The key's path should now be complete. + assert(postKey.path[1]); + + ds.save({ key: postKey, method: 'insert', data: post }, function(err) { + assert.notEqual(err, null); // should fail insert + + ds.get(postKey, function(err, entity) { + assert.ifError(err); + + assert.deepEqual(entity.data, post); + + ds.delete(postKey, done); + }); + }); + }); + }); + + it('should fail explicitly set first update on save', function(done) { + var postKey = ds.key('Post'); + + ds.save({ key: postKey, method: 'update', data: post }, function(err) { + assert.notEqual(err, null); + done(); + }); + }); + it('should save/get/delete multiple entities at once', function(done) { var post2 = { title: 'How to make the perfect homemade pasta', diff --git a/test/datastore/request.js b/test/datastore/request.js index 488fd71c5d4..3d49b668a69 100644 --- a/test/datastore/request.js +++ b/test/datastore/request.js @@ -205,6 +205,25 @@ describe('Request', function() { ], done); }); + it('should save with specific method', function(done) { + request.makeReq_ = function(method, req, callback) { + assert.equal(method, 'commit'); + assert.equal(req.mutation.insert.length, 1); + assert.equal(req.mutation.update.length, 1); + assert.equal(req.mutation.insert[0].property[0].name, 'k'); + assert.equal( + req.mutation.insert[0].property[0].value.string_value, 'v'); + assert.equal(req.mutation.update[0].property[0].name, 'k2'); + assert.equal( + req.mutation.update[0].property[0].value.string_value, 'v2'); + callback(); + }; + request.save([ + { key: key, method: 'insert', data: { k: 'v' } }, + { key: key, method: 'update', data: { k2: 'v2' } } + ], done); + }); + it('should not set an indexed value by default', function(done) { request.makeReq_ = function(method, req) { var property = req.mutation.upsert[0].property[0];