Skip to content

Commit 1b9f1c2

Browse files
committed
Merge pull request #425 from tseaver/423-expose_query_more_results
Fix #423: Expose the 'more_results' value fetched from the back-end.
2 parents ac999e8 + 5743370 commit 1b9f1c2

2 files changed

Lines changed: 56 additions & 9 deletions

File tree

gcloud/datastore/query.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def __init__(self, kind=None, dataset=None, namespace=None):
6969
self._dataset = dataset
7070
self._namespace = namespace
7171
self._pb = datastore_pb.Query()
72-
self._cursor = None
72+
self._cursor = self._more_results = None
7373
self._offset = 0
7474

7575
if kind:
@@ -85,6 +85,7 @@ def _clone(self):
8585
namespace=self._namespace)
8686
clone._pb.CopyFrom(self._pb)
8787
clone._cursor = self._cursor
88+
clone._more_results = self._more_results
8889
return clone
8990

9091
def namespace(self):
@@ -347,21 +348,22 @@ def fetch(self, limit=None):
347348
dataset_id=self.dataset().id(),
348349
namespace=self._namespace,
349350
)
350-
# NOTE: `query_results` contains two extra values that we don't use,
351-
# namely `more_results` and `skipped_results`. The value of
352-
# `more_results` is unusable because it always returns an enum
351+
# NOTE: `query_results` contains an extra value that we don't use,
352+
# namely `skipped_results`.
353+
#
354+
# NOTE: The value of `more_results` is not currently useful because
355+
# the back-end always returns an enum
353356
# value of MORE_RESULTS_AFTER_LIMIT even if there are no more
354357
# results. See
355358
# https://github.com/GoogleCloudPlatform/gcloud-python/issues/280
356359
# for discussion.
357-
entity_pbs, end_cursor = query_results[:2]
360+
entity_pbs, self._cursor, self._more_results = query_results[:3]
358361

359-
self._cursor = end_cursor
360362
return [helpers.entity_from_protobuf(entity, dataset=self.dataset())
361363
for entity in entity_pbs]
362364

363365
def cursor(self):
364-
"""Returns cursor ID
366+
"""Returns cursor ID from most recent ``fetch()``.
365367
366368
.. warning:: Invoking this method on a query that has not yet
367369
been executed will raise a RuntimeError.
@@ -374,6 +376,28 @@ def cursor(self):
374376
raise RuntimeError('No cursor')
375377
return base64.b64encode(self._cursor)
376378

379+
def more_results(self):
380+
"""Returns ``more_results`` flag from most recent ``fetch()``.
381+
382+
.. warning:: Invoking this method on a query that has not yet
383+
been executed will raise a RuntimeError.
384+
385+
.. note::
386+
387+
The `more_results` is not currently useful because it is
388+
always returned by the back-end as ``MORE_RESULTS_AFTER_LIMIT``
389+
even if there are no more results. See
390+
https://github.com/GoogleCloudPlatform/gcloud-python/issues/280
391+
for discussion.
392+
393+
:rtype: :class:`gcloud.datastore.datastore_v1_pb2.
394+
QueryResultBatch.MoreResultsType`
395+
:returns: enumerated value: are there more results available.
396+
"""
397+
if self._more_results is None:
398+
raise RuntimeError('No results')
399+
return self._more_results
400+
377401
def with_cursor(self, start_cursor, end_cursor=None):
378402
"""Specifies the starting / ending positions in a query's result set.
379403

gcloud/datastore/test_query.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@ def test__clone(self):
5151
_DATASET = 'DATASET'
5252
_KIND = 'KIND'
5353
_CURSOR = 'DEADBEEF'
54+
_MORE_RESULTS = 2
5455
_NAMESPACE = 'NAMESPACE'
5556
dataset = Dataset(_DATASET)
5657
query = self._makeOne(_KIND, dataset, _NAMESPACE)
5758
query._cursor = _CURSOR
59+
query._more_results = _MORE_RESULTS
5860
clone = query._clone()
5961
self.assertFalse(clone is query)
6062
self.assertTrue(isinstance(clone, self._getTargetClass()))
@@ -63,6 +65,7 @@ def test__clone(self):
6365
kq_pb, = list(clone.kind())
6466
self.assertEqual(kq_pb.name, _KIND)
6567
self.assertEqual(clone._cursor, _CURSOR)
68+
self.assertEqual(clone._more_results, _MORE_RESULTS)
6669

6770
def test_to_protobuf_empty(self):
6871
query = self._makeOne()
@@ -317,6 +320,7 @@ def test_fetch_default_limit(self):
317320
self.assertEqual(connection._called_with, expected_called_with)
318321

319322
def test_fetch_explicit_limit(self):
323+
import base64
320324
from gcloud.datastore.datastore_v1_pb2 import Entity
321325
_CURSOR = 'CURSOR'
322326
_DATASET = 'DATASET'
@@ -336,7 +340,8 @@ def test_fetch_explicit_limit(self):
336340
query = self._makeOne(_KIND, dataset, _NAMESPACE)
337341
limited = query.limit(13)
338342
entities = query.fetch(13)
339-
self.assertEqual(query._cursor, _CURSOR)
343+
self.assertEqual(query.cursor(), base64.b64encode(_CURSOR))
344+
self.assertEqual(query.more_results(), connection._more)
340345
self.assertEqual(len(entities), 1)
341346
self.assertEqual(entities[0].key().path(),
342347
[{'kind': _KIND, 'id': _ID}])
@@ -347,6 +352,24 @@ def test_fetch_explicit_limit(self):
347352
}
348353
self.assertEqual(connection._called_with, expected_called_with)
349354

355+
def test_more_results_not_fetched(self):
356+
_DATASET = 'DATASET'
357+
_KIND = 'KIND'
358+
connection = _Connection()
359+
dataset = _Dataset(_DATASET, connection)
360+
query = self._makeOne(_KIND, dataset)
361+
self.assertRaises(RuntimeError, query.more_results)
362+
363+
def test_more_results_fetched(self):
364+
_MORE_RESULTS = 2
365+
_DATASET = 'DATASET'
366+
_KIND = 'KIND'
367+
connection = _Connection()
368+
dataset = _Dataset(_DATASET, connection)
369+
query = self._makeOne(_KIND, dataset)
370+
query._more_results = _MORE_RESULTS
371+
self.assertEqual(query.more_results(), _MORE_RESULTS)
372+
350373
def test_cursor_not_fetched(self):
351374
_DATASET = 'DATASET'
352375
_KIND = 'KIND'
@@ -564,7 +587,7 @@ def connection(self):
564587
class _Connection(object):
565588
_called_with = None
566589
_cursor = ''
567-
_more = True
590+
_more = 2
568591
_skipped = 0
569592

570593
def __init__(self, *result):

0 commit comments

Comments
 (0)