Skip to content

Commit c2ba130

Browse files
committed
Merge pull request #429 from tseaver/306-connection_lookup_missing_deferred
Expose 'missing'/'deferred' in 'Connection.lookup'/'Dataset.get_entities'
2 parents 2a61f47 + 9cd0f6a commit c2ba130

File tree

4 files changed

+329
-121
lines changed

4 files changed

+329
-121
lines changed

gcloud/datastore/connection.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def dataset(self, *args, **kwargs):
167167
kwargs['connection'] = self
168168
return Dataset(*args, **kwargs)
169169

170-
def lookup(self, dataset_id, key_pbs):
170+
def lookup(self, dataset_id, key_pbs, missing=None, deferred=None):
171171
"""Lookup keys from a dataset in the Cloud Datastore.
172172
173173
Maps the ``DatastoreService.Lookup`` protobuf RPC.
@@ -201,6 +201,16 @@ def lookup(self, dataset_id, key_pbs):
201201
(or a single Key)
202202
:param key_pbs: The key (or keys) to retrieve from the datastore.
203203
204+
:type missing: an empty list or None.
205+
:param missing: If a list is passed, the key-only entity protobufs
206+
returned by the backend as "missing" will be copied
207+
into it. Use only as a keyword param.
208+
209+
:type deferred: an empty list or None.
210+
:param deferred: If a list is passed, the key protobufs returned
211+
by the backend as "deferred" will be copied into it.
212+
Use only as a keyword param.
213+
204214
:rtype: list of :class:`gcloud.datastore.datastore_v1_pb2.Entity`
205215
(or a single Entity)
206216
:returns: The entities corresponding to the keys provided.
@@ -209,6 +219,12 @@ def lookup(self, dataset_id, key_pbs):
209219
If multiple keys were provided and no results matched,
210220
this will return an empty list.
211221
"""
222+
if missing is not None and missing != []:
223+
raise ValueError('missing must be None or an empty list')
224+
225+
if deferred is not None and deferred != []:
226+
raise ValueError('deferred must be None or an empty list')
227+
212228
lookup_request = datastore_pb.LookupRequest()
213229

214230
single_key = isinstance(key_pbs, datastore_pb.Key)
@@ -219,10 +235,28 @@ def lookup(self, dataset_id, key_pbs):
219235
for key_pb in key_pbs:
220236
lookup_request.key.add().CopyFrom(key_pb)
221237

222-
lookup_response = self._rpc(dataset_id, 'lookup', lookup_request,
223-
datastore_pb.LookupResponse)
238+
results = []
239+
while True: # loop against possible deferred.
240+
lookup_response = self._rpc(dataset_id, 'lookup', lookup_request,
241+
datastore_pb.LookupResponse)
242+
243+
results.extend(
244+
[result.entity for result in lookup_response.found])
245+
246+
if missing is not None:
247+
missing.extend(
248+
[result.entity for result in lookup_response.missing])
224249

225-
results = [result.entity for result in lookup_response.found]
250+
if deferred is not None:
251+
deferred.extend([key for key in lookup_response.deferred])
252+
break
253+
254+
if not lookup_response.deferred:
255+
break
256+
257+
# We have deferred keys, and the user didn't ask to know about
258+
# them, so retry (but only with the deferred ones).
259+
_copy_deferred_keys(lookup_request, lookup_response)
226260

227261
if single_key:
228262
if results:
@@ -476,3 +510,14 @@ def delete_entities(self, dataset_id, key_pbs):
476510
self.commit(dataset_id, mutation)
477511

478512
return True
513+
514+
515+
def _copy_deferred_keys(lookup_request, lookup_response):
516+
"""Clear requested keys and copy deferred keys back in.
517+
518+
Helper ``Connection.lookup()``.
519+
"""
520+
for old_key in list(lookup_request.key):
521+
lookup_request.key.remove(old_key)
522+
for def_key in lookup_response.deferred:
523+
lookup_request.key.add().CopyFrom(def_key)

gcloud/datastore/dataset.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,20 +142,41 @@ def get_entity(self, key_or_path):
142142
if entities:
143143
return entities[0]
144144

145-
def get_entities(self, keys):
145+
def get_entities(self, keys, missing=None, deferred=None):
146146
"""Retrieves entities from the dataset, along with their attributes.
147147
148148
:type key: list of :class:`gcloud.datastore.key.Key`
149149
:param item_name: The name of the item to retrieve.
150150
151+
:type missing: an empty list or None.
152+
:param missing: If a list is passed, the key-only entities returned
153+
by the backend as "missing" will be copied into it.
154+
Use only as a keyword param.
155+
156+
:type deferred: an empty list or None.
157+
:param deferred: If a list is passed, the keys returned
158+
by the backend as "deferred" will be copied into it.
159+
Use only as a keyword param.
160+
151161
:rtype: list of :class:`gcloud.datastore.entity.Entity`
152162
:return: The requested entities.
153163
"""
154164
entity_pbs = self.connection().lookup(
155165
dataset_id=self.id(),
156-
key_pbs=[k.to_protobuf() for k in keys]
166+
key_pbs=[k.to_protobuf() for k in keys],
167+
missing=missing, deferred=deferred,
157168
)
158169

170+
if missing is not None:
171+
missing[:] = [
172+
helpers.entity_from_protobuf(missed_pb, dataset=self)
173+
for missed_pb in missing]
174+
175+
if deferred is not None:
176+
deferred[:] = [
177+
helpers.key_from_protobuf(deferred_pb)
178+
for deferred_pb in deferred]
179+
159180
entities = []
160181
for entity_pb in entity_pbs:
161182
entities.append(helpers.entity_from_protobuf(

0 commit comments

Comments
 (0)