Skip to content

Commit f4e431f

Browse files
committed
Merge pull request #1280 from stephenplusplus/spp--1266
datastore: add query.run
2 parents 243b52d + acdb279 commit f4e431f

8 files changed

Lines changed: 223 additions & 48 deletions

File tree

lib/datastore/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ Datastore.prototype.createQuery = function(namespace, kind) {
395395
namespace = this.namespace;
396396
}
397397

398-
return new Query(namespace, arrify(kind));
398+
return new Query(this, namespace, arrify(kind));
399399
};
400400

401401
/**

lib/datastore/query.js

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@
2222

2323
var arrify = require('arrify');
2424

25+
/*! Developer Documentation
26+
*
27+
* @param {module:datastore|module:transaction} scope - The parent scope the
28+
* query was created from.
29+
*/
2530
/**
2631
* Build a Query object.
2732
*
28-
* **Queries should be built with {module:datastore#createQuery} and run via
29-
* {module:datastore#runQuery}.**
33+
* **Queries are built with {module:datastore#createQuery} and
34+
* {module:transaction#createQuery}.**
3035
*
3136
* @resource [Datastore Queries]{@link http://goo.gl/Cag0r6}
3237
*
@@ -45,12 +50,14 @@ var arrify = require('arrify');
4550
* var datastore = gcloud.datastore();
4651
* var query = datastore.createQuery('AnimalNamespace', 'Lion');
4752
*/
48-
function Query(namespace, kinds) {
53+
function Query(scope, namespace, kinds) {
4954
if (!kinds) {
5055
kinds = namespace;
5156
namespace = null;
5257
}
5358

59+
this.scope = scope;
60+
5461
this.namespace = namespace || null;
5562
this.kinds = kinds;
5663

@@ -73,15 +80,17 @@ function Query(namespace, kinds) {
7380
* @return {module:datastore/query}
7481
*
7582
* @example
76-
* // Retrieve a list of people related to person "1234",
77-
* // disabling auto pagination
83+
* //-
84+
* // Retrieve a list of people related to "Dave", with auto-pagination
85+
* // disabled.
86+
* //-
7887
* var query = datastore.createQuery('Person')
79-
* .hasAncestor(datastore.key(['Person', 1234]))
88+
* .hasAncestor(datastore.key(['Person', 'Dave']))
8089
* .autoPaginate(false);
8190
*
8291
* function callback(err, entities, nextQuery, apiResponse) {
8392
* if (nextQuery) {
84-
* // More results might exist, so we'll manually fetch them
93+
* // More results might exist, so we'll manually fetch them.
8594
* datastore.runQuery(nextQuery, callback);
8695
* }
8796
* }
@@ -103,7 +112,7 @@ Query.prototype.autoPaginate = function(autoPaginateVal) {
103112
* @resource [Datastore Filters]{@link https://cloud.google.com/datastore/docs/concepts/queries#Datastore_Filters}
104113
*
105114
* @param {string} property - The field name.
106-
* @param {string=} operator - Operator (=, <, >, <=, >=). Default: `=`.
115+
* @param {string=} operator - Operator (=, <, >, <=, >=). Default: `=`
107116
* @param {*} value - Value to compare property to.
108117
* @return {module:datastore/query}
109118
*
@@ -295,4 +304,76 @@ Query.prototype.offset = function(n) {
295304
return this;
296305
};
297306

307+
/**
308+
* Run the query.
309+
*
310+
* @param {object=} options - Optional configuration.
311+
* @param {string} options.consistency - Specify either `strong` or `eventual`.
312+
* If not specified, default values are chosen by Datastore for the
313+
* operation. Learn more about strong and eventual consistency
314+
* [here](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore).
315+
* @param {function=} callback - The callback function. If omitted, a readable
316+
* stream instance is returned.
317+
* @param {?error} callback.err - An error returned while making this request
318+
* @param {object[]} callback.entities - A list of entities.
319+
* @param {?object} callback.nextQuery - If present, query with this object to
320+
* check for more results.
321+
* @param {object} callback.apiResponse - The full API response.
322+
*
323+
* @example
324+
* query.run(function(err, entities) {});
325+
*
326+
* //-
327+
* // To control how many API requests are made and page through the results
328+
* // manually, call `autoPaginate(false)` on your query.
329+
* //-
330+
* query.autoPaginate(false);
331+
*
332+
* function callback(err, entities, nextQuery, apiResponse) {
333+
* if (nextQuery) {
334+
* // More results might exist.
335+
* nextQuery.run(callback);
336+
* }
337+
* }
338+
*
339+
* query.run(callback);
340+
*
341+
* //-
342+
* // If you omit the callback, `run` will automatically call subsequent queries
343+
* // until no results remain. Entity objects will be pushed as they are found.
344+
* //-
345+
* query.run()
346+
* .on('error', console.error)
347+
* .on('data', function (entity) {})
348+
* .on('end', function() {
349+
* // All entities retrieved.
350+
* });
351+
*
352+
* //-
353+
* // If you anticipate many results, you can end a stream early to prevent
354+
* // unnecessary processing and API requests.
355+
* //-
356+
* query.run()
357+
* .on('data', function (entity) {
358+
* this.end();
359+
* });
360+
*
361+
* //-
362+
* // A keys-only query returns just the keys of the result entities instead of
363+
* // the entities themselves, at lower latency and cost.
364+
* //-
365+
* query.select('__key__');
366+
*
367+
* query.run(function(err, entities) {
368+
* // entities[].key = Key object
369+
* // entities[].data = Empty object
370+
* });
371+
*/
372+
Query.prototype.run = function() {
373+
var query = this;
374+
var args = [query].concat([].slice.call(arguments));
375+
376+
return this.scope.runQuery.apply(this.scope, args);
377+
};
378+
298379
module.exports = Query;

lib/datastore/request.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -388,15 +388,7 @@ DatastoreRequest.prototype.insert = function(entities, callback) {
388388
* @param {function=} callback - The callback function. If omitted, a readable
389389
* stream instance is returned.
390390
* @param {?error} callback.err - An error returned while making this request
391-
* (may be null).
392-
* @param {array} callback.entities - The list of entities returned by this
393-
* query. Note that this is a single page of entities, not necessarily
394-
* all of the entities.
395-
* @param {?module:datastore/query} callback.nextQuery - If present, run another
396-
* query with this object to check for more results.
397-
* @param {object} callback.apiResponse - The full API response.
398-
* @param {?error} callback.err - An error returned while making this request
399-
* @param {module:datastore/entity[]} callback.entities - A list of Entities
391+
* @param {object[]} callback.entities - A list of entities.
400392
* @param {?object} callback.nextQuery - If present, query with this object to
401393
* check for more results.
402394
* @param {object} callback.apiResponse - The full API response.

lib/datastore/transaction.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ var util = require('../common/util.js');
7171
* }, function(err) {});
7272
*/
7373
function Transaction(datastore) {
74+
this.datastore = datastore;
75+
7476
this.projectId = datastore.projectId;
7577

7678
this.request = datastore.request.bind(datastore);
@@ -96,6 +98,31 @@ nodeutil.inherits(Transaction, DatastoreRequest);
9698
* B) we build up a "modifiedEntities_" array on this object, used to build
9799
* the final commit request with.
98100
*/
101+
102+
/**
103+
* Create a query for the specified kind. See {module:datastore/query} for all
104+
* of the available methods.
105+
*
106+
* @resource [Datastore Queries]{@link https://cloud.google.com/datastore/docs/concepts/queries}
107+
*
108+
* @see {module:datastore/query}
109+
*
110+
* @param {string=} namespace - Namespace.
111+
* @param {string} kind - The kind to query.
112+
* @return {module:datastore/query}
113+
*
114+
* @example
115+
* datastore.runInTransaction(function(transaction, done) {
116+
* var query = transaction.createQuery('Company');
117+
*
118+
* // Run the query inside the transaction.
119+
* query.run(function(err, entities) {});
120+
* });
121+
*/
122+
Transaction.prototype.createQuery = function() {
123+
return this.datastore.createQuery.apply(this, arguments);
124+
};
125+
99126
/**
100127
* Delete all entities identified with the specified key(s) in the current
101128
* transaction.
@@ -134,7 +161,7 @@ Transaction.prototype.delete = function(entities) {
134161
* Reverse a transaction remotely and finalize the current transaction instance.
135162
*
136163
* @param {function} callback - The callback function.
137-
* @param {?error} callback.err - An error returned while making this request
164+
* @param {?error} callback.err - An error returned while making this request.
138165
* @param {object} callback.apiResponse - The full API response.
139166
*
140167
* @example

system-test/datastore.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,12 @@ describe('Datastore', function() {
641641
done();
642642
});
643643
});
644+
645+
it('should query from the Query object', function(done) {
646+
var q = datastore.createQuery('Character');
647+
648+
q.run(done);
649+
});
644650
});
645651

646652
describe('transactions', function() {
@@ -758,5 +764,21 @@ describe('Datastore', function() {
758764
});
759765
});
760766
});
767+
768+
it('should query within a transaction', function(done) {
769+
datastore.runInTransaction(function(t, tDone) {
770+
var query = t.createQuery('Company');
771+
772+
query.run(function(err, entities) {
773+
if (err) {
774+
tDone(err);
775+
return;
776+
}
777+
778+
assert(entities.length > 0);
779+
tDone();
780+
});
781+
}, done);
782+
});
761783
});
762784
});

test/datastore/index.js

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -191,32 +191,25 @@ describe('Datastore', function() {
191191
});
192192

193193
describe('createQuery', function() {
194-
var dsWithoutNamespace;
195-
196-
beforeEach(function() {
197-
dsWithoutNamespace = new Datastore({
198-
projectId: 'test',
199-
namespace: 'my-ns'
200-
});
201-
});
202-
203194
it('should return a Query object', function() {
204195
var namespace = 'namespace';
205196
var kind = ['Kind'];
206197

207198
var query = datastore.createQuery(namespace, kind);
208199
assert(query instanceof FakeQuery);
209200

210-
assert.strictEqual(query.calledWith_[0], namespace);
211-
assert.deepEqual(query.calledWith_[1], kind);
201+
assert.strictEqual(query.calledWith_[0], datastore);
202+
assert.strictEqual(query.calledWith_[1], namespace);
203+
assert.deepEqual(query.calledWith_[2], kind);
212204
});
213205

214206
it('should include the default namespace', function() {
215207
var kind = ['Kind'];
216208
var query = datastore.createQuery(kind);
217209

218-
assert.strictEqual(query.calledWith_[0], datastore.namespace);
219-
assert.deepEqual(query.calledWith_[1], kind);
210+
assert.strictEqual(query.calledWith_[0], datastore);
211+
assert.strictEqual(query.calledWith_[1], datastore.namespace);
212+
assert.deepEqual(query.calledWith_[2], kind);
220213
});
221214
});
222215

test/datastore/query.js

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,46 @@
1717
'use strict';
1818

1919
var assert = require('assert');
20+
2021
var Query = require('../../lib/datastore/query.js');
2122

2223
describe('Query', function() {
24+
var SCOPE = {};
25+
var NAMESPACE = 'Namespace';
26+
var KINDS = 'Kind';
27+
28+
var query;
29+
30+
beforeEach(function() {
31+
query = new Query(SCOPE, NAMESPACE, KINDS);
32+
});
33+
2334
describe('instantiation', function() {
35+
it('should localize the scope', function() {
36+
assert.strictEqual(query.scope, SCOPE);
37+
});
38+
39+
it('should localize the namespace', function() {
40+
assert.strictEqual(query.namespace, NAMESPACE);
41+
});
42+
43+
it('should localize the kind', function() {
44+
assert.strictEqual(query.kinds, KINDS);
45+
});
46+
2447
it('should use null for all falsy namespace values', function() {
2548
[
26-
new Query('', 'Kind'),
27-
new Query(null, 'Kind'),
28-
new Query(undefined, 'Kind'),
29-
new Query(0, 'Kind'),
30-
new Query('Kind')
49+
new Query(SCOPE, '', KINDS),
50+
new Query(SCOPE, null, KINDS),
51+
new Query(SCOPE, undefined, KINDS),
52+
new Query(SCOPE, 0, KINDS),
53+
new Query(SCOPE, KINDS)
3154
].forEach(function(query) {
3255
assert.strictEqual(query.namespace, null);
33-
assert.equal(query.kinds, 'Kind');
3456
});
3557
});
3658

37-
it('should support custom namespaces', function() {
38-
var query = new Query('ns', ['kind1']);
39-
assert.equal(query.namespace, 'ns');
40-
});
41-
4259
it('should enable auto pagination by default', function() {
43-
var query = new Query(['kind1']);
4460
assert.strictEqual(query.autoPaginateVal, true);
4561
});
4662
});
@@ -301,4 +317,23 @@ describe('Query', function() {
301317
assert.strictEqual(query, nextQuery);
302318
});
303319
});
320+
321+
describe('run', function() {
322+
it('should call the parent instance runQuery correctly', function() {
323+
var args = [0, 1, 2];
324+
var runQueryReturnValue = {};
325+
326+
query.scope.runQuery = function() {
327+
assert.strictEqual(this, query.scope);
328+
assert.strictEqual(arguments[0], query);
329+
assert.strictEqual(arguments[1], args[0]);
330+
assert.strictEqual(arguments[2], args[1]);
331+
assert.strictEqual(arguments[3], args[2]);
332+
return runQueryReturnValue;
333+
};
334+
335+
var results = query.run.apply(query, args);
336+
assert.strictEqual(results, runQueryReturnValue);
337+
});
338+
});
304339
});

0 commit comments

Comments
 (0)