Skip to content

Commit c298ac5

Browse files
committed
Get rid of unnecessary conversion between Entity and EntityPb in commit
function and make sure we handle partial keys correctly for hierarchical / nested keys. Also add test cases which cover hierarchical keys.
1 parent 2b2e9cd commit c298ac5

File tree

2 files changed

+149
-13
lines changed

2 files changed

+149
-13
lines changed

datastore/google/cloud/datastore/batch.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ class Batch(object):
8080
def __init__(self, client):
8181
self._client = client
8282
self._mutations = []
83+
# Stores a list of entity.Entity and .entity_pb2.Entity objects with
84+
# partial keys
8385
self._partial_key_entities = []
8486
self._status = self._INITIAL
8587

@@ -210,10 +212,10 @@ def put_entity_pb(self, entity_pb):
210212
if self.project != entity_pb.key.partition_id.project_id:
211213
raise ValueError("Key must be from same project as batch")
212214

213-
# Key is considered partial if neither "id" nor "name" values of the key
214-
# "path" field are set
215+
# Key is considered partial if neither "id" nor "name" values of the last
216+
# key path element are set
215217
is_key_partial = not entity_pb.key.path or (
216-
not entity_pb.key.path[0].name and not entity_pb.key.path[0].id
218+
not entity_pb.key.path[-1].name and not entity_pb.key.path[-1].id
217219
)
218220

219221
if is_key_partial:
@@ -280,17 +282,11 @@ def _commit(self):
280282
# order) directly ``_partial_key_entities``.
281283
for new_key_pb, entity in zip(updated_keys, self._partial_key_entities):
282284
if isinstance(entity, entity_pb2.Entity):
283-
# Entity Protobuf instance
285+
# entity_pb2.Entity Protobuf instance
284286
new_id = new_key_pb.path[-1].id
285-
286-
# TODO: Make this efficient, don't rely on conversion back and forth
287-
entity_ = helpers.entity_from_protobuf(entity)
288-
entity_.key = entity_.key.completed_key(new_id)
289-
290-
entity.key.CopyFrom(entity_.key.to_protobuf())
291-
pass
287+
entity.key.path[-1].id = new_id
292288
else:
293-
# Entity class
289+
# entity.Entity class
294290
new_id = new_key_pb.path[-1].id
295291
entity.key = entity.key.completed_key(new_id)
296292

datastore/tests/unit/test_batch.py

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import mock
1818

1919
from google.cloud.datastore_v1.proto import entity_pb2
20+
from google.cloud.datastore.key import Key
2021

2122

2223
class TestBatch(unittest.TestCase):
@@ -463,6 +464,90 @@ def test_put_entity_w_completed_key(self):
463464
mutated_entity = _mutated_pb(self, batch.mutations, "upsert")
464465
self.assertEqual(mutated_entity.key, key._key)
465466

467+
def test_commit_w_completed_key_hierarchical_key_integer_id(self):
468+
from google.cloud.datastore_v1.proto import datastore_pb2
469+
470+
project = "PROJECT"
471+
new_id = 1234
472+
ds_api = _make_datastore_api(new_id)
473+
client = _Client(project, datastore_api=ds_api)
474+
batch = self._make_one(client)
475+
476+
# Complete key where ID of last key path is set
477+
key1 = Key("Parent", "foo", "Child", 1234, project=project)
478+
self.assertFalse(key1.is_partial)
479+
480+
entity_pb = entity_pb2.Entity()
481+
entity_pb.key.CopyFrom(key1.to_protobuf())
482+
483+
self.assertEqual(len(entity_pb.key.path), 2)
484+
self.assertEqual(entity_pb.key.path[0].kind, "Parent")
485+
self.assertEqual(entity_pb.key.path[0].name, "foo")
486+
self.assertEqual(entity_pb.key.path[1].kind, "Child")
487+
self.assertEqual(entity_pb.key.path[1].id, 1234)
488+
489+
is_key_partial = not entity_pb.key.path or (
490+
not entity_pb.key.path[-1].name and not entity_pb.key.path[-1].id
491+
)
492+
self.assertFalse(is_key_partial)
493+
494+
self.assertEqual(batch._status, batch._INITIAL)
495+
batch.begin()
496+
self.assertEqual(batch._status, batch._IN_PROGRESS)
497+
batch.commit()
498+
self.assertEqual(batch._status, batch._FINISHED)
499+
500+
mode = datastore_pb2.CommitRequest.NON_TRANSACTIONAL
501+
ds_api.commit.assert_called_once_with(project, mode, [], transaction=None)
502+
503+
is_key_partial = not entity_pb.key.path or (
504+
not entity_pb.key.path[-1].name and not entity_pb.key.path[-1].id
505+
)
506+
self.assertFalse(is_key_partial)
507+
self.assertEqual(entity_pb.key.path[-1].id, 1234)
508+
509+
def test_commit_w_completed_key_hierarchical_key_string_name(self):
510+
from google.cloud.datastore_v1.proto import datastore_pb2
511+
512+
project = "PROJECT"
513+
new_id = 1234
514+
ds_api = _make_datastore_api(new_id)
515+
client = _Client(project, datastore_api=ds_api)
516+
batch = self._make_one(client)
517+
518+
# Complete key where ID of last key path is set
519+
key1 = Key("Parent", "foo", "Child", "name1", project=project)
520+
self.assertFalse(key1.is_partial)
521+
522+
entity_pb = entity_pb2.Entity()
523+
entity_pb.key.CopyFrom(key1.to_protobuf())
524+
525+
self.assertEqual(len(entity_pb.key.path), 2)
526+
self.assertEqual(entity_pb.key.path[0].kind, "Parent")
527+
self.assertEqual(entity_pb.key.path[0].name, "foo")
528+
self.assertEqual(entity_pb.key.path[1].kind, "Child")
529+
self.assertEqual(entity_pb.key.path[1].name, "name1")
530+
531+
is_key_partial = not entity_pb.key.path or (
532+
not entity_pb.key.path[-1].name and not entity_pb.key.path[-1].id
533+
)
534+
self.assertFalse(is_key_partial)
535+
536+
self.assertEqual(batch._status, batch._INITIAL)
537+
batch.begin()
538+
self.assertEqual(batch._status, batch._IN_PROGRESS)
539+
batch.commit()
540+
self.assertEqual(batch._status, batch._FINISHED)
541+
542+
mode = datastore_pb2.CommitRequest.NON_TRANSACTIONAL
543+
ds_api.commit.assert_called_once_with(project, mode, [], transaction=None)
544+
545+
is_key_partial = not entity_pb.key.path or (
546+
not entity_pb.key.path[-1].name and not entity_pb.key.path[-1].id
547+
)
548+
self.assertFalse(is_key_partial)
549+
self.assertEqual(entity_pb.key.path[-1].name, "name1")
550+
466551
def test_commit_w_partial_key_entities(self):
467552
from google.cloud.datastore_v1.proto import datastore_pb2
468553

@@ -493,11 +578,66 @@ def test_commit_w_partial_key_entities(self):
493578
ds_api.commit.assert_called_once_with(project, mode, [], transaction=None)
494579

495580
is_key_partial = not entity_pb.key.path or (
496-
not entity_pb.key.path[0].name and not entity_pb.key.path[0].id
581+
not entity_pb.key.path[-1].name and not entity_pb.key.path[-1].id
497582
)
498583
self.assertFalse(is_key_partial)
499584
self.assertEqual(entity_pb.key.path[0].id, new_id)
500585

586+
def test_commit_w_partial_key_entities_hierarchical_key(self):
587+
from google.cloud.datastore_v1.proto import datastore_pb2
588+
589+
project = "PROJECT"
590+
new_id = 1234
591+
ds_api = _make_datastore_api(new_id)
592+
client = _Client(project, datastore_api=ds_api)
593+
batch = self._make_one(client)
594+
595+
key = Key("Parent", "foo", "Child", project=project)
596+
self.assertTrue(key.is_partial)
597+
598+
key_completed_1 = Key("Parent", "foo", "Child", 1234, project=project)
599+
self.assertFalse(key_completed_1.is_partial)
600+
601+
key_completed_2 = Key("Parent", "foo", "Child", "child1", project=project)
602+
self.assertFalse(key_completed_2.is_partial)
603+
604+
key._id = None
605+
entity_pb = entity_pb2.Entity()
606+
entity_pb.key.CopyFrom(key.to_protobuf())
607+
batch._partial_key_entities.append(entity_pb)
608+
609+
self.assertEqual(len(entity_pb.key.path), 2)
610+
self.assertEqual(entity_pb.key.path[0].kind, "Parent")
611+
self.assertEqual(entity_pb.key.path[0].name, "foo")
612+
self.assertEqual(entity_pb.key.path[1].kind, "Child")
613+
614+
is_key_partial = not entity_pb.key.path or (
615+
not entity_pb.key.path[-1].name and not entity_pb.key.path[-1].id
616+
)
617+
self.assertTrue(is_key_partial)
618+
619+
self.assertEqual(batch._status, batch._INITIAL)
620+
batch.begin()
621+
self.assertEqual(batch._status, batch._IN_PROGRESS)
622+
batch.commit()
623+
self.assertEqual(batch._status, batch._FINISHED)
624+
625+
mode = datastore_pb2.CommitRequest.NON_TRANSACTIONAL
626+
ds_api.commit.assert_called_once_with(project, mode, [], transaction=None)
627+
628+
is_key_partial = not entity_pb.key.path or (
629+
not entity_pb.key.path[-1].name and not entity_pb.key.path[-1].id
630+
)
631+
self.assertFalse(is_key_partial)
632+
self.assertEqual(entity_pb.key.path[-1].id, new_id)
633+
634+
self.assertEqual(len(entity_pb.key.path), 2)
635+
self.assertEqual(entity_pb.key.path[0].kind, "Parent")
636+
self.assertEqual(entity_pb.key.path[0].name, "foo")
637+
self.assertEqual(entity_pb.key.path[1].kind, "Child")
638+
self.assertEqual(entity_pb.key.path[-1].id, new_id)
639+
self.assertEqual(entity_pb.key.path[1].id, new_id)
640+
501641

502642
class Test__parse_commit_response(unittest.TestCase):
503643
def _call_fut(self, commit_response_pb):

0 commit comments

Comments
 (0)