ほんじゃらねっと

ダイエット中プログラマのブログ

Google App EngineでフィルタしないリストはBlob化してキーで一括取得

使いまわせそうな方法が見つかったのでメモ。


ユーザー毎にタグを作成できるような機能を作成していて、特定のユーザーのタグを一括で取得しようと
する場合、普通にタグ毎にエンティティを作成していると、200件くらいのタグをuserでfilterして
取得するだけでも結構時間がかかる。

class Tag(db.Model):
"""key_name is user_id/tagid"""
user = db.ReferenceProperty(User)
name = db.StringProperty()
...
tag_list = Tag.all().filter("user = ", user).order("name")


色々調べてみたところ、検索に使用しないデータはシリアライズしてzlib圧縮して
BlobPropertyに入れておくといいらしいことが分かった。

考えてみるとタグはユーザー毎に取得する以外でフィルタする予定はないので、
わざわざ個別のエンティティに入れておく必要はなく、リストのまま1つのエンティティに
入れておいても問題はなさそう。


Shibazuke作者さんから、pickleよりShibazukeがいいよ、とおすすめいただいたので、シリアライズには
Shibazukeを使ってみた。
http://pypi.python.org/pypi/Shibazuke/


ちなみにこちらのシリアライズスクリプトでも動作はした。
http://code.activestate.com/recipes/415791/


Shibazukeにzlib圧縮は含まれないので、ラッパーモジュールを作る。
上記のスクリプトを真似てみた。


serializer.py

import zlib
from cStringIO import StringIO
import pyshibazuke
def dumps(data, compress=False):
option = "Z" if compress else "N"
dumped = pyshibazuke.dumps(data)
if compress:
dumped = zlib.compress(dumped)
return "%s%s" % (option, dumped)
def loads(s):
buf = StringIO(s)
option = buf.read(1)
if option == "Z":
loaded = zlib.decompress(buf.read())
else:
loaded = buf.read()
data = pyshibazuke.loads(loaded)
return data


変更したモデルと取得処理はこんな感じ。
タグはタグIDをキーとし、値にタグ名を入れた辞書に入れて保管する。
取得後にタグ名を取得してソートする。

class Tag(db.Model):
"""key_name is user_id"""
tags = db.BlobProperty() # tag dict list. key = id
def set_tags(self, tags, compress=False):
self.tags = serializer.dumps(tags, compress)
def get_tags(self):
if self.tags:
return serializer.loads(self.tags)
else:
return None
...
user_tag = Tag.get_by_key_name(user_id)
tag_list = None
if user_tag:
tags = user_tag.get_tags()
if tags:
tag_list = tags.values()
if tag_list is None:
tag_list = []
tag_list = sorted(tag_list, key=lambda x: x.get("name"))


これでえらく速くなった。
タグの更新は、取得したタグの辞書を更新して、またset_tagsで更新する。


フィルタする必要がなく、zlib圧縮して1MBを超えないデータはこの方法で管理するのが良さそう。
1MBを超えそうでも、キーにインデックス番号をつけて分割すればいいかな。


今回はリストの保存にBlobPropertyを使ったけど、エンティティ毎に検索で使用しないデータを
BlobPropertyに入れておくのもいいみたい。取得にかかる時間が短かくなるのかな?