データストアのエンティティ
さて今日からデータストアについて学習していきます。本書では実に100ページ以上をデータストアに割いています。やはりデータストアはアプリケーションにとって要ですよね。これがないと始まらない。
本章ではデータストアの導入をとても面白く行っていますのでご紹介。
これまでの議論でも話があったように、Webリクエストは複数のマシン上で処理されます。つまりWebリクエストを処理するスクリプトはステートレス、状態がないと言えます。お互いに関係を持たないってこと。これに対してデータストアはステートフルです。あるマシンが更新した内容を別のマシンでも見たいですからね。そんなステートフルなデータストアを複数のマシンに分散すると様々な問題 (?) が出てきます。この辺りがデータストアを難しいものにしているんでしょうね。
エンティティ、キー、プロパティ
データストアに保存されるデータのことをエンティティと言います。エンティティにはシステム全体の中でユニークな情報であるキーが付随しています。このキーは以下の情報で構成されています。
- アプリケーションID
- 種別
- エンティティID
最後のエンティティIDはアプリが自分で指定することも出来れば、データストアに自動的に生成してもらうことも出来ます。なお、指定の仕方によって呼び方が違います。
- アプリが指定する場合 → キー名
- データストアが生成する場合 → ID
全部データストアに生成してもらった方が楽でしょ?と思われるかもしれませんが、実はデータストアはキー名を指定してエンティティを取得するのが一番早いのです。なのでアプリが後で指定出来るようなキー名をエンティティIDにするわけです。
またエンティティで特徴的なのがプロパティです。エンティティのデータはこのプロパティに保存します。プロパティには色々な種類があります。何が特徴的かというと、一つのプロパティが複数の値を持つことが出来る点です。この辺りはおいおい見ていきましょう。
データストアAPIの紹介
それでは実際にデータストアを使ってみます。データストアとプログラムの関係 (マッピング) は以下のようになっています。
データストア | プログラム |
---|---|
種別 | クラス (db.Modelなどのサブクラス) |
エンティティ | 上記クラスのインスタンス |
プロパティ | インスタンスの各フィールド |
まず使うのはdb.Expandoというクラスです。このクラスから継承してエンティティを作成してみます。
class Book(db.Expando): pass # スタインベックの怒りの葡萄を登録する entity = Book() entity.title = 'The Grapes of Wrath' entity.author = 'John Steinbeck' entity.copyright_year = 1933 entity.author_birthdate = datetime.date(1902, 2, 27) entity.put()
db.Expandoクラスを継承して種別を作成して、エンティティを作成するとプロパティは後付けになります。この辺りがRDBMSと異なる点ですよね。エンティティの種別を決まっているけど、プロパティは決まっていないのです。まさしく自由です。
でも多分ほとんどの場合、このような使い方はせず、RDBMSのようにスキーマを定義して使うと思います。それがdb.Modelクラスです。
class Book(db.Model): title = db.StringProperty() author = db.StringProperty() copyright_year = db.IntegerProperty() author_birthdate = db.DateProperty() # ...生成、登録はdb.Expandoの場合と同じ
この場合、Bookという種別にある種強制をしています。つまり、↑で定義したプロパティ以外は登録することが出来ませんし、それぞれのプロパティの型も決められています。制約があってもこちらの方が扱いやすいのではないでしょうか。
この辺り、サンプルプロジェクトを使っていじった方が理解が深まると思います。Google Codeに簡単なサンプルを置いておきます。ご参考にどうぞ。
プロパティの値
プロパティの値に設定出来る型はGoogle Codeに掲載されていますので、そちらをご確認くださいませ。
ここで押さえておきたいのがどの型であればインデックスを作るのか、ということです。つまり検索することが出来るか、ということです。
型 | インデックスを作るか否か |
---|---|
db.StringProperty | ○ |
db.TextProperty | × |
db.ByteStringProperty | ○ |
db.BlobProperty | × |
この他にも型がありますが、インデックスを作るのかどうかが分かりません。まぁおいおいってことで。
文字列、テキスト、Blob
データストアに文字列を保存する場合、2つの型から選択します。
ここで大事なのは↑でも書きましたが、TextPropertyの場合インデックスが生成されないということです。つまりTextPropertyを使ってクエリを発行することは出来ないのです。なお、データストアではUnicodeをUTF-8にエンコードして保存します。なので500文字 (バイト) という制約はUTF-8にエンコードした後のサイズの制約になります。
またバイトデータに関しても同様に2つの型から選択します。
- str (500バイト未満) → ByteStringProperty
- str (500バイト以上) → BlobProperty
unsetとnull値
データストア上、プロパティの値をunset (設定していない) 状態とNone値を設定している状態とは異なる状態です。Noneという値が設定されていると見なします。私的には全てのプロパティをとりあえず埋めておいた方がよい感じがするんですがどうなんでしょう?まぁ、おいおい分かってくるでしょう。
複数の値を持つプロパティ
何度か出てきましたが、エンティティは一つのプロパティに対して複数の値を持つことが出来ます。もちろん型はばらばらで問題ありません。一見不思議な感じがしますが、プロパティにリストオブジェクトを持っていると考えると、そう目新しいことではありません。このようなプロパティの型はListPropertyとして定義します。
キーとキーオブジェクト
↑でキーの構成について記載しましたが、このキーはエンティティのプロパティにすることが出来ます。つまりエンティティ間で関連を作ることが可能になります。これはモデリングには必須の機能と言えます。ちなみにエンティティのキーはkey()メソッドで取得することが出来ます。
ここで勘違いしてはいけないのが (私が勘違いしていたんですが) 、キーとキー名 (もしくはID) は別物だということです。キー名 (またはID) はキーの構成要素の一つです。スクリプトからはこのキー名を指定してエンティティを作成することが出来ます。そしてデータストアからキー名を指定してエンティティを (高速に) 取り出すには、キー名をキーに変換しないといけません。キー名だけではエンティティを一意に特定出来ないのです。その変換を行う関数がこちらです。
# エンティティをキー名「hogehoge」で生成し、データストアに保存 e = Entity(key_name='hogehoge') e.prop = 123 e.put() # キー名を使ってデータストアからエンティティを取り出す key = db.Key.from_path('Entity', 'hogehoge') # ←ここ! e = Entity.get(key)
自分の中でキーとキー名がこんがらがってしまったので書いておきました。