Skip to content

Commit

Permalink
Merge pull request #255 from tseaver/170-query_namespaces
Browse files Browse the repository at this point in the history
Fix #170:  handle namespaces in queries
  • Loading branch information
tseaver committed Oct 19, 2014
2 parents 628c430 + bfffdbf commit 67b8302
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 14 deletions.
22 changes: 19 additions & 3 deletions gcloud/datastore/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class Query(object):
:type dataset: :class:`gcloud.datastore.dataset.Dataset`
:param dataset: The dataset to query.
:type namespace: string or None
:param dataset: The namespace to which to restrict results.
"""

OPERATORS = {
Expand All @@ -52,8 +55,9 @@ class Query(object):
}
"""Mapping of operator strings and their protobuf equivalents."""

def __init__(self, kind=None, dataset=None):
def __init__(self, kind=None, dataset=None, namespace=None):
self._dataset = dataset
self._namespace = namespace
self._pb = datastore_pb.Query()
self._cursor = None

Expand All @@ -66,11 +70,20 @@ def _clone(self):
:rtype: :class:`gcloud.datastore.query.Query`
:returns: a copy of 'self'.
"""
clone = self.__class__(dataset=self._dataset)
clone = self.__class__(dataset=self._dataset,
namespace=self._namespace)
clone._pb.CopyFrom(self._pb)
clone._cursor = self._cursor
return clone

def namespace(self):
"""This query's namespace
:rtype: string or None
:returns: the namespace assigned to this query
"""
return self._namespace

def to_protobuf(self):
"""Convert :class:`Query` instance to :class:`.datastore_v1_pb2.Query`.
Expand Down Expand Up @@ -316,7 +329,10 @@ def fetch(self, limit=None):
clone = self.limit(limit)

query_results = self.dataset().connection().run_query(
query_pb=clone.to_protobuf(), dataset_id=self.dataset().id())
query_pb=clone.to_protobuf(),
dataset_id=self.dataset().id(),
namespace=self._namespace,
)
entity_pbs, end_cursor = query_results[:2]

self._cursor = end_cursor
Expand Down
30 changes: 19 additions & 11 deletions gcloud/datastore/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,44 @@ def _getTargetClass(self):

return Query

def _makeOne(self, kind=None, dataset=None):
return self._getTargetClass()(kind, dataset)
def _makeOne(self, kind=None, dataset=None, namespace=None):
return self._getTargetClass()(kind, dataset, namespace)

def test_ctor_defaults(self):
query = self._makeOne()
query = self._getTargetClass()()
self.assertEqual(query.dataset(), None)
self.assertEqual(list(query.kind()), [])
self.assertEqual(query.limit(), 0)
self.assertEqual(query.namespace(), None)

def test_ctor_explicit(self):
from gcloud.datastore.dataset import Dataset

_DATASET = 'DATASET'
_KIND = 'KIND'
_NAMESPACE = 'NAMESPACE'
dataset = Dataset(_DATASET)
query = self._makeOne(_KIND, dataset)
query = self._makeOne(_KIND, dataset, _NAMESPACE)
self.assertTrue(query.dataset() is dataset)
kq_pb, = list(query.kind())
self.assertEqual(kq_pb.name, _KIND)
self.assertEqual(query.namespace(), _NAMESPACE)

def test__clone(self):
from gcloud.datastore.dataset import Dataset

_DATASET = 'DATASET'
_KIND = 'KIND'
_CURSOR = 'DEADBEEF'
_NAMESPACE = 'NAMESPACE'
dataset = Dataset(_DATASET)
query = self._makeOne(_KIND, dataset)
query = self._makeOne(_KIND, dataset, _NAMESPACE)
query._cursor = _CURSOR
clone = query._clone()
self.assertFalse(clone is query)
self.assertTrue(isinstance(clone, self._getTargetClass()))
self.assertTrue(clone.dataset() is dataset)
self.assertEqual(clone.namespace(), _NAMESPACE)
kq_pb, = list(clone.kind())
self.assertEqual(kq_pb.name, _KIND)
self.assertEqual(clone._cursor, _CURSOR)
Expand Down Expand Up @@ -101,7 +106,7 @@ def test_ancestor_w_non_key_non_list(self):
query = self._makeOne()
self.assertRaises(TypeError, query.ancestor, object())

def test_ancester_wo_existing_ancestor_query_w_key_and_propfilter(self):
def test_ancestor_wo_existing_ancestor_query_w_key_and_propfilter(self):
from gcloud.datastore.key import Key
_KIND = 'KIND'
_ID = 123
Expand All @@ -121,7 +126,7 @@ def test_ancester_wo_existing_ancestor_query_w_key_and_propfilter(self):
self.assertEqual(p_pb.property.name, '__key__')
self.assertEqual(p_pb.value.key_value, key.to_protobuf())

def test_ancester_wo_existing_ancestor_query_w_key(self):
def test_ancestor_wo_existing_ancestor_query_w_key(self):
from gcloud.datastore.key import Key
_KIND = 'KIND'
_ID = 123
Expand All @@ -137,7 +142,7 @@ def test_ancester_wo_existing_ancestor_query_w_key(self):
self.assertEqual(p_pb.property.name, '__key__')
self.assertEqual(p_pb.value.key_value, key.to_protobuf())

def test_ancester_wo_existing_ancestor_query_w_list(self):
def test_ancestor_wo_existing_ancestor_query_w_list(self):
from gcloud.datastore.key import Key
_KIND = 'KIND'
_ID = 123
Expand All @@ -153,7 +158,7 @@ def test_ancester_wo_existing_ancestor_query_w_list(self):
self.assertEqual(p_pb.property.name, '__key__')
self.assertEqual(p_pb.value.key_value, key.to_protobuf())

def test_ancester_clears_existing_ancestor_query_w_only(self):
def test_ancestor_clears_existing_ancestor_query_w_only(self):
_KIND = 'KIND'
_ID = 123
query = self._makeOne()
Expand All @@ -164,7 +169,7 @@ def test_ancester_clears_existing_ancestor_query_w_only(self):
q_pb = after.to_protobuf()
self.assertEqual(list(q_pb.filter.composite_filter.filter), [])

def test_ancester_clears_existing_ancestor_query_w_others(self):
def test_ancestor_clears_existing_ancestor_query_w_others(self):
_KIND = 'KIND'
_ID = 123
_NAME = 'NAME'
Expand Down Expand Up @@ -257,6 +262,7 @@ def test_fetch_default_limit(self):
expected_called_with = {
'dataset_id': _DATASET,
'query_pb': query.to_protobuf(),
'namespace': None,
}
self.assertEqual(connection._called_with, expected_called_with)

Expand All @@ -266,6 +272,7 @@ def test_fetch_explicit_limit(self):
_DATASET = 'DATASET'
_KIND = 'KIND'
_ID = 123
_NAMESPACE = 'NAMESPACE'
entity_pb = Entity()
path_element = entity_pb.key.path_element.add()
path_element.kind = _KIND
Expand All @@ -276,7 +283,7 @@ def test_fetch_explicit_limit(self):
connection = _Connection(entity_pb)
connection._cursor = _CURSOR
dataset = _Dataset(_DATASET, connection)
query = self._makeOne(_KIND, dataset)
query = self._makeOne(_KIND, dataset, _NAMESPACE)
limited = query.limit(13)
entities = query.fetch(13)
self.assertEqual(query._cursor, _CURSOR)
Expand All @@ -286,6 +293,7 @@ def test_fetch_explicit_limit(self):
expected_called_with = {
'dataset_id': _DATASET,
'query_pb': limited.to_protobuf(),
'namespace': _NAMESPACE,
}
self.assertEqual(connection._called_with, expected_called_with)

Expand Down

0 comments on commit 67b8302

Please sign in to comment.