使いまわせそうな方法が見つかったのでメモ。
ユーザー毎にタグを作成できるような機能を作成していて、特定のユーザーのタグを一括で取得しようと
する場合、普通にタグ毎にエンティティを作成していると、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に入れておくのもいいみたい。取得にかかる時間が短かくなるのかな?