293 if unique: |
293 if unique: |
294 return query.get() |
294 return query.get() |
295 |
295 |
296 return query.fetch(limit, offset) |
296 return query.fetch(limit, offset) |
297 |
297 |
298 def buildTypedQueryString(self): |
|
299 """Returns a GQL query string compatible with PolyModel. |
|
300 """ |
|
301 return ''.join(self._buildTypedQueryParts()) |
|
302 |
|
303 def _buildTypedQueryParts(self): |
|
304 if self._base_model: |
|
305 return [ |
|
306 'SELECT * FROM ', self._base_model.__name__, |
|
307 " WHERE inheritance_line = '", self.getFullModelClassName(), "'"] |
|
308 |
|
309 return ['SELECT * FROM ', self._model.__name__] |
|
310 |
|
311 def buildOrderedQueryString(self, order_by=None): |
|
312 """Returns a an ordered GQL query string compatible with PolyModel. |
|
313 |
|
314 Args: |
|
315 order_by: optional field name by which to order the query results; |
|
316 default is None, in which case no ORDER BY clause is placed in |
|
317 the query string |
|
318 """ |
|
319 return ''.join(self._buildOrderedQueryParts(order_by=order_by)) |
|
320 |
|
321 def _buildOrderedQueryParts(self, order_by=None): |
|
322 query_str_parts = self._buildTypedQueryParts() |
|
323 |
|
324 if order_by: |
|
325 query_str_parts.extend([' ORDER BY ', order_by]) |
|
326 |
|
327 return query_str_parts |
|
328 |
|
329 def getEntitiesForLimitAndOffset(self, limit, offset=0, order_by=None): |
|
330 """Returns entities for given offset and limit or None if not found. |
|
331 |
|
332 Args: |
|
333 limit: max amount of entities to return |
|
334 offset: optional offset in entities list which defines first entity to |
|
335 return; default is zero (first entity) |
|
336 """ |
|
337 query_string = self.buildOrderedQueryString(order_by=order_by) |
|
338 query = db.GqlQuery(query_string) |
|
339 |
|
340 return query.fetch(limit, offset) |
|
341 |
|
342 def getNearestEntities(self, fields_to_try): |
|
343 """Get entities just before and just after the described entity. |
|
344 |
|
345 Args: |
|
346 fields_to_try: ordered list of key/value pairs that "describe" |
|
347 the desired entity (which may not necessarily exist), where key is |
|
348 the name of the field, and value is an instance of that field |
|
349 used in the comparison; if value is None, that field is skipped |
|
350 |
|
351 Returns: |
|
352 a two-tuple: ([nearest_entities], 'field_name') |
|
353 |
|
354 nearest_entities: list of entities being those just before and just |
|
355 after the (possibly non-existent) entity identified by the first |
|
356 of the supplied (non-None) fields |
|
357 OR |
|
358 possibly None if query had no results for the supplied field |
|
359 that was used. |
|
360 """ |
|
361 # SELECT * FROM base_class WHERE inheritance_line = 'derived_class' |
|
362 typed_query_parts = self._buildTypedQueryParts() |
|
363 |
|
364 if self._base_model: |
|
365 typed_query_parts.append(' AND %s > :1') |
|
366 else: |
|
367 typed_query_parts.append(' WHERE %s > :1') |
|
368 |
|
369 typed_query_fmt = ''.join(typed_query_parts) |
|
370 |
|
371 for field, value in fields_to_try: |
|
372 if value is None: |
|
373 # skip this not-supplied field |
|
374 continue |
|
375 |
|
376 query = db.GqlQuery(typed_query_fmt % field, value) |
|
377 return query.fetch(1), field |
|
378 |
|
379 # all fields exhausted, and all had None values |
|
380 return (None, None) |
|
381 |
|
382 def findNearestEntitiesOffset(width, fields_to_try): |
|
383 """Finds offset of beginning of a range of entities around the nearest. |
|
384 |
|
385 Args: |
|
386 width: the width of the "found" window around the nearest User found |
|
387 fields_to_try: see getNearestEntities() |
|
388 |
|
389 Returns: |
|
390 an offset into the list of entities that is width/2 less than the |
|
391 offset of the first entity returned by getNearestEntities(), or zero |
|
392 if that offset would be less than zero |
|
393 OR |
|
394 None if there are no nearest entities or the offset of the beginning of |
|
395 the range cannot be found for some reason |
|
396 """ |
|
397 # find entity "nearest" to supplied fields |
|
398 nearest_entities, field = self.getNearestEntities(fields_to_try) |
|
399 |
|
400 if not nearest_entities: |
|
401 # no "nearest" entity, so indicate that with None |
|
402 return None |
|
403 |
|
404 nearest_entity = nearest_entities[0] |
|
405 |
|
406 # start search for beginning of nearest Users range at offset zero |
|
407 offset = 0 |
|
408 entities = self.getEntitiesForLimitAndOffset(width, offset=offset) |
|
409 |
|
410 while True: |
|
411 for entity in entities: |
|
412 if getattr(nearest_entity, field) == getattr(entity, field): |
|
413 # nearest User found in current search range, so return a range start |
|
414 return max(0, (offset - (width/2))) |
|
415 |
|
416 offset = offset + 1 |
|
417 |
|
418 # nearest User was not in the current search range, so fetch the next set |
|
419 entities = self.getEntitiesForLimitAndOffset(width, offset=offset) |
|
420 |
|
421 if not entities: |
|
422 # nearest User never found, so indicate that with None |
|
423 break |
|
424 |
|
425 return None |
|
426 |
|
427 def updateModelProperties(self, model, model_properties): |
298 def updateModelProperties(self, model, model_properties): |
428 """Update existing model entity using supplied model properties. |
299 """Update existing model entity using supplied model properties. |
429 |
300 |
430 Args: |
301 Args: |
431 model: a model entity |
302 model: a model entity |