Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

datastore: scope Key creation to include NS. fixes #116. #124

Merged
merged 7 commits into from
Aug 22, 2014
41 changes: 16 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,12 @@ 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(datastore.key('Company', 123), function(err, entity) {});
ds.get(ds.key('Company', 123), function(err, entity) {});

// alternatively, you can retrieve multiple entities at once.
ds.get([
datastore.key('Company', 123),
datastore.key('Product', 'Computer')
ds.key('Company', 123),
ds.key('Product', 'Computer')
], function(err, entities) {});
~~~~

Expand All @@ -114,15 +114,15 @@ To learn more about keys and incomplete keys, skip to the Keys section.

~~~~ js
ds.save({
key: datastore.key('Company', null), data: {/*...*/}
key: ds.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.save([
{ key: datastore.key('Company', 123), data: {/*...*/} },
{ key: datastore.key('Product', 'Computer'), data: {/*...*/} }
{ key: ds.key('Company', 123), data: {/*...*/} },
{ key: ds.key('Product', 'Computer'), data: {/*...*/} }
], function(err, keys) {
// if the first key was incomplete, keys[0] will return the generated key.
});
Expand All @@ -136,10 +136,10 @@ ds.delete(['Company', 599900452312], function(err) {});
// alternatively, you can delete multiple entities of different
// kinds at once.
ds.delete([
datastore.key('Company', 599900452312),
datastore.key('Company', 599900452315),
datastore.key('Office', 'mtv'),
datastore.key('Company', 123, 'Employee', 'jbd')
ds.key('Company', 599900452312),
ds.key('Company', 599900452315),
ds.key('Office', 'mtv'),
ds.key('Company', 123, 'Employee', 'jbd')
], function(err) {});
~~~~

Expand Down Expand Up @@ -178,14 +178,14 @@ stored as properties is not currently supported.

~~~~ js
var q = ds.createQuery('Company')
.filter('__key__ =', datastore.key('Company', 'Google'))
.filter('__key__ =', ds.key('Company', 'Google'))
~~~~

In order to filter by ancestors, use `hasAncestor` helper.

~~~ js
var q = ds.createQuery('Child')
.hasAncestor(datastore.key('Parent', 123));
.hasAncestor(ds.key('Parent', 123));
~~~

##### Sorting
Expand Down Expand Up @@ -223,25 +223,16 @@ var q = ds.createQuery('Company')
#### Allocating IDs (ID generation)

You can generate IDs without creating entities. The following call will create
100 new IDs from the Company kind which exists under the default namespace.
100 new IDs from the Company kind which exists under the dataset's namespace. If
no namespace was provided when the dataset was created, the default namespace
will be used.

~~~~ js
ds.allocateIds(datastore.key('Company', null), 100, function(err, keys) {
ds.allocateIds(ds.key('Company', null), 100, function(err, keys) {

});
~~~~

You may prefer to create IDs from a non-default namespace by providing
an incomplete key with a namespace. Similar to the previous example, the
call below will create 100 new IDs, but from the Company kind that exists
under the "ns-test" namespace.

~~~~ js
var incompleteKey = datastore.key('ns-test', 'Company', null);
ds.allocateIds(incompleteKey, 100, function(err, keys) {
});
~~~~

#### Transactions

Datastore has support for transactions. Transactions allow you to perform
Expand Down
52 changes: 52 additions & 0 deletions lib/common/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,55 @@ function handleResp(err, resp, body, callback) {
}

module.exports.handleResp = handleResp;

/**
* Get the type of a value.
*
* @private
*
* @return {string}
*/
function getType(value) {
return Object.prototype.toString.call(value).match(/\s(\w+)\]/)[1];
}

/**
* Check if an object is of the given type.
*
* @param {*} value - Value to compare to.
* @param {string} type - Type to check against object's value.
* @return {boolean}
*
* @example
* ```js
* is([1, 2, 3], 'array');
* // true
* ```
*/
function is(value, type) {
return getType(value).toLowerCase() === type.toLowerCase();
}

module.exports.is = is;

/**
* Convert an object into an array.
*
* @param {object} object - Object to convert to an array.
* @return {array}
*
* @example
* ```js
* function aFunction() {
* return toArray(arguments);
* }
*
* aFunction(1, 2, 3);
* // [1, 2, 3]
* ```
*/
function toArray(object) {
return [].slice.call(object);
}

module.exports.toArray = toArray;
79 changes: 61 additions & 18 deletions lib/datastore/dataset.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,51 +54,94 @@ var SCOPES = [
* @constructor
* @alias module:datastore/dataset
*
* @param {object} options
* @param {string} options.id - Dataset ID. This is your project ID from the
* Google Developers Console.
* @param {object=} options
* @param {string} options.projectId - Dataset ID. This is your project ID from
* the Google Developers Console.
* @param {string} options.keyFileName - Full path to the JSON key downloaded
* from the Google Developers Console.
* @param {string} options.namespace - Namespace to isolate transactions to.
*
* @example
* ```js
* var dataset = new Dataset({
* id: 'my-project',
* projectId: 'my-project',
* keyFileName: '/path/to/keyfile.json'
* });
* ```
*/
function Dataset(opts) {
opts = opts || {};
var id = opts.projectId;
function Dataset(options) {
options = options || {};

this.connection = new conn.Connection({
keyFilename: opts.keyFilename,
keyFilename: options.keyFilename,
scopes: SCOPES
});
this.id = id;
this.id = options.projectId;
this.namespace = options.namespace;
this.transaction = this.createTransaction_();
}

/**
* Create a query from the current dataset to query the specified kinds.
* Helper to create a Key object, scoped to the dataset's namespace by default.
*
* You may also specify a configuration object to define a namespace and path.
*
* @example
* ```js
* var key;
*
* // Create a key from the dataset's namespace.
* key = dataset.key('Company', 123);
*
* // Create a key from a provided namespace and path.
* key = dataset.key({
* namespace: 'My-NS',
* path: ['Company', 123]
* });
* ```
*/
Dataset.prototype.key = function(keyConfig) {
if (!util.is(keyConfig, 'object')) {
keyConfig = {
namespace: this.namespace,
path: util.toArray(arguments)
};
}
return new entity.Key(keyConfig);
};


/**
* Create a query from the current dataset to query the specified kinds, scoped
* to the namespace provided at the initialization of the dataset.
*
* @borrows {module:datastore/query} as createQuery
*
* @param {string=} namespace - Optional namespace.
* @param {string|array} kinds - Kinds to query.
*
* @example
* ```js
* var query = dataset.createQuery(['Lion', 'Chimp']);
* var zooQuery = dataset.createQuery('zoo', ['Lion', 'Chimp']);
* var query;
*
* // If your dataset was scoped to a namespace at initialization, your query
* // will likewise be scoped to that namespace.
* query = dataset.createQuery(['Lion', 'Chimp']);
*
* // However, you may override the namespace per query.
* query = dataset.createQuery('AnimalNamespace', ['Lion', 'Chimp']);
*
* // You may also remove the namespace altogether.
* query = dataset.createQuery(null, ['Lion', 'Chimp']);
* ```
* @return {module:datastore/query}
*/
Dataset.prototype.createQuery = function(ns, kinds) {
if (!kinds) {
kinds = ns;
ns = '';
Dataset.prototype.createQuery = function(namespace, kinds) {
if (arguments.length === 1) {
kinds = util.arrayize(namespace);
namespace = this.namespace;
}
kinds = util.arrayize(kinds);
return new Query(ns, kinds);
return new Query(namespace, util.arrayize(kinds));
};

/**
Expand Down
38 changes: 16 additions & 22 deletions lib/datastore/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,18 @@ var SIGN_TO_ORDER = {
* Build a Datastore Key object.
*
* @constructor
* @param {...*} - Key descriptors.
* @param {object} - Configuration object.
* @param {...*} options.path - Key path.
* @param {string=} options.namespace - Optional namespace.
*
* @example
* ```js
* var key = new Key('Company', 123);
* ```
*/
function Key() {
if (arguments.length > 1) {
this.path_ = [].slice.call(arguments);
} else {
this.path_ = arguments[0];
}
function Key(options) {
this.namespace_ = options.namespace;
this.path_ = options.path;
}

module.exports.Key = Key;
Expand Down Expand Up @@ -220,15 +219,17 @@ module.exports.entityFromEntityProto = entityFromEntityProto;
* ```
*/
function keyFromKeyProto(proto) {
var keyPath = [];
var keyOptions = {
path: []
};
if (proto.partition_id && proto.partition_id.namespace) {
keyPath.push(proto.partition_id.namespace);
keyOptions.namespace = proto.partition_id.namespace;
}
proto.path_element.forEach(function(path) {
keyPath.push(path.kind);
keyPath.push(Number(path.id) || path.name || null);
keyOptions.path.push(path.kind);
keyOptions.path.push(Number(path.id) || path.name || null);
});
return new Key(keyPath);
return new Key(keyOptions);
}

module.exports.keyFromKeyProto = keyFromKeyProto;
Expand Down Expand Up @@ -261,15 +262,8 @@ function keyToKeyProto(key) {
if (keyPath.length < 2) {
throw new Error('A key should contain at least a kind and an identifier.');
}
var namespace = null;
var start = 0;
if (keyPath.length % 2 === 1) {
// the first item is the namespace
namespace = keyPath[0];
start = 1;
}
var path = [];
for (var i = start; i < (keyPath.length - start); i += 2) {
for (var i = 0; i < keyPath.length; i += 2) {
var p = { kind: keyPath[i] };
var val = keyPath[i+1];
if (val) {
Expand All @@ -285,9 +279,9 @@ function keyToKeyProto(key) {
var proto = {
path_element: path
};
if (namespace) {
if (key.namespace_) {
proto.partition_id = {
namespace: namespace
namespace: key.namespace_
};
}
return proto;
Expand Down
12 changes: 0 additions & 12 deletions lib/datastore/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,6 @@ var datastore = {};
*/
datastore.Dataset = require('./dataset');

/**
* @borrows {module:datastore/entity~Key} as key
*
* @example
* ```js
* var key = dataset.key('Company', 123);
* ```
*/
datastore.key = function() {
return new entity.Key([].slice.call(arguments));
};

/**
* @borrows {module:datastore/entity~Int} as int
*
Expand Down
6 changes: 5 additions & 1 deletion lib/datastore/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
* limitations under the License.
*/

/**
* @module datastore/query
*/

'use strict';

var util = require('../common/util.js');
Expand All @@ -38,7 +42,7 @@ var util = require('../common/util.js');
* ```
*/
function Query(namespace, kinds) {
this.namespace = namespace;
this.namespace = namespace || null;
this.kinds = kinds;

this.filters = [];
Expand Down
2 changes: 1 addition & 1 deletion lib/datastore/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ Transaction.prototype.delete = function(keys, callback) {
* supported. If more results are available, a query to retrieve the next page
* is provided to the callback function.
*
* @param {Query} query - Query object.
* @param {module:datastore/query} query - Query object.
* @param {function} callback - The callback function.
*
* @example
Expand Down
Loading