Skip to content

Commit

Permalink
Add 'exclude_from_indexes' method to Entity.
Browse files Browse the repository at this point in the history
Set it via ctor argument.

Pass it to 'Connection.save_entity'.

Fields in the sequence will have the 'indexed' field set False in the
corrsponding protobuf.

Fixes #83.
  • Loading branch information
tseaver committed Nov 3, 2014
1 parent f33e557 commit 79b4d5a
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 14 deletions.
9 changes: 8 additions & 1 deletion gcloud/datastore/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,8 @@ def allocate_ids(self, dataset_id, key_pbs):
datastore_pb.AllocateIdsResponse)
return list(response.key)

def save_entity(self, dataset_id, key_pb, properties):
def save_entity(self, dataset_id, key_pb, properties,
exclude_from_indexes=()):
"""Save an entity to the Cloud Datastore with the provided properties.
.. note::
Expand All @@ -387,6 +388,9 @@ def save_entity(self, dataset_id, key_pb, properties):
:type properties: dict
:param properties: The properties to store on the entity.
:type exclude_from_indexes: sequence of str
:param exclude_from_indexes: Names of properties *not* to be indexed.
"""
mutation = self.mutation()

Expand All @@ -410,6 +414,9 @@ def save_entity(self, dataset_id, key_pb, properties):
# Set the appropriate value.
helpers._set_protobuf_value(prop.value, value)

if name in exclude_from_indexes:
prop.value.indexed = False

# If this is in a transaction, we should just return True. The
# transaction will handle assigning any keys as necessary.
if self.transaction():
Expand Down
13 changes: 11 additions & 2 deletions gcloud/datastore/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,14 @@ class Entity(dict):
"""

def __init__(self, dataset=None, kind=None):
def __init__(self, dataset=None, kind=None, exclude_from_indexes=()):
super(Entity, self).__init__()
self._dataset = dataset
if kind:
self._key = Key().kind(kind)
else:
self._key = None
self._exclude_from_indexes = set(exclude_from_indexes)

def dataset(self):
"""Get the :class:`.dataset.Dataset` in which this entity belongs.
Expand Down Expand Up @@ -130,6 +131,13 @@ def kind(self):
if self._key:
return self._key.kind()

def exclude_from_indexes(self):
"""Return field names which are *not* to be indexed.
:rtype: list(str)
"""
return frozenset(self._exclude_from_indexes)

@classmethod
def from_key(cls, key, dataset=None):
"""Create entity based on :class:`.datastore.key.Key`.
Expand Down Expand Up @@ -213,7 +221,8 @@ def save(self):
key_pb = connection.save_entity(
dataset_id=dataset.id(),
key_pb=key.to_protobuf(),
properties=dict(self))
properties=dict(self),
exclude_from_indexes=self.exclude_from_indexes())

# If we are in a transaction and the current entity needs an
# automatically assigned ID, tell the transaction where to put that.
Expand Down
45 changes: 45 additions & 0 deletions gcloud/datastore/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,51 @@ def test_save_entity_wo_transaction_w_upsert(self):
self.assertEqual(len(props), 1)
self.assertEqual(props[0].name, 'foo')
self.assertEqual(props[0].value.string_value, u'Foo')
self.assertEqual(props[0].value.indexed, True)
self.assertEqual(len(mutation.delete), 0)
self.assertEqual(request.mode, rq_class.NON_TRANSACTIONAL)

def test_save_entity_w_exclude_from_indexes(self):
from gcloud.datastore.connection import datastore_pb
from gcloud.datastore.key import Key

DATASET_ID = 'DATASET'
key_pb = Key(path=[{'kind': 'Kind', 'id': 1234}]).to_protobuf()
rsp_pb = datastore_pb.CommitResponse()
conn = self._makeOne()
URI = '/'.join([
conn.API_BASE_URL,
'datastore',
conn.API_VERSION,
'datasets',
DATASET_ID,
'commit',
])
http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString())
result = conn.save_entity(DATASET_ID, key_pb, {'foo': u'Foo'},
exclude_from_indexes=['foo'])
self.assertEqual(result, True)
cw = http._called_with
self.assertEqual(cw['uri'], URI)
self.assertEqual(cw['method'], 'POST')
self.assertEqual(cw['headers']['Content-Type'],
'application/x-protobuf')
self.assertEqual(cw['headers']['User-Agent'], conn.USER_AGENT)
rq_class = datastore_pb.CommitRequest
request = rq_class()
request.ParseFromString(cw['body'])
self.assertEqual(request.transaction, '')
mutation = request.mutation
self.assertEqual(len(mutation.insert_auto_id), 0)
upserts = list(mutation.upsert)
self.assertEqual(len(upserts), 1)
upsert = upserts[0]
self.assertEqual(upsert.key, key_pb)
props = list(upsert.property)
self.assertEqual(len(props), 1)
self.assertEqual(props[0].name, 'foo')
self.assertEqual(props[0].value.string_value, u'Foo')
self.assertEqual(props[0].value.indexed, False)
self.assertEqual(len(mutation.delete), 0)
self.assertEqual(request.mode, rq_class.NON_TRANSACTIONAL)

Expand Down
28 changes: 17 additions & 11 deletions gcloud/datastore/test_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,31 @@ def _getTargetClass(self):

return Entity

def _makeOne(self, dataset=_MARKER, kind=_KIND):
def _makeOne(self, dataset=_MARKER, kind=_KIND, exclude_from_indexes=()):
from gcloud.datastore.dataset import Dataset

klass = self._getTargetClass()
if dataset is _MARKER:
dataset = Dataset(_DATASET_ID)
return klass(dataset, kind)
return klass(dataset, kind, exclude_from_indexes)

def test_ctor_defaults(self):
klass = self._getTargetClass()
entity = klass()
self.assertEqual(entity.key(), None)
self.assertEqual(entity.dataset(), None)
self.assertEqual(entity.kind(), None)
self.assertEqual(sorted(entity.exclude_from_indexes()), [])

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

dataset = Dataset(_DATASET_ID)
entity = self._makeOne(dataset, _KIND)
_EXCLUDE_FROM_INDEXES = ['foo', 'bar']
entity = self._makeOne(dataset, _KIND, _EXCLUDE_FROM_INDEXES)
self.assertTrue(entity.dataset() is dataset)
self.assertEqual(sorted(entity.exclude_from_indexes()),
sorted(_EXCLUDE_FROM_INDEXES))

def test_key_getter(self):
from gcloud.datastore.key import Key
Expand Down Expand Up @@ -132,7 +136,7 @@ def test_save_wo_transaction_wo_auto_id_wo_returned_key(self):
self.assertTrue(entity.save() is entity)
self.assertEqual(entity['foo'], 'Foo')
self.assertEqual(connection._saved,
(_DATASET_ID, 'KEY', {'foo': 'Foo'}))
(_DATASET_ID, 'KEY', {'foo': 'Foo'}, ()))
self.assertEqual(key._path, None)

def test_save_w_transaction_wo_partial_key(self):
Expand All @@ -146,7 +150,7 @@ def test_save_w_transaction_wo_partial_key(self):
self.assertTrue(entity.save() is entity)
self.assertEqual(entity['foo'], 'Foo')
self.assertEqual(connection._saved,
(_DATASET_ID, 'KEY', {'foo': 'Foo'}))
(_DATASET_ID, 'KEY', {'foo': 'Foo'}, ()))
self.assertEqual(transaction._added, ())
self.assertEqual(key._path, None)

Expand All @@ -162,11 +166,11 @@ def test_save_w_transaction_w_partial_key(self):
self.assertTrue(entity.save() is entity)
self.assertEqual(entity['foo'], 'Foo')
self.assertEqual(connection._saved,
(_DATASET_ID, 'KEY', {'foo': 'Foo'}))
(_DATASET_ID, 'KEY', {'foo': 'Foo'}, ()))
self.assertEqual(transaction._added, (entity,))
self.assertEqual(key._path, None)

def test_save_w_returned_key(self):
def test_save_w_returned_key_exclude_from_indexes(self):
from gcloud.datastore import datastore_v1_pb2 as datastore_pb
key_pb = datastore_pb.Key()
key_pb.partition_id.dataset_id = _DATASET_ID
Expand All @@ -175,13 +179,13 @@ def test_save_w_returned_key(self):
connection._save_result = key_pb
dataset = _Dataset(connection)
key = _Key()
entity = self._makeOne(dataset)
entity = self._makeOne(dataset, exclude_from_indexes=['foo'])
entity.key(key)
entity['foo'] = 'Foo'
self.assertTrue(entity.save() is entity)
self.assertEqual(entity['foo'], 'Foo')
self.assertEqual(connection._saved,
(_DATASET_ID, 'KEY', {'foo': 'Foo'}))
(_DATASET_ID, 'KEY', {'foo': 'Foo'}, ('foo',)))
self.assertEqual(key._path, [{'kind': _KIND, 'id': _ID}])

def test_delete_no_key(self):
Expand Down Expand Up @@ -257,8 +261,10 @@ class _Connection(object):
def transaction(self):
return self._transaction

def save_entity(self, dataset_id, key_pb, properties):
self._saved = (dataset_id, key_pb, properties)
def save_entity(self, dataset_id, key_pb, properties,
exclude_from_indexes=()):
self._saved = (dataset_id, key_pb, properties,
tuple(exclude_from_indexes))
return self._save_result

def delete_entities(self, dataset_id, key_pbs):
Expand Down

0 comments on commit 79b4d5a

Please sign in to comment.