Минимизируйте подзапросы с IN-запросами в AppEngine (python)

Есть ли какой-нибудь умный способ избежать дорогостоящего запроса с предложением IN в случаях, подобных следующему?

Я использую Google App Engine для создания приложения Facebook, и в какой-то момент мне (очевидно) нужно запросить хранилище данных, чтобы получить все объекты, принадлежащие любому из друзей facebook данного пользователя.

Предположим, у меня есть пара объектов, смоделированных как таковые:

class Thing(db.Model):
    owner = db.ReferenceProperty(reference_class=User, required=True)
    owner_id = db.StringProperty(required=True)
    ...

и

class User(db.Model):
    id = db.StringProperty(required=True)
    ...

В какой-то момент я запрашиваю Facebook, чтобы получить список друзей данного пользователя, и мне нужно выполнить следующий запрос

# get all Thing instances that belong to friends
query = Thing.all()
query.filter('owner_id IN', friend_ids)

Если бы я это сделал, AppEngine выполнил бы подзапрос для каждого идентификатора в friend_ids, вероятно, превысив максимальное количество подзапросов, которое может породить любой запрос (30).

Есть ли лучший способ сделать это (то есть минимизировать количество запросов)? Я понимаю, что нет никаких отношений и объединений с использованием хранилища данных, но, в частности, я бы подумал о добавлении новых полей в класс User или Thing, если это поможет упростить задачу.


person abahgat    schedule 18.10.2010    source источник


Ответы (2)


Я не думаю, что есть элегантное решение, но вы можете попробовать следующее:

В модели User используйте идентификатор Facebook в качестве имени ключа и сохраните список вещей каждого пользователя в ListProperty.

class Thing(db.Model):
  ...

class User(db.Model):
  things = db.ListProperty(db.Key)
  ...

Создание сущности будет выглядеть так:

user = User.get_or_insert(my_facebook_id)

thing = Thing()
thing.put()

user.things.append(thing.key())
user.put()

Поиск занимает 2 запроса:

friends = User.get_by_key_name(friend_ids)
thing_keys = []

for friend in friends:
  thing_keys.extend(friend.things)

things = db.get(thing_keys)
person Drew Sears    schedule 18.10.2010
comment
+1 Другой вариант - сделать вещи дочерними для пользователя, чтобы можно было возвращать запросы предков для определенного типа вещей. Использование key_names имеет решающее значение для того, чтобы это действительно работало. - person kevpie; 19.10.2010
comment
Это здорово, я даже сделал Вещи дочерними для Пользователя, как предложил Кевпи. Однако мне пришлось столкнуться еще с парой проблем: а) я не храню сущность пользователя для каждого friend_id, поэтому мне нужно отфильтровать значения None, которые я получаю при запросе с использованием get_by_key_name; б) Мне также нужно отфильтровать вещи по некоторым другим полям, но я делаю это для сущностей, которые я извлекаю после того, как получу их из DataStore. Есть ли лучший способ сделать это? - person abahgat; 20.10.2010
comment
Обязательно посмотрите выступления, которые Ник опубликовал в своем ответе. Вы можете использовать объект индекса в сочетании со свойством списка. Это показано в первом разговоре, опубликованном Ником. - person kevpie; 20.10.2010
comment
Я пытаюсь разработать альтернативу, используя объекты индекса. Выполнение get_by_key_name со списком друзей Facebook в качестве аргумента, по-видимому, слишком сильно нагружает DataStore. Проблема с объектами индекса, связанными с каждым пользователем, заключается в том, когда их обновлять, чтобы они соответствовали Facebook. - person abahgat; 25.10.2010

В этом выступлении Google I/O Бретт Слаткин описывает конкретную ситуацию. вы имеете дело с. См. также его дополнительный доклад в этом году.

person Nick Johnson    schedule 19.10.2010
comment
Боюсь, вы дважды разместили одну и ту же ссылку на прошлый доклад. Вы имели в виду именно этот? google.com/events/io/2009/sessions/ - person abahgat; 20.10.2010