Лучший способ определить, какой из набора ключей существует в хранилище данных - PullRequest
3 голосов
/ 22 октября 2009

У меня есть несколько сотен ключей, все той же модели, которую я предварительно вычислил:

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

Некоторые из этих ключей относятся к реальным объектам в хранилище данных, а некоторые - нет. Я хочу определить, какие ключи соответствуют сущностям.

Нет необходимости знать данные внутри сущностей, просто существуют ли они.

Одним из решений было бы использование db.get ():

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

Однако эта процедура извлекает все данные сущностей из хранилища, что является ненужным и дорогостоящим.

Вторая идея состоит в том, чтобы использовать запрос с IN фильтром на key_name, вручную выбирая порциями по 30, чтобы соответствовать требованиям псевдофильтра IN. Однако запросы только к ключу не разрешены с фильтром IN.

Есть ли лучший способ?

Ответы [ 3 ]

3 голосов
/ 22 октября 2009
Фильтры

IN не поддерживаются хранилищем данных App Engine напрямую; это удобство, которое реализовано в клиентской библиотеке. Запрос IN с 30 значениями преобразуется в 30 запросов на равенство по одному значению каждый, что приводит к 30 регулярным запросам!

Из-за времени прохождения туда-обратно и затрат даже на запросы только по ключам, я подозреваю, вы обнаружите, что простейшая попытка выбрать все объекты в одной пакетной выборке является наиболее эффективной. Однако, если ваши объекты большие, вы можете выполнить дополнительную оптимизацию: для каждой вставляемой сущности вставьте пустую сущность «присутствие» как дочернюю для этой сущности и используйте ее в запросах. Например:

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)]
0 голосов
/ 22 октября 2009

Как этого не делать (обновление на основе ответа Ника Джонсона):

Я также рассматриваю возможность добавления параметра специально для возможности его сканирования с помощью фильтра 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)

Причина, по которой этого следует избегать, заключается в том, что фильтр IN для свойства последовательно выполняет сканирование индекса, плюс поиск по одному элементу в вашем наборе IN. Каждый поиск занимает 160-200 мс, поэтому очень быстро становится очень медленной операцией.

0 голосов
/ 22 октября 2009

На данный момент единственное решение, которое у меня есть, - это вручную запросить по ключу с помощью keys_only=True, один раз для каждого ключа.

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

На самом деле это может быть медленнее, чем просто загружать объекты в пакет и отбрасывать их, хотя пакетная загрузка также нарушает квоту Data Received from API.

...