Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions gcloud/datastore/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def __init__(self, kind=None, dataset=None, namespace=None):
self._dataset = dataset
self._namespace = namespace
self._pb = datastore_pb.Query()
self._cursor = None
self._cursor = self._more_results = None
self._offset = 0

if kind:
Expand All @@ -85,6 +85,7 @@ def _clone(self):
namespace=self._namespace)
clone._pb.CopyFrom(self._pb)
clone._cursor = self._cursor
clone._more_results = self._more_results
return clone

def namespace(self):
Expand Down Expand Up @@ -347,21 +348,22 @@ def fetch(self, limit=None):
dataset_id=self.dataset().id(),
namespace=self._namespace,
)
# NOTE: `query_results` contains two extra values that we don't use,
# namely `more_results` and `skipped_results`. The value of
# `more_results` is unusable because it always returns an enum
# NOTE: `query_results` contains an extra value that we don't use,
# namely `skipped_results`.
#
# NOTE: The value of `more_results` is not currently useful because

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

# the back-end always returns an enum
# value of MORE_RESULTS_AFTER_LIMIT even if there are no more
# results. See
# https://github.com/GoogleCloudPlatform/gcloud-python/issues/280
# for discussion.
entity_pbs, end_cursor = query_results[:2]
entity_pbs, self._cursor, self._more_results = query_results[:3]

self._cursor = end_cursor
return [helpers.entity_from_protobuf(entity, dataset=self.dataset())
for entity in entity_pbs]

def cursor(self):
"""Returns cursor ID
"""Returns cursor ID from most recent ``fetch()``.

.. warning:: Invoking this method on a query that has not yet
been executed will raise a RuntimeError.
Expand All @@ -374,6 +376,28 @@ def cursor(self):
raise RuntimeError('No cursor')
return base64.b64encode(self._cursor)

def more_results(self):
"""Returns ``more_results`` flag from most recent ``fetch()``.

.. warning:: Invoking this method on a query that has not yet
been executed will raise a RuntimeError.

.. note::

The `more_results` is not currently useful because it is
always returned by the back-end as ``MORE_RESULTS_AFTER_LIMIT``
even if there are no more results. See
https://github.com/GoogleCloudPlatform/gcloud-python/issues/280
for discussion.

:rtype: :class:`gcloud.datastore.datastore_v1_pb2.
QueryResultBatch.MoreResultsType`
:returns: enumerated value: are there more results available.
"""
if self._more_results is None:
raise RuntimeError('No results')
return self._more_results

def with_cursor(self, start_cursor, end_cursor=None):
"""Specifies the starting / ending positions in a query's result set.

Expand Down
27 changes: 25 additions & 2 deletions gcloud/datastore/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ def test__clone(self):
_DATASET = 'DATASET'
_KIND = 'KIND'
_CURSOR = 'DEADBEEF'
_MORE_RESULTS = 2

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

_NAMESPACE = 'NAMESPACE'
dataset = Dataset(_DATASET)
query = self._makeOne(_KIND, dataset, _NAMESPACE)
query._cursor = _CURSOR
query._more_results = _MORE_RESULTS
clone = query._clone()
self.assertFalse(clone is query)
self.assertTrue(isinstance(clone, self._getTargetClass()))
Expand All @@ -63,6 +65,7 @@ def test__clone(self):
kq_pb, = list(clone.kind())
self.assertEqual(kq_pb.name, _KIND)
self.assertEqual(clone._cursor, _CURSOR)
self.assertEqual(clone._more_results, _MORE_RESULTS)

def test_to_protobuf_empty(self):
query = self._makeOne()
Expand Down Expand Up @@ -317,6 +320,7 @@ def test_fetch_default_limit(self):
self.assertEqual(connection._called_with, expected_called_with)

def test_fetch_explicit_limit(self):
import base64
from gcloud.datastore.datastore_v1_pb2 import Entity
_CURSOR = 'CURSOR'
_DATASET = 'DATASET'
Expand All @@ -336,7 +340,8 @@ def test_fetch_explicit_limit(self):
query = self._makeOne(_KIND, dataset, _NAMESPACE)
limited = query.limit(13)
entities = query.fetch(13)
self.assertEqual(query._cursor, _CURSOR)
self.assertEqual(query.cursor(), base64.b64encode(_CURSOR))
self.assertEqual(query.more_results(), connection._more)
self.assertEqual(len(entities), 1)
self.assertEqual(entities[0].key().path(),
[{'kind': _KIND, 'id': _ID}])
Expand All @@ -347,6 +352,24 @@ def test_fetch_explicit_limit(self):
}
self.assertEqual(connection._called_with, expected_called_with)

def test_more_results_not_fetched(self):
_DATASET = 'DATASET'
_KIND = 'KIND'
connection = _Connection()
dataset = _Dataset(_DATASET, connection)
query = self._makeOne(_KIND, dataset)
self.assertRaises(RuntimeError, query.more_results)

def test_more_results_fetched(self):
_MORE_RESULTS = 2

This comment was marked as spam.

This comment was marked as spam.

_DATASET = 'DATASET'
_KIND = 'KIND'
connection = _Connection()
dataset = _Dataset(_DATASET, connection)
query = self._makeOne(_KIND, dataset)
query._more_results = _MORE_RESULTS
self.assertEqual(query.more_results(), _MORE_RESULTS)

def test_cursor_not_fetched(self):
_DATASET = 'DATASET'
_KIND = 'KIND'
Expand Down Expand Up @@ -564,7 +587,7 @@ def connection(self):
class _Connection(object):
_called_with = None
_cursor = ''
_more = True
_more = 2

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

_skipped = 0

def __init__(self, *result):
Expand Down