GAE - Как жить без анонсов?

Пример проблемы:

Сущности:

  • Пользователь содержит имя и список друзей (Ссылки на пользователей)
  • Сообщение в блоге содержит заголовок, содержание, дату и автора (пользователя)

Требование:

Мне нужна страница, которая отображает заголовок и ссылку на блог последних 10 сообщений друга пользователя. Я также хотел бы иметь возможность просматривать старые записи.

Решение SQL:

Итак, в sql land это будет примерно так:

select * from blog_post where user_id in (select friend_id from user_friend where user_id = :userId) order by date

Я могу думать о следующих решениях GAE:

  • Загрузите пользователя, пролистайте список друзей и загрузите их последние сообщения в блоге. Наконец, объедините все сообщения в блоге, чтобы найти последние 10 записей в блоге.
  • В сообщении блога есть список всех пользователей, у которых писатель является другом. Это будет означать простое чтение, но приведет к перегрузке квоты при добавлении друга, у которого много сообщений в блоге.

Я не верю, что какое-либо из этих решений будет масштабным.

Я уверен, что другие сталкивались с этой проблемой, но я искал, смотрел видео в Google io, читал чужой код ... Что мне не хватает?


person Sam    schedule 15.01.2009    source источник


Ответы (4)


Если вы посмотрите, как будет выполняться предоставленное вами решение SQL, оно будет выглядеть примерно так:

  1. Получить список друзей текущего пользователя
  2. Для каждого пользователя в списке запустите сканирование индекса по последним сообщениям.
  3. Объедините все сканы с шага 2, остановив их, когда вы получите достаточно записей.

Вы можете выполнить ту же процедуру самостоятельно в App Engine, используя экземпляры Query в качестве итераторов и выполняя над ними объединение слиянием.

Вы правы, что это не будет хорошо масштабироваться для большого количества друзей, но оно страдает точно такими же проблемами, как и реализация SQL, просто не маскирует их: получение последних 20 (например) записей стоит примерно O (n log n) work, где n - количество друзей.

person Nick Johnson    schedule 15.01.2009
comment
Поэтому, если мне нужна эта функция, я должен зацикливаться и отсортировать себя, взять любое требуемое попадание в процессор и кэшировать результаты. Это будет считаться лучшей практикой? - person Sam; 15.01.2009
comment
В значительной степени да. Если вам нужно 20 или меньше результатов, я бы предложил просто получить первые 20 результатов от каждого пользователя, отсортировать и взять первые 20 результатов. Если вы хотите большего, реализуйте правильную сортировку слиянием, используя запросы в качестве итераторов. - person Nick Johnson; 16.01.2009

Эта тема рассматривается в разговоре о Google io: http://code.google.com/events/io/sessions/BuildingScalableComplexApps.html

В основном команда Google предлагает использовать свойства списка и то, что они называют объектами реляционного индекса, пример приложения можно найти здесь: http://pubsub-test.appspot.com/

person Sam    schedule 25.06.2009

«Загрузите пользователя, пролистайте список друзей и загрузите их последние сообщения в блоге».

Вот и все, что есть соединение - вложенные циклы. Некоторые виды соединений представляют собой циклы с поиском. Большинство поисков - это просто циклы; некоторые из них - хэши.

«Наконец, объедините все сообщения в блоге, чтобы найти последние 10 записей в блоге»

Это ЗАКАЗ с ПРЕДЕЛОМ. Вот что делает за вас база данных.

Я не уверен, что в этом не масштабируется; в любом случае это то, что делает база данных.

person S.Lott    schedule 15.01.2009
comment
Да, но база данных ЗНАЕТ это. Следовательно, он может его оптимизировать. - person Lee B; 09.10.2009

Вот пример на Python, взятый из http://pubsub-test.appspot.com/:

У кого-нибудь есть для java? Спасибо.

from google.appengine.ext import webapp

from google.appengine.ext import db

class Message(db.Model):
 body = db.TextProperty(required=True)
 sender = db.StringProperty(required=True)
 receiver_id = db.ListProperty(int)

class SlimMessage(db.Model):
 body = db.TextProperty(required=True)
 sender = db.StringProperty(required=True)

class MessageIndex(db.Model):  
 receiver_id = db.ListProperty(int)

class MainHandler(webapp.RequestHandler):

 def get(self):
  receiver_id = int(self.request.get('receiver_id', '1'))
  key_only = self.request.get('key_only').lower() == 'on'
  if receiver_id:
    if key_only:
      keys = db.GqlQuery(
          'SELECT __key__ FROM MessageIndex WHERE receiver_id = :1',
          receiver_id).fetch(10)
      messages.extend(db.get([k.parent() for k in keys]))
    else:
      messages.extend(Message.gql('WHERE receiver_id = :1',
                      receiver_id).fetch(10))
person davidjnelson    schedule 23.11.2009