Skip to content

Commit

Permalink
Merge pull request #124 from stephenplusplus/namespaces
Browse files Browse the repository at this point in the history
datastore: scope Key creation to include NS. fixes #116.
  • Loading branch information
silvolu committed Aug 22, 2014
2 parents e6b24cc + ea60e50 commit 3297751
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 128 deletions.
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

0 comments on commit 3297751

Please sign in to comment.