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, читал чужой код ... Что мне не хватает?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
13
0
2 095
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

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

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

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

Поэтому, если мне нужна эта функция, я должен зацикливаться и отсортировать себя, взять любое требуемое попадание в процессор и кэшировать результаты. Это будет считаться «лучшей практикой»?

Sam 15.01.2009 15:19

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

Nick Johnson 16.01.2009 01:24

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

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

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

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

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

Да, но база данных ЗНАЕТ, что это делает. Следовательно, он может его оптимизировать.

Lee B 09.10.2009 17:53
Ответ принят как подходящий

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

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

Вот пример на 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))

Другие вопросы по теме