@@ -167,7 +167,7 @@ def dataset(self, *args, **kwargs):
167
167
kwargs ['connection' ] = self
168
168
return Dataset (* args , ** kwargs )
169
169
170
- def lookup (self , dataset_id , key_pbs ):
170
+ def lookup (self , dataset_id , key_pbs , missing = None , deferred = None ):
171
171
"""Lookup keys from a dataset in the Cloud Datastore.
172
172
173
173
Maps the ``DatastoreService.Lookup`` protobuf RPC.
@@ -201,6 +201,16 @@ def lookup(self, dataset_id, key_pbs):
201
201
(or a single Key)
202
202
:param key_pbs: The key (or keys) to retrieve from the datastore.
203
203
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
+
204
214
:rtype: list of :class:`gcloud.datastore.datastore_v1_pb2.Entity`
205
215
(or a single Entity)
206
216
:returns: The entities corresponding to the keys provided.
@@ -209,6 +219,12 @@ def lookup(self, dataset_id, key_pbs):
209
219
If multiple keys were provided and no results matched,
210
220
this will return an empty list.
211
221
"""
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
+
212
228
lookup_request = datastore_pb .LookupRequest ()
213
229
214
230
single_key = isinstance (key_pbs , datastore_pb .Key )
@@ -219,10 +235,28 @@ def lookup(self, dataset_id, key_pbs):
219
235
for key_pb in key_pbs :
220
236
lookup_request .key .add ().CopyFrom (key_pb )
221
237
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 ])
224
249
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 )
226
260
227
261
if single_key :
228
262
if results :
@@ -476,3 +510,14 @@ def delete_entities(self, dataset_id, key_pbs):
476
510
self .commit (dataset_id , mutation )
477
511
478
512
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 )
0 commit comments