GAE-結合なしで生きる方法は?
-
22-07-2019 - |
質問
問題の例:
エンティティ:
- ユーザーには名前と友達のリストが含まれます(ユーザー参照)
- ブログ投稿には、タイトル、コンテンツ、日付、ライター(ユーザー)が含まれています
要件:
ユーザーの友人による最近の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ビデオを見て、他のコードを読んでいます...何が欠けていますか?
解決 2
このトピックは、Google ioトークで説明されています。 http://code.google.com/events/io/sessions/BuildingScalableComplexApps。 html
基本的に、Googleチームは、リストプロパティとリレーショナルインデックスエンティティを使用することを提案しています。サンプルアプリケーションは、次の場所にあります。 http://pubsub-test.appspot.com/
他のヒント
提供したSQLソリューションの実行方法を見ると、基本的に次のようになります。
- 現在のユーザーの友達のリストを取得する
- リスト内の各ユーザーについて、最近の投稿に対してインデックススキャンを開始します
- ステップ2のすべてのスキャンを結合し、十分なエントリを取得したら停止します
イテレータとしてQueryインスタンスを使用し、それらに対してマージ結合を実行することにより、App Engineでまったく同じ手順を自分で実行できます。
これは多数の友人にうまく対応できないことは確かですが、SQLの実装とまったく同じ問題に悩まされています。エントリのコストはおおよそO(n log n)で、nは友人の数です。
"ユーザーをロードし、友人のリストをループして、最新のブログ投稿をロードします。"
結合はそれだけです-ネストされたループ。いくつかの種類の結合は、ルックアップを伴うループです。ほとんどのルックアップは単なるループです。一部はハッシュです。
"最後にすべてのブログ投稿をマージして、最新の10個のブログエントリを見つけます"
これは、制限付きのORDER BYです。それはデータベースがあなたのためにしていることです。
これに関してスケーラブルではないものはわかりません。とにかくデータベースが行うことです。
こちらは、 http://pubsub-test.appspot.com/から得たpythonの例です。:
誰もが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))