StringPropertyの最大サイズ
こちらのドキュメントによると「StringPropertyに入れられる文字列は500byteまで」と書いてます。500byteと言われても、文字コードによって一文字あたりのサイズは違うしなあ。
というわけで実験してみました。
このページで適当な文字列を入力して……
<html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> </head> <body> <form action="/submit" method="POST"> <input type="text" name="the_text"> <input type="submit"> </form> </body> </html>
こっちのコードでStringPropertyの中に突っ込んでみます。
import wsgiref.handlers from google.appengine.ext import webapp from google.appengine.ext import db class MyString(db.Model): str = db.StringProperty() class SubmitPage(webapp.RequestHandler): def post(self): obj = MyString() obj.str = self.request.get('the_text') obj.put() obj2 = db.get(obj.key()) self.response.out.write(obj2.str) wsgiref.handlers.CGIHandler().run(webapp.WSGIApplication([('/submit', SubmitPage)]))
これらをGAEサーバにアップロードして、実験。
- '0'を500文字入力 → 正常
- '0'を500文字入力 → 500 ERROR
妥当ですよね。
さて、ここで「あ」を限界まで入力しますが、何文字まで入力できるでしょうか? EUC-JPやSHIFT_JISだと「あ」は2bytes、UTF-16でも2bytes、UTF-8だと3bytesです。
答は……
- 'あ'を500文字入力 → 正常
- 'あ'を501文字入力 → 500 ERROR
あっるぇぇぇ?
正解は、どうやら「StringPropertyには(500bytesではなくて)500文字を格納することができる」のようです。
ドキュメントの修正が必要なんじゃない?とMLに投げておきました。
Googleの囲い込みってのは杞憂じゃないかなあ
Google App Engineなんか使ってると、Googleに囲い込まれちゃうよ?という記事。
ただ、どうせGAE使うなら大規模にスケールしてくれるようなアプリケーションを書かないと意味がない(面白くない)ですし、そういうアプリケーションを書いちゃったらどうせGAE以外に動かせるプラットフォームがないという現実もあります。
なので、個人で使う分にはあんまり気にしなくて良いんじゃないかなあ。法人で使うものではないとは思うけど。
Datastore API
GAEのキモと言えば、データストアAPI。他のAPIはそんな大したことはなくて(多分)、データストアがGoogleのBigTableによって篦棒にスケールするというのが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迄の文字列を入れることができる。
- 文字コードは? UTF-8なのかUTF-16なのか。→500byteじゃなくて500文字の間違いぽい
- 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
- トランザクションは単一のentity groupしか扱うことができない。
- トランザクション内では検索式を使うことができない。
- keyを直接指定して検索するのは可能。
- トランザクション内では1回しか値の追加/更新ができない。
- トランザクション関数はデータストア以外に副作用を及ぼしちゃダメ。
- トランザクションが失敗したときに、自動的に複数回実行される可能性があるため。
インデックスはどういうことかというと、
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)までが検索式の解になる、という寸法。