スキップしてメイン コンテンツに移動

DatastoreとSerach APIを連携させる


Datastoreでは、複数のフィールドに対して不等式検索(!=や>など)やソートを行う場合、事前にIndexを構築しておく必要がある。しかも全ての検索条件の組み合わせごとにIndexが必要になるので、バックオフィス機能にありがちな複雑な条件での検索を行いたい場合、多くのIndexが必要だ。


ところが、Indexが増えるということは、そのぶん書き込みのコストとDatastoreの容量も大幅に増える。さらに、一つのプロジェクトに構築できるIndexの数は200までという上限もあるため、気軽にIndexを導入することは避けたいところだ。

こういったDatastore固有の制限の中で、RDBのような複雑な検索を行いたい場合、Search APIを活用するのは一つの手だ。Search APIの詳しい解説はこちらのブログがわかりやすい。

DatastoreとSearch APIを連携するにあたって重要なのはデータの一貫性だ。今回はこちらの記事を参考に実装してみた。というかそのまんまだけど(笑)

#!-*- coding:utf-8 -*-
from google.appengine.api import search
from google.appengine.ext import ndb
from google.appengine.ext import deferred

class UserModel(ndb.Model):
    """
    ユーザーモデル
    """
    user_name = ndb.StringProperty()        # 名前
    height = ndb.IntegerProperty()          # 身長
    birthday = ndb.DateProperty()           # 誕生日
    @classmethod
    def put_search_document(cls, key_id):
        model = ndb.Key(cls, key_id).get()
        if model:
            document = search.Document(
                doc_id=key_id,
                fields=[
                    search.TextField(name='user_name', value=model.user_name),
                    search.NumberField(name='height', value=model.height),
                    search.DateField(name='birthday', value=model.birthday),
                   ])
            index = search.Index(name="UserIndex")
            index.put(document)
    def _post_put_hook(self, future):
        deferred.defer(UserModel.put_search_document,
                       self.key.id(),
                       _transactional=ndb.in_transaction())
ndb.Modelの _post_put_hook を利用してDatastore登録時にSearch APIにも登録を行う。この時、deferredライブラリを使うところがポイントだ。deferredライブラリの内部ではtaskqueueを利用しているため、データの一貫性は保たれている。ただし、多少のタイムラグがおきるので、そういうタイミングが厳密なケースでは、この方法はおすすめできない。

参考サイトの例では、Datastore側とSearch APIにバージョン番号をもたせて、さらに厳密なチェックしている。正直、そこまでするならDatastoreでなく、もういっそMySQLにすればいいじゃんとも思ったけど、それではGAEを利用するメリットが半減してしまう。なぜならMySQL(Cloud SQL)はオートスケールしないからだ。悩ましいところだ。

このSearch APIとDatastoreが統合したような、新Datastoreができれば最高なんだけどなぁ。なんかできそうな気もするけど。どうなんでしょ。


コメント

このブログの人気の投稿

Managed Virtual Machine に望むこと

先日2014年4月22日に行われた、Google Cloud Platformセッションの模様がYoutubeに公開されてた。 地方民はこういうイベントになかなか参加できないので、動画配信はほんとに助かる。ありがたや。。。 動画の中で語られている Managed VM は地味だけど、なにげにすごい機能だと思う。GAEでできなかったほとんどの事がこれで解決できるかもしれない。 ただ、個人的に Managed VMでは、(スケールアウトしなくていいので)固定IPも使えるようになってほしい。なぜなら、Google App Engineで外部のシステムと連携するとき、相手側でIPアドレス制限されている事がある。だから、連携前にあらかじめこちらのIPアドレスを教えておくのだけど、GAEはPaaSなのでその範囲も広いし、いつ変わるかわからない。まぁ、IPアドレス制限という方法自体がすでに時代遅れな感じではあるけど。 ともかく、GAEではそういうシステムと連携する時は、間にGAEでないサーバーを中継させておく必要がある。なので、このためだけにOSやApacheのメンテが発生する。これは、ほんとにアホらしいことだ。もし、Managed VMで固定IPが使えれば、対外部システムとの処理だけそのインスタンスで行えばいいし、中継サーバーも不要になるので、だいぶ楽になるのだ。 Googleさん、よろしくおねがいします。

BlobstoreUploadHandlerの文字化け問題の解決方法

Google App Engine Python2.7環境で、blobstore_handlers.BlobstoreUploadHandlerを使って、ファイルアップロードする際、日本語のリクエストパラメータが文字化けする問題に長年悩まされていた。 「文字化け」といっても、マルチバイト文字はBase64にエンコードされて送信される、という謎仕様。さらに、文字の組み合わせによっては Base64ではなく、Quoted-printable にエンコードされたりと、意味がわからない。。。 そんなわけで、ファイルとテキストは、別のリクエストに分けたりと、クライアント側で対処していた。 先日、この問題をGCPのサポートに問い合わせた結果、あっさりと対応方法を教えてくれた。 app.yamlに以下を追加すれば良いだけ。 libraries: - name: webob version: "1.2.3" なんと、WebObのバグだったようだ。 http://docs.webob.org/en/stable/changes.html#id25 GAEのPython2.7環境では、webobの1.1.1(かなり古い)がデフォルトらしく、1.2.3で解決されたこのbugfixのため、この対応で回避できる。という事らしい。 といっても、1.2.3もかなり前のバージョンなので、もっと上位のバージョンしても問題なさそうだけど、今回はこのバージョンで様子見で。。。 そろそろ、Goに移行しようかなぁ。。。