Datastore API

GAEのキモと言えば、データストアAPI。他のAPIはそんな大したことはなくて(多分)、データストアがGoogleBigTableによって篦棒にスケールするというのがGAEの特徴なわけですよ。
と言うわけで、ドキュメントを印刷して通勤時間に読んでみました。3回。GAE使うなら精読しなきゃダメですな、これ。
http://code.google.com/appengine/docs/datastore/

特に検索式の制約が特徴的というか結構厳しいので、何か作ろうと思う前にインデックスの考え方と検索式の制約は頭に入れといたほうが良さそうです。

以下、メモ。下線を引いたとこだけ抜粋。

http://code.google.com/appengine/docs/datastore/entitiesandmodels.html

  • Modelクラスのクラス変数(Propertyを設定する奴)は、そのクラスをストアするときの設定を保持する。インスタンス変数は、値の実態を保持する。
  • ModelのサブクラスであるExpandoは、Modelと異なり保持するプロパティを動的に追加/削除できる。
  • Stringプロパティには500byte迄の文字列を入れることができる。
  • Listプロパティを検索式内で使うときには、リスト要素が1つでも検索式にマッチしたら解として採用される。
  • Referenceプロパティは逆方向の参照が可能で、"ModelName_set"(ModelNameは逆参照元となるクラスの名前)で参照する。
    • 名前が被るんじゃないか?
    • key値をリストやExpandoのダイナミックプロパティに入れても、逆参照はできない。Referenceのみ。

http://code.google.com/appengine/docs/datastore/creatinggettinganddeletingdata.html

  • 検索式や検索関数で取得エンティティの上限数を指定できるが、この値は実際にデータストアから取得されるデータ数には影響しない。
    • ということは上限数の指定ってあんまり使う機会がないかも。オフセット指定もできないし。

http://code.google.com/appengine/docs/datastore/keysandentitygroups.html

  • 全てのエンティティはkeyを保持している。keyの属性は、
    • path; エンティティの親子関係(後述)を保持。
    • kind; エンティティの種類。
    • name; エンティティの名前。nameかIDの択一。
    • ID; エンティティの通し番号。nameかIDの択一。昇順で採番されることが多いけど、あんまりあてにしないでね。
  • nameやIDは検索式には使えないが、関数を用いて直接エンティティを取得するのに使える。
  • 全てのエンティティはentity groupに所属する。
    • entity group単位でトランザクションを実行できる。
    • entity group単位で分散ネットワークのノードに配置される。
  • 全てのエンティティは0〜1個の親エンティティを持つことができる。
    • 親エンティティを持たないエンティティはrootと称される。
    • 親エンティティの指定はエンティティ生成時にのみ行うことができる。
    • rootを根とするエンティティの木構造ができるが、一本の木が一つのentity groupとなる。
  • Google的お勧めは、
    • トランザクション使わないならentity groupを作る必要はないよ。
    • なるべくデータを分散させた方が高速なので、entity groupの数は多いほうがいいよ。
    • entity groupは検索速度にはあんまり影響しないよ。

http://code.google.com/appengine/docs/datastore/queriesandindexes.html (重要!)

  • データの検索には常にインデックスが使われる。
    • インデックスは検索の種類の数だけ作成される。
    • インデックスはデータの更新があるたびに更新される。
    • インデックスはエンティティを以下の優先順位で並べた表。
      • 検索式で、等号(==)と共に使われるプロパティ
      • 検索式で、不等号(<, >, <=, >=)と共に使われるプロパティ
      • 検索式での並べ替え順
    • 検索は、インデックスを以下のように捜査することで行う。
      • インデックスの先頭位置から順に検索式を適用し、最初に適合するエンティティを見つける。
      • その位置から走査を継続し、検索式に適合しないエンティティかインデックスの終端を見つける。
      • 適合するエンティティと適合しないエンティティの間のエンティティが、検索式の解となる。
  • インデックスが存在しないと検索できない。
  • インデックスはindex.yamlを用いて作成される。
  • 開発用サーバを使っている場合、インデックスが存在しない検索を行うとエラーになるかわりに自動的にインデックスが作成され、index.yamlが更新される。
    • なので、開発用サーバで十分にテストしておけばindex.yamlの編集が必要になることはない気がする。
  • 検索式には以下の制約有り。
    • 検索式に出てくるプロパティを保持していないエンティティは、検索式の解とならない。
    • 「プロパティに値を保持していない」ことを検索式で調べることはできない。
      • None値を入れておけばOK
    • 一種類のプロパティしか不等号と共に使えない。
      • WHERE aaa > 10 AND aaa < 20 これはOK
      • WHERE aaa > 10 AND bbb < 20 これはNG。aaaとbbbの2種類が出てきてるから。
      • 等号は制限無し。
    • 不等号とORDER BYを併用するときは、ORDER BYの先頭に不等号で使うプロパティを入れる必要がある。
    • "!="演算子はない。

Transactions  |  App Engine standard environment for Python  |  Google Cloud

インデックスはどういうことかというと、
SELECT * FROM Sample WHERE aaa >= 10 AND aaa < 20 AND bbb == 0 ORDER BY aaa, ccc
という式があり、以下のようなエンティティがある場合、

aaa bbb ccc
9 0 26
8 1 4
13 0 10
12 0 6
26 1 29
23 0 10
13 1 13
18 1 25
23 0 10
10 1 27
27 0 29
22 1 6

bbb, aaa, cccの優先順でソートされてインデックスが作成され、

aaa bbb ccc
9 0 26
12 0 6
13 0 10
23 0 10
23 0 10
27 0 29
8 1 4
10 1 27
13 1 13
18 1 25
22 1 6
26 1 29

(10, 1, 27)から(18, 1, 25)までが検索式の解になる、という寸法。