Pregunta

Tengo unos cientos de teclas, todas del mismo modelo, que he calculado previamente:

candidate_keys = [db.Key(...), db.Key(...), db.Key(...), ...]

Algunas de estas claves se refieren a entidades reales en el almacén de datos, y otras no. Deseo determinar qué claves corresponden a las entidades.

No es necesario conocer los datos dentro de las entidades, solo si existen.

Una solución sería usar db.get ():

keys_with_entities = set()
for entity in db.get(candidate_keys):
  if entity:
    keys_with_entities.add(entity.key())

Sin embargo, este procedimiento recuperará todos los datos de la entidad de la tienda, lo que es innecesario y costoso.

Una segunda idea es utilizar una Consulta con un filtro IN en key_name , obteniendo manualmente fragmentos de 30 para cumplir los requisitos de IN pseudo-filtro. Sin embargo, las consultas de solo claves no están permitidas con el filtro IN .

¿Hay una mejor manera?

¿Fue útil?

Solución

Los filtros IN no son compatibles directamente con el almacén de datos de App Engine; son una conveniencia que se implementa en la biblioteca del cliente. Una consulta IN con 30 valores se traduce en 30 consultas de igualdad en un valor cada una, lo que da como resultado 30 consultas regulares.

Debido a los tiempos de viaje de ida y vuelta y al costo de incluso las consultas de solo claves, sospecho que encontrará que intentar obtener todas las entidades en un solo lote es lo más eficiente. Sin embargo, si sus entidades son grandes, puede realizar una optimización adicional: para cada entidad que inserte, inserte una entidad de 'presencia' vacía como elemento secundario de esa entidad, y úsela en las consultas. Por ejemplo:

foo = AnEntity(...)
foo.put()
presence = PresenceEntity(key_name='x', parent=foo)
presence.put()
...
def exists(keys):
  test_keys = [db.Key.from_path('PresenceEntity', 'x', parent=x) for x in keys)
  return [x is not None for x in db.get(test_keys)]

Otros consejos

En este punto, la única solución que tengo es consultar manualmente por clave con keys_only = True , una vez por clave.

for key in candidate_keys:
  if MyModel.all(keys_only=True).filter('__key__ =', key).count():
    keys_with_entities.add(key)

De hecho, esto puede ser más lento, simplemente cargando las entidades por lotes y descartándolas, aunque la carga por lotes también afecta la cuota de Datos Recibidos de API .

Cómo no hacerlo (actualización basada en la respuesta de Nick Johnson):

También estoy considerando agregar un parámetro específicamente para poder buscarlo con un filtro IN .

class MyModel(db.Model):
  """Some model"""
  # ... all the old stuff
  the_key = db.StringProperty(required=True) # just a duplicate of the key_name

#... meanwhile back in the example

for key_batch in batches_of_30(candidate_keys):
  key_names = [x.name() for x in key_batch]
  found_keys = MyModel.all(keys_only=True).filter('the_key IN', key_names)
  keys_with_entities.update(found_keys)

La razón por la que se debe evitar esto es que el filtro IN en una propiedad realiza un análisis de índice de forma secuencial, más la búsqueda una vez por elemento en su conjunto IN . Cada búsqueda lleva de 160 a 200 ms, por lo que muy rápidamente se convierte en una operación muy lenta.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top