Skip to content

Commit 79b4d5a

Browse files
committed
Add 'exclude_from_indexes' method to Entity.
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.
1 parent f33e557 commit 79b4d5a

4 files changed

Lines changed: 81 additions & 14 deletions

File tree

gcloud/datastore/connection.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,8 @@ def allocate_ids(self, dataset_id, key_pbs):
371371
datastore_pb.AllocateIdsResponse)
372372
return list(response.key)
373373

374-
def save_entity(self, dataset_id, key_pb, properties):
374+
def save_entity(self, dataset_id, key_pb, properties,
375+
exclude_from_indexes=()):
375376
"""Save an entity to the Cloud Datastore with the provided properties.
376377
377378
.. note::
@@ -387,6 +388,9 @@ def save_entity(self, dataset_id, key_pb, properties):
387388
388389
:type properties: dict
389390
:param properties: The properties to store on the entity.
391+
392+
:type exclude_from_indexes: sequence of str
393+
:param exclude_from_indexes: Names of properties *not* to be indexed.
390394
"""
391395
mutation = self.mutation()
392396

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

417+
if name in exclude_from_indexes:
418+
prop.value.indexed = False
419+
413420
# If this is in a transaction, we should just return True. The
414421
# transaction will handle assigning any keys as necessary.
415422
if self.transaction():

gcloud/datastore/entity.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,14 @@ class Entity(dict):
7070
7171
"""
7272

73-
def __init__(self, dataset=None, kind=None):
73+
def __init__(self, dataset=None, kind=None, exclude_from_indexes=()):
7474
super(Entity, self).__init__()
7575
self._dataset = dataset
7676
if kind:
7777
self._key = Key().kind(kind)
7878
else:
7979
self._key = None
80+
self._exclude_from_indexes = set(exclude_from_indexes)
8081

8182
def dataset(self):
8283
"""Get the :class:`.dataset.Dataset` in which this entity belongs.
@@ -130,6 +131,13 @@ def kind(self):
130131
if self._key:
131132
return self._key.kind()
132133

134+
def exclude_from_indexes(self):
135+
"""Return field names which are *not* to be indexed.
136+
137+
:rtype: list(str)
138+
"""
139+
return frozenset(self._exclude_from_indexes)
140+
133141
@classmethod
134142
def from_key(cls, key, dataset=None):
135143
"""Create entity based on :class:`.datastore.key.Key`.
@@ -213,7 +221,8 @@ def save(self):
213221
key_pb = connection.save_entity(
214222
dataset_id=dataset.id(),
215223
key_pb=key.to_protobuf(),
216-
properties=dict(self))
224+
properties=dict(self),
225+
exclude_from_indexes=self.exclude_from_indexes())
217226

218227
# If we are in a transaction and the current entity needs an
219228
# automatically assigned ID, tell the transaction where to put that.

gcloud/datastore/test_connection.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,51 @@ def test_save_entity_wo_transaction_w_upsert(self):
665665
self.assertEqual(len(props), 1)
666666
self.assertEqual(props[0].name, 'foo')
667667
self.assertEqual(props[0].value.string_value, u'Foo')
668+
self.assertEqual(props[0].value.indexed, True)
669+
self.assertEqual(len(mutation.delete), 0)
670+
self.assertEqual(request.mode, rq_class.NON_TRANSACTIONAL)
671+
672+
def test_save_entity_w_exclude_from_indexes(self):
673+
from gcloud.datastore.connection import datastore_pb
674+
from gcloud.datastore.key import Key
675+
676+
DATASET_ID = 'DATASET'
677+
key_pb = Key(path=[{'kind': 'Kind', 'id': 1234}]).to_protobuf()
678+
rsp_pb = datastore_pb.CommitResponse()
679+
conn = self._makeOne()
680+
URI = '/'.join([
681+
conn.API_BASE_URL,
682+
'datastore',
683+
conn.API_VERSION,
684+
'datasets',
685+
DATASET_ID,
686+
'commit',
687+
])
688+
http = conn._http = Http({'status': '200'}, rsp_pb.SerializeToString())
689+
result = conn.save_entity(DATASET_ID, key_pb, {'foo': u'Foo'},
690+
exclude_from_indexes=['foo'])
691+
self.assertEqual(result, True)
692+
cw = http._called_with
693+
self.assertEqual(cw['uri'], URI)
694+
self.assertEqual(cw['method'], 'POST')
695+
self.assertEqual(cw['headers']['Content-Type'],
696+
'application/x-protobuf')
697+
self.assertEqual(cw['headers']['User-Agent'], conn.USER_AGENT)
698+
rq_class = datastore_pb.CommitRequest
699+
request = rq_class()
700+
request.ParseFromString(cw['body'])
701+
self.assertEqual(request.transaction, '')
702+
mutation = request.mutation
703+
self.assertEqual(len(mutation.insert_auto_id), 0)
704+
upserts = list(mutation.upsert)
705+
self.assertEqual(len(upserts), 1)
706+
upsert = upserts[0]
707+
self.assertEqual(upsert.key, key_pb)
708+
props = list(upsert.property)
709+
self.assertEqual(len(props), 1)
710+
self.assertEqual(props[0].name, 'foo')
711+
self.assertEqual(props[0].value.string_value, u'Foo')
712+
self.assertEqual(props[0].value.indexed, False)
668713
self.assertEqual(len(mutation.delete), 0)
669714
self.assertEqual(request.mode, rq_class.NON_TRANSACTIONAL)
670715

gcloud/datastore/test_entity.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,31 @@ def _getTargetClass(self):
1313

1414
return Entity
1515

16-
def _makeOne(self, dataset=_MARKER, kind=_KIND):
16+
def _makeOne(self, dataset=_MARKER, kind=_KIND, exclude_from_indexes=()):
1717
from gcloud.datastore.dataset import Dataset
1818

1919
klass = self._getTargetClass()
2020
if dataset is _MARKER:
2121
dataset = Dataset(_DATASET_ID)
22-
return klass(dataset, kind)
22+
return klass(dataset, kind, exclude_from_indexes)
2323

2424
def test_ctor_defaults(self):
2525
klass = self._getTargetClass()
2626
entity = klass()
2727
self.assertEqual(entity.key(), None)
2828
self.assertEqual(entity.dataset(), None)
2929
self.assertEqual(entity.kind(), None)
30+
self.assertEqual(sorted(entity.exclude_from_indexes()), [])
3031

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

3435
dataset = Dataset(_DATASET_ID)
35-
entity = self._makeOne(dataset, _KIND)
36+
_EXCLUDE_FROM_INDEXES = ['foo', 'bar']
37+
entity = self._makeOne(dataset, _KIND, _EXCLUDE_FROM_INDEXES)
3638
self.assertTrue(entity.dataset() is dataset)
39+
self.assertEqual(sorted(entity.exclude_from_indexes()),
40+
sorted(_EXCLUDE_FROM_INDEXES))
3741

3842
def test_key_getter(self):
3943
from gcloud.datastore.key import Key
@@ -132,7 +136,7 @@ def test_save_wo_transaction_wo_auto_id_wo_returned_key(self):
132136
self.assertTrue(entity.save() is entity)
133137
self.assertEqual(entity['foo'], 'Foo')
134138
self.assertEqual(connection._saved,
135-
(_DATASET_ID, 'KEY', {'foo': 'Foo'}))
139+
(_DATASET_ID, 'KEY', {'foo': 'Foo'}, ()))
136140
self.assertEqual(key._path, None)
137141

138142
def test_save_w_transaction_wo_partial_key(self):
@@ -146,7 +150,7 @@ def test_save_w_transaction_wo_partial_key(self):
146150
self.assertTrue(entity.save() is entity)
147151
self.assertEqual(entity['foo'], 'Foo')
148152
self.assertEqual(connection._saved,
149-
(_DATASET_ID, 'KEY', {'foo': 'Foo'}))
153+
(_DATASET_ID, 'KEY', {'foo': 'Foo'}, ()))
150154
self.assertEqual(transaction._added, ())
151155
self.assertEqual(key._path, None)
152156

@@ -162,11 +166,11 @@ def test_save_w_transaction_w_partial_key(self):
162166
self.assertTrue(entity.save() is entity)
163167
self.assertEqual(entity['foo'], 'Foo')
164168
self.assertEqual(connection._saved,
165-
(_DATASET_ID, 'KEY', {'foo': 'Foo'}))
169+
(_DATASET_ID, 'KEY', {'foo': 'Foo'}, ()))
166170
self.assertEqual(transaction._added, (entity,))
167171
self.assertEqual(key._path, None)
168172

169-
def test_save_w_returned_key(self):
173+
def test_save_w_returned_key_exclude_from_indexes(self):
170174
from gcloud.datastore import datastore_v1_pb2 as datastore_pb
171175
key_pb = datastore_pb.Key()
172176
key_pb.partition_id.dataset_id = _DATASET_ID
@@ -175,13 +179,13 @@ def test_save_w_returned_key(self):
175179
connection._save_result = key_pb
176180
dataset = _Dataset(connection)
177181
key = _Key()
178-
entity = self._makeOne(dataset)
182+
entity = self._makeOne(dataset, exclude_from_indexes=['foo'])
179183
entity.key(key)
180184
entity['foo'] = 'Foo'
181185
self.assertTrue(entity.save() is entity)
182186
self.assertEqual(entity['foo'], 'Foo')
183187
self.assertEqual(connection._saved,
184-
(_DATASET_ID, 'KEY', {'foo': 'Foo'}))
188+
(_DATASET_ID, 'KEY', {'foo': 'Foo'}, ('foo',)))
185189
self.assertEqual(key._path, [{'kind': _KIND, 'id': _ID}])
186190

187191
def test_delete_no_key(self):
@@ -257,8 +261,10 @@ class _Connection(object):
257261
def transaction(self):
258262
return self._transaction
259263

260-
def save_entity(self, dataset_id, key_pb, properties):
261-
self._saved = (dataset_id, key_pb, properties)
264+
def save_entity(self, dataset_id, key_pb, properties,
265+
exclude_from_indexes=()):
266+
self._saved = (dataset_id, key_pb, properties,
267+
tuple(exclude_from_indexes))
262268
return self._save_result
263269

264270
def delete_entities(self, dataset_id, key_pbs):

0 commit comments

Comments
 (0)