SyntaxHighlighter

2012年11月25日日曜日

Google App Engine Python NDB を使ってみた。(6)

NDB Queries(2)


前回の続き。
Structured Propertyに対するフィルタリング

QueryはStructed Propertyのフィールドの値に対して直接フィルタリングする事が出来る。
例えば、cityがAmsterdamのContactオブジェクトに対してQueryを定義すると下記のとおりになる。
複合したフィルタリングを用いたい場合は、以下のように定義する。
上記で抽出されるContactはcityがAmsterdamかほかのcityでadressがSpear Stになる。しかし、少なくとも等価のフィルターになる。もし単一の結果を返却したい場合は以下のように定義する。
このテクニックを使うと、プロパティのサブエンティティがNoneと等価の場合はクエリで無視される。プロパティがデフォルト値を持っている場合にQueryで無視したい場合は明示的にNoneを設定する必要がある。それか、Queryで指定しているフィルターをデフォルト値と一致させる必要がある。例えば、Addressモデルがcoutryプロパティのデフォルト値をdefault='us'と設定している場合、上記例では、countryが'us'のContactのみを返す事になる。他のcountryを持つContactを抽出したい場合は、filterをAddress(city='San Francisco', street='Spear St', country=None).のようにする必要がある。

サブエンティティのプロパティのどこかにNOneが設定されている場合はそれらは無視される。従って、Noneが設定されてるサブエンティティへのfilterは意味をなさない。

・Projection Queries

Queryにprojectionを指定することができる。これは検索したいプロパティのリストで、もしprojectionを指定した場合は、NDBはそれぞれのEntityから全ての値は取得しない。
projectionで指定したプロパティの値しか取得しない。これはQueryのindexより取得するものなので、indexされている必要がある。数個の大きいEntityから小さいプロパティのいくつかが抽出したい場合に有用であり、Fetchが効率的に行われる。もしprojectionを指定せずに行うとndb.UnprojectedPropertyError.例外を送出することになる。

データストアよりArticleを取得する例では、分類学者は彼らの記事に適用されているタグの作成者を知りたいと仮定すると、必要なのはauthorとtagsの情報だけなので、projectionを以下のように指定する。
Repeated Properties:例では、Aritcle.tagsはrelated propertyとなっている。これは、repeated property はインデックスされていて、(projectionはindexから取得する)projectionクエリは格納されたentityへ複数のentityをフェッチする。もしArticle(author='Guido', tags=['python', 'jython'])のようなEntityが存在した場合に、projectionクエリはArticle(author='Guido', tags=['python'])とArticle(author='Guido', tags=['jython'])の二つのEntityを返却する。

Structured Properties:インデックスされたstructured propertyのサブプロパティを射影する事ができる。下記のように射影を指定することができる。


・String値によってPropertyを指定する

時々String値によってフィルター、順序するプロパティをクエリーで指定したい場合があるが、例えば、ユーザーが指定したtags:pythonのようなサーチクエリを使いたい場合は以下のようにする。

もし、ModelがExpndo Modelの場合は、GenericPropertyを使ってフィルタリングする事ができる。Expandoは動的プロパティを使うので、
このGenericPropertyはExpando Modelでなくても使用する事ができるが、もし自分で確保したプロパティのみを指定させたい場合は、_properties属性を使用することもできる。
または、getattr()を使用する事もできる。
getattr()と_propertiesの違いは、getattr()はPython上のproperyの名前を使い、_propertiesはインデックスされたデータストアのプロパティの名前を使用する。下記のように定義した場合のみ、そのような挙動をする。

これは、Python上ではtitleだが、データストア上はtとなる。

以下のようなアプローチでも使用することができる。
・Query Iterators

Queryが処理されている間は、iteratorオブジェクトとして保持されている。(アプリケーションでこれらが使用される場合はほとんどない。fetch(20)のような方が普通かもしれない)基本的には以下の2点でiteratorオブジェクトを取得することができる。

・QueryからPythonのiter()関数を呼んで使う場合
・Queryオブジェクトのiter()メソッドを呼ぶ場合

一つ目は、 PythonのforループをつかってQueryが終わるまでループする方法
二つ目は、Queryオブジェクトのiter()メソッドを使用する方法で、iteratorの振る舞いに影響を与える為にiteratorにオプションを渡す事ができる。例えば、keyのみのクエリーをforループで使用する場合。
Query iteratorsは他に以下のような便利なメソッドがある。
__iter__(), next(), has_next(), probably_has_next(), cursor_before(), cursor_after()

・Query Cursors

クエリカーソルは、クエリ内の再開ポイントを表す小さな不透明なデータ構造です。これは、その時点での結果をユーザーに見せる場合に便利である。また、長いデータなどで、一時停止して処理したい場合などにも使える。典型的な方法では、Queryのfetch_page()メソッドを使用する方法で、fetch()と同じように動作するが、返却値としてresults, cursor, moreを返却する。moreフラッグは更に結果が存在する事を示し、UIはこれを、例えばNextPageボタンをリンクとして設置したりできる。後続のページをリクエストする場合は、返却値のcursorのfetch_page()を呼ぶ。

したがって、ユーザーにその時点の全ての検索結果を表示する場合に以下のようにコーディングすることができる。

urlsafe()とCursor(urlsafe=s)をシリアライズとでシリアライズに使用する場合には注意が必要。これは、クライアントに一回のリクエストでcursorを渡すことができるのと、後のリクエストにデシリアライズして使用することができる。

注意:fetch_page()メソッドは結果がそれ以上無い場合もcursorを返却するが、それは保証がない。Noneが返却されるだろう。moreフラッグも注意が必要でこれは、iteratorのprobably_has_next()を使用しているので、時々、Trueでも次のページが空の場合もある。

いくつかのNDB Queryは、cursorをサポートしていないが、これを解消することができる。QueryでIN,ORまたは!=を使用すると、キーで順序指定されない限りcursorとして動作しない。
もしアプリケーションで、順序を指定しないでfetch_page()を呼んだ場合は、BadArgumentErroが送出される。User.query(User.name.IN(['Joe', 'Jane'])).order(User.name).fetch_page(N)これではエラーになるので、User.query(User.name.IN(['Joe', 'Jane'])).order(User.name, User.key).fetch_page(N)とすれば問題ない。

pagingの代わりにqueryの結果を取得する場合は、queryのiter()メソッドを正確なポイントで使用するとよい。ただ、produce_cursor=Trueをiter()に渡して上げる必要がある。正しい場所で、iteratorを使用した後は、cursor_afterを呼ぶ必要がある。(または、同様に、cursor_beforeをcursorの前に呼ぶ)cursor_after()またはcursor_before()を呼び出すと、cursorを抽出する為に、クエリの一部を再実行すると、ブロックされるかもしれないので、
注意が必要。

cursorをさかのぼって結果取得する場合は以下のようにする。
・それぞれのEntityへ呼び出す関数をMappingする

Account Entityに関連するMessageをQueryによって取得したいと仮定すると以下のようにコーディングすることができる。
しかしながら、これは非効率である。以下のようにcallback関数を使用することができる。

このバージョンでは、並列化することができるので、forループよりすこし早くすることができる。しかし、get()メソッドでcallback()を使用しても同期処理になってしまうので、asyncronous getを使用するといい。

・GQL

NDBで以下のようにGQLを使用することもできる。
または、

bind()関数は元と同じ新しいqueryを返却する。

SQLに慣れている場合、GQLを使用して誤った仮定には注意。 GQLはNDBのネイティブクエリAPIに変換される。これは、それらがデータベース·サーバーに送信される前にAPIの呼び出しがSQLに変換され、典型的なオブジェクト·リレーショナル·マッパー(SQLAlchemyのか、Djangoのデータベースのサポートなど)とは異なる。 GQLは、データストアの変更(挿入、削除または更新)をサポートしていないので、クエリのみをサポートしている。

今回は、Queryについて2回にわたって紹介した。
しかし、翻訳がつたないのと理解不足があるので、
いずれは、実際に使用してみた例を紹介したいと思う。

次回は、Transactionsについて。

2012年11月18日日曜日

Google App Engine Python NDB を使ってみた。(5)

NDB Queries

アプリケーションはクエリーを使用したフィルターの基準によって指定した検索にマッチするEntityをDatastoreから検索できる。

・概要
アプリケーションはクエリーを使用したフィルターの基準によって指定した検索にマッチするEntityをDatastoreから検索できる。例えば、アプリケーションはクエリーを使ってTrackとGuestbooksを一つのguestbookから日付順に検索できるようにしたままに出来る。

いくつかのクエリーは他より更に複合にできるが、Datastoreはインデックスを事前に構築しておく必要がある。これらの事前に構築するindexはindex.yamlというconfiguration fileで定義できる。開発サーバーでは、クエリーを実行する為に必要なindexを指定していなくても動作するのだが、これは開発サーバーが自動的にindex.yamlを追加するからである。しかし実際のWebSiteでは指定していないindexが必要になると失敗する。したがって、典型的な赤井発サイクルでは、開発サーバーで新しいクエリを試して自動的に更新されたindex.yamlをWebSiteのindex.yamlとして更新する。 
また、index.yamlをアプリケーションとは別に更新できる。
もしDatastoreにたくさんのEntityが存在するとき、インデックスを作成するのに長い時間がかかる。今回の場合、新しいindexを使用するコードをアップデートかける前に、index定義を更新するのが賢明。アドミンコンソールからindex構築の状況を確認することができる。

Datastoreは一致条件に (the==operator)と比較に(<,<=,> and >= operatros)をサポートしている。複数のフィルターをANDを使用する事によって結合できるが、いくつかの制限がある(以下を参照)

さらに、APIは!=とグループのフィルタリングをORで結合できるのと、INが使える。INはPythonのinのようにlistの中で一致する要素を検査する。これらは1対1でDatastoreのオペレーションではない。従って、相対的に少し風変わりで遅い。これらは結果のストリームをインメモリでマージする実装をしている。p!=vは実装上p<v OR p>vとなっている。

制限:データストアはいくつかの制限を強制している。これらを違反すると例外を送出する原因となる。例えば、複数のプロパティーに対する不等号のフィルタリングをたくさん結合したり、異なるプロパティーでソートを行う不等号の結合フィルターは現在全て禁止されている。複数のプロパティを参照するフィルターもまた時々セカンダリーindexの設定が必要になる。

非サポート:Datastoreは一部の文字を使用する検索、大文字小文字区別なし、また全文検索はサポートしていない。これらの大文字小文字区別なしと全文検索でさえ実装するにはcomputedプロパティーを使用する。

・Propertyの値でフィルタリングする
通常は与えられたkindの全てのEntityを検索したくない。だいたいはいくつかのプロパティーに対して範囲を指定したい。
プロパティはフィルタ
ーを表すクエリーによって操作できる。例えば、useridが42をもつEntityを抽出すると以下のような表現になる。

もしuseridがAccount内で一つである事が確実であれば、useridはキーとして扱うだろう。Account.get_by_id()にした方が早いからである。
NDBは以下のオペレーションをサポートしている。
不等のフィルタリングを行う場合は、以下のような文法を使える。
これはuseridが40以上のEntityを探すクエリーとなる。

!=とINは実装上、他のオペレーションとの複合である。また、これらは少し風変わりな説明である。

複合フィルターは以下のように指定する。

この複合フィルターの引数は、useridが40以上から50未満のEntityを返すクエリーとなるが、前述のとおりDatastoreは、複数のプロパティで不等号を使用したクエリーを拒否する。

代わりに全体のクエリーフィルターを一つで表現する。多分もっと便利にクエリーを積み上げることが出来る事を見つけるであろう。
qry3は前述の例のqrtと等価である。このクエリーオブジェクトは不変であり、qry2はqry1に影響を与えず、また、qry3もqry1とqry2に影響は与えない。

・!= と IN のオペレーション
!=(不等)とIN(メンバーシップ)オペレーションはORを使った他のオペレーションとの複合である。まずは以下。
は実装上
例えば、
これは以下と等価

Note:多分驚くであろうが、このクエリーは、perlタグを含まないEntityを探すよりむしろ最低でも一つのタグがperlでない全てのエンティティーを探している。例えば次のエンティティはそのタグの一つとしてperlを持っているのみも関わらず、結果に含まれるだろう。
しかしながら、これは含まれないであろう。
perlと同じタグが含まれていないエンティティを抽出する為の方法はない。
また、INオペレーションについては、以下の表現ができる。

これはリストの値のメンバーシップを検索する。実装上は以下の通り
例は下記
以下とも等価である。

ORをしようすると重複した結果は得られない。

・Repeated Propertiesへのクエリー
Articleクラスはrepeatedプロパティへのクエリの例としても先行したセクションとして定義した。特にフィルターのように、

上記Article.tagsはrepeatedプロパティにも関わらず、一つの値を使った。すると、このプロパティはlistオブジェクトと比べることが出来なくなる。また、フィルターのように、

この場合は、tagsのプロパティーがlistの['python','ruby','php']を持っているEntityを探す事とは全く違う。これはtagsの値が['python','ruby','php']のうち最低でも一つが含まれるEntityを探す。
Noneはクエリーで検索する事が出来ない。

・ANDとORを複合して使う
ANDとORオペレーションをネストして使う事が出来る。


ORの実装の為に、ORを複合しすぎたクエリーは例外とともに失敗するであろう。これらのフィルターは下記のように、一つのレベルのANDとネストされたクエリーの最上位にあるOR
を表現する為に正規化される。この拡張は、既に!=とINに与えられた拡張とともに、ブール式のための論理和標準形を得るための標準的な規則を使用する。要するに、上記の例の正規化された形は、(非公式の表記法を使用する)


注意:
この正規化は複合の爆発の可能性がある。

・ソート順序を指定する
order()メソッドを使用してクエリーの結果の順序を指定する事が出来る。このメソッドはlist引数を受け取り、それぞれ、プロパティーのオブジェクトかまたは、それの昇順降順を指定できる。

この検索は、messageプロパティーの値で昇順にしたGreeting Entityを検索する。
連続したmessageプロパティをuseridを降順にソートした結果を返却するためには、複合のorderを呼び出せば可能である。
order()でフィルタを組み合わせたとき、データストアは、特定の組み合わせを拒否する。特に、不等号フィルタで最初の並べ替え順序を(もしあれば)を使用した場合、フィルタと同じプロパティを指定する必要がある。また、時々セカンダリインデックスを設定する必要がある。

・先祖クエリ
先祖クエリはクエリの結果を先祖から制約する。
ソート順とフィルターの複合で使用できる。
同じ先祖のEntityへの操作となるので、これは特にtransaction内で便利である。

・Queryオブジェクトの属性
Queryオブジェクトは以下のような読み込み専用の属性を持っている。
kind str None Kind name (usually the class name)
ancestor Key None Ancestor specified to query
filters Filter Node None Filter expression
orders Order None Sort orders

str()とかrepr()を呼んで内容を表示するときに表現できる。


今回はQueriesについての前半を記載した。
ここまでは特にNDBに特化した機能などはほとんどなかったが、
次回もQueriesについてだが、NDB特有のmapなど登場する予定。

2012年11月11日日曜日

Google App Engine Python NDB を使ってみた。(4)

プロパティーのサブクラスを書く(Writing Property Subclasses)


プロパティクラスはサブクラス化できるよう設計されている。しかし普通は既存のプロパティクラスの方が簡単にサブクラス化できる。
全ての特別なプロパティ属性とpublicと考えられる属性もアンダースコアから始まる名前を持っている。この理由はStructuredPropertyがネストされたプロパティーのアンダースコアの付いていない属性を使用するからで、これはサブプロパティにクエリを指定する為に不可欠である。

プロパティクラスと既存のサブクラスは構成可能な(またはスタッカブル)検証および変換APIを使用してサブクラス化する事が出来る。これらはいくつかの用語定義を必要とする。

・ユーザーの値(user value)は、アプリケーションが使用する標準の属性を持つエンティティに設定されるまたは、アクセスされるような値。
・基本の値(base value)はデータストアからシリアライズまたはでシリアライズされるような値。

シリアライズ可能な値とuser valueの間で特定の変換を実装する場合は二つのメソッドを実装した方がよい。to_base_type()と_from_base_type()がそれで、これらは構成可能なAPIという意味でsuper()メソッドは呼ばない方がよい。

APIは今までよりも洗練されたuser-baseな変換を行えるstacking classesをサポートしている。user-to-base変換はbase-to-user 変換でより洗練から更に洗練へ変換している間に更に洗練からより洗練へ行く。例えば、BlobProperty,TextPropertyとStringPropertyの関係や、例えば、TextPropertyはBlobPropertyを継承している。それは必要な動作のほとんどを継承しているので、そのコードは簡単なもの。
加えて、_to_base_type()と_from_base_type(),_validate()メソッドもまた、変換APIである。

バリデーションAPIはuser valuesへの緩い制約と厳密な制約とを区別する。 緩い制約の値は厳密な値の集合の上位集合である。_validate()メソッドは緩い値をとり、また必要であれば、厳密な値へ変換する。これは、プロパティの値を取得する場合に、唯一厳密な値が返されるときにプロパティー値を設定するときは、緩い制約の値が受け入れられていることを意味する。もし変換が必要ない場合は、_validate()は多分Noneを返す。もし外部からの引数が、緩い値を設定した場合は、_validate()は例外を送出すべきでTypeErrorかdatasore_error.BadValueErrorが好ましい。

_validate(),_to_base_Type(),_from_base_type()の操作が必要が無い場合:
・None:これらはNoneと一緒に呼び出せない。(また、Noneを返却する場合、それは変換が必要ない事を意味する)
・Repeated values:基盤で_from_base_type()か_to_base_typeをそれぞれのアイテム毎に呼ぶ。
・base valuesからuservaluesを区別する場合:基盤では変換APIがよびだされる。
・比較する場合:比較演算は、オペランドに_to_base_type()を呼び出す。
・base valueとuser valueを区別する場合:基盤で_from_base_type()がbase valueと一緒に呼び出される事と、_to_base_type()がuser valueと一緒に呼び出される事を保証している。

例えば、本当に長いintegerを必要とすると仮定する。標準のIntegerPropertyは64bitのintegerしかサポートしていない。長いintegerを保存する場合はStringとして格納しなくてはならない。変換を操作するpropertyクラスが良い。アプリケーションでそのプロパティを使うと恐らくこのようになるであろう。

これはシンプルで素直だ。またこのデモは標準のプロパティのオプションも使用している。
LongIntegerPropertyクラスの所有者はこれらの作業を取得する任意の定型を記述するひつようがないので、喜んでいるでしょう。他のプロパティのサブクラスを定義するのは簡単だ。下記が例

例えば、ent.abc = 42をエンティティのプロパティに設定した際_validate()メソッドが呼びだされて、値はエンティティに格納される。また、エンティティをDatastoreに格納する際は_to_base_type()が呼び出され、string値に変換する。そして、その値はStringProperyによってシリアライズされる。Datastoreからエンティティを読み戻した場合に逆算が起こる。
StringPropertyとPropertyクラスはシリアライズやデシリアライズ、デフォルト値の設定、repeated propertyの値を操作するような他の世話を一緒にする。

不等号を扱うにはさらに仕事とが必要である。下記の例では、固定長文字列として整数を格納した値の最大サイズを課している。

これは、LongIntegerPropertyと同じ方法で使用され、整数をプロパティのコンストラクターで設定されることを期待している。BoudedLongIntegerProperty(1024)のように。

他のプロパティの型も同じような方法でサブクラスにできる。

このアプローチはstructured dataでも動作する。日付の範囲を表すPythonのFuzzyDateクラスを持っていると仮定する。それはfistとlastのフィールドを持っていて、日付の範囲を開始と終了で格納している。

StructuredPropertyから派生しているFuzzyDateProperyを作成することが出来る。しかし残念ながら、後者は昔ながらのPythonクラスでは動作しない。Modelのサブクラスが必要である。中間モデルとしてModelのサブクラスを定義しよう。

次に、FuzzyDateとFuzzyDataModelを変換する為にFuzzyDateモデルのmodelclassの属性のコードと、_to_base_type()と_from_base_typeメソッドを定義したSturucturedProperyのサブクラスを定義する。

アプリケーションでは以下のように使用する。


FuzzyDateProperyにFuzzyDateオブジェクトのようにdateオブジェクトを格納したいと仮定すると、_validate()メソッドは以下のように変更する。

以下のようにFuzzyDateProperyクラスの代わりに使用する事も出来る。


MaybeFuzzyDateProperyフィールドを割り当てるときにMaybeFuzzyDateProperty._validate()とFuzzyDateProperty._validate()が両方呼び出される。同じ事が_to_base_type()と_from_base_type()に適用される。スーパークラスとサブクラス内のメソッドは暗黙的に結合される。

今回は自前でプロパティを用意したい場合の方法を紹介した。
次回はNDBのQueryについて。

2012年11月3日土曜日

Google App Engine Python NDB を使ってみた。(3)

【NDB プロパティについて】


NDBのエンティティはプロパティの定義ができる。
エンティティはデータを保持するために用いるPythonのクラスと似ている。
それらはデータベースのスキーマにも似ている。

・最初に
典型的なアプリケーションはデータモデルをModelクラスを継承したクラスにプロパティを定義する。

上記はusername, userid,emailがAccountのプロパティとなっている。
いくつか違う型のプロパティを用いているが、手軽に日付、時間を表す事が出来て、自動更新機能も付いるプロパティもある。
アプリケーションはプロパティに特別な振る舞いを指定でき、それらは簡易な検証や、デフォルト値設定、インデックスの変更などができる。
モデルは複合プロパティを持つ事が出来る。また、リストのようなRepeated propertiesも設定できる。構造体プロパティはオブジェクトのように、読み込み専用計算プロパティは関数により定義できる。プロパティに多方面の他のプロパティを定義することが簡単にできる。Expandoモデルは動的にプロパティを設定できる。

・プロパティの型

NDBは以下のプロパティの型をサポートしている。

IntegerProperty:64-bitのInteger型
FloatProperty:倍精度のfoating-point numberの型
BooleanProperty:Boolean値
StringProperty:Unicodeの文字列で500文字以内。インデックスされる。
TextProperty:Unicodeの文字列で長さに制限はないが、インデックスできない。
BlobProperty:byte配列を格納する。もし500文字以内の文字列であれば、インデックスされる。インデックスしないのであれば、長さに制限はない。圧縮もできる。
DateTimeProperty:日付や時間
DateProperty:日付
TimeProperty:時間
GeoPtProperty:位置情報。これはbdb.GeoPtオブジェクトでlatとlonをどちらもfloatで持っている。ndb.GeoPt(52.34, 4.88)やndb.GeoPt("52.37, 4.88")などで生成できる。
KeyProperty:DatastoreのKey、kind="カインド"を指定すれば、キーの割当にいつもカインドを示すことが必須にできる。それは文字列かModelのサブクラスであろう。
BlobKeyProperty:古いdb API(BlobReferenceProperty)に対応しているがプロパティの値はBlobKeyの代わりにBlobInfoとなる。BlobInfoは使用しているBlobInfo(blobkey)から構築できる。
UserProperty:ユーザーのプロパティ
StructuredProperty:値により内部的に別のモデルのカインドを含む
LocalStructuredProperty:StructuredPropertyに似ているが、ディスク上では不透明なblobとして表現され、インデックスはされない。圧縮可能。
JsonProperty:Pythonのjsonモジュールを使ってシリアライズしたオブジェクトを設定できる。JSONシリアライズしてblobとしてdatastoreに格納される。デフォルトでインデックスはされない。圧縮可能。
PickleProperty:pickleプロトコルを使ってシリアライズされたPythonのオブジェクトを設定できる。pickleシリアライズされたデータをblobとしてdatastoreへ格納する。デフォルトでインデックスはされない。圧縮可能。
GenericProperty:汎用的な値で、主にExpandoクラスで使われる。ただし、明示的に使用可能な型はint,long,float,bool,str,unicode,datetime,Key,BlobKey,GeoPt,User,Noneとなっている。
ComputedProperty:UDFによって他のプロパティより計算された値を設定できる。

いくつかのプロパティはオプションの引数のcompressedを使用する事ができる。もし、プロパティがcompressed=Trueで設定されていた場合はデータはgzip圧縮されてディスク上に格納される。これにより格納容量は小さくできるが、エンコードとデコードにCPUを消費する。

・プロパティのオプション

ほとんどのプロパティの種類は、いくつかの標準的な引数をサポートしている。
第一引数はDatastoreネームを指定する任意の引数。これによってカプリケーションの観点よりもデータストアに別の名前を与える事ができる。一般的な使用方法はデータストアのスペースを減らす目的で使う。データストアは短縮されたプロパティ名を使う。以下が参考。
意味を持つ一文字など設定するとよい。


これは特にrepeated property でEntityごとに多くの値を期待している場合
に有効。
さらに、ほとんどのプロパティはkeyword argumentsをサポートしている。

詳細はArgument&Typeを参照

・Repeated Properties

どのプロパティもrepeated = Trueにするとrepeated propertyになる。
そのプロパティは基クラスのリストの値になる。
下記が参考。プロパティの値はIntegerProperty のリストとなる。
データストアはそのようなプロパティに複数の値が表示される場合がある。独立したインデックスレコードは各値の為に作成されるこれはクエリセマンティクスに影響を与える。
以下が例。

以下がEntity


tagsプロパティの検索をする場合にこのEntityはpythonとrubyどちらも満たす。

もし、repeated propertyを更新する場合は、新しいリストを割り当てるかその場で既存のリストを変更する事が出来る。新しいリストを割り当てた場合は即時に型チェックが行われる。例えば[1,2]をart.tagsに割り当てると例外が起こる。もし、既存のリストを更新した場合は即時に型チェックは行われない。代わりにDatastoreへ書き込むときに型チェックが行われる。
データストアはrepeated propertyのリストの順番を保持する。

・日付と時間のプロパティ

日付と時間は3つのプロパティの型が有効である。
・DateProperty
・TimeProperty
・DateTimeProperty

これらの値はPythonのdatetimeモジュールのdata, time, datetime,クラスに対応する型である。3つの中で最も一般的なのはDateTimePropertyでカレンダーの日付と日付の時間を意味する。また時折便利な特別な使い方として、ちょうどの時間が必要な場合(例えば誕生日、ミーティングの時間)がある。技術的な理由としてDatePropertyとTimePropertyはDateTimePropertyのサブクラスである。ただこの継承関係に依存しない方がよい。また、この継承関係は基本になっているクラスdatetimeの継承関係とは違う。

Note:App EngineのクロックタイムはUTCで設定されている。もしPOSIX timestampsかtime tuplesに変換したデータで現在時間を使おうとする事に関係する。明白なタイムゾーンの情報をDatasotereに格納する際に与えられない。もし、ローカル時間などで現在時刻を使うときは注意が必要である。

それぞれのプロパティは下記のkeyword argmentを使う事ができる。
auto_now_add: Entityが作られた際に自動的に現在時刻が設定される。
auto_now: Entityが更新された際に自動的に現在時刻が設定される。
これらのオプションはrepeated=Trueと複合することができない。どちらもデフォルト値はFalseでもし両方Trueにした場合はauto_nowが優先される。auto_now_add=Trueは上書きすることが出来るが、auto_now=Trueはできない。自動的な値はEntityが書き込まれるまで設定されない。これらのオプションはdynamic defaultsを提供していない。

・Structured プロパティ

構造化されたプロパティを設定する事が出来る。以下が例である。
Contactモデルクラスは住所をリストで持っている。

以下のような一つのEntityが作成される。


Entityを読み直すとContactエンティティを正確に再構築する。しかし、Adressインスタンスはモデルクラスと同じ構文を使用して定義されているが、Entityではない。これらはKeyを持っていない。Contactエンティティから独立して取得することができない。個々のフィールドへのクエリーとしてならアプリケーションで可能である。(Structured プロパティへのフィルタリングを参照)adress.type, address.streedとaddress.cityはDatastoreの観点では並行だが、NDBはこの側面を隠している。NDBでは関連するAdressインスタンスのリストとして構築している。
プロパティオプションを指定する事も出来る(indexedなど)この場合は、第二引数にDatastoreの名前を指定している。
もしStruectureプロパティへのクエリが必要ない場合はLocalStructuredPropertyを代わりに使用する事が出来る。Pythonコードとしては同じであるが、データストアは各AdressのBlobを見ている。例で作成したEntityは以下のようになる。

Entityは正しく読み戻される。このタイプのプロパティは常にインデックスは無いので、Adressをクエリで検索することは出来ない。

・Computed プロパティ

Computedプロパティは読み込み専用で、アプリケーションが提供する関数で計算した結果を設定することが出来る。計算された値はクエリとDatastoreビュアーの為に書き込まれるが、格納された値はDatastoreから読み戻された際は無視される。値は、関数が呼びだされた際に再計算される。以下が例。

格納されたEntityのプロパティの値は以下のとおり

もしnameをNickieに変更した場合にname_lowerはnickieを返す。

Note:もしクエリーで計算した値を使いたい場合は、ComputedPropertyを使う。もし派生バージョンのPythonコードを使いたい場合はregular methodを定義するか@propertyを使うとよい。

・ProtoRPC Message プロパティ

ProtoRPC API は 構造データの為にMessageオブジェクトを使用する。これらはRPCリクエスト、レスポンス、等を表現できる。NDBはMessageオブジェクトをEntityのプロパティとして定義できる。Messageサブクラスを定義すると仮定すると、

NDBのmsgprop APIを使用してDatastoreへEntityのプロパティとして格納できる。


もしクエリで検索したい場合はインデックスする必要がある。MessagePropertyへのindexed_fieldsを指定する事が出来る。
MessagePropertyは通常のプロパティオプションの全てはサポートしていない。
以下がサポートしているオプション
・name
・repeated
・required
・default
・choices
・validator
・verbose_name

Message プロパティはそれ自体にindexを使う事は出来ない。(フィールド名をしていすることで、indexできる)

ネストしたmessageも使用できる。


MessagePropertyは特別なオプションprotocolを指定できる。これはdatastoreへmessageオブジェクトをどのようにシリアライズして格納するかを指定できる。protocolの値はprotorpc.remote.Protocolsクラスの名前を使用できる。サポートするprotocolはprotobufとprotojsonでデフォルトはprotobuf
msgpropはEnumPropertyも定義できる。このプロパティはprotorpc.messages.Enumの値をエンティティとして格納できる。以下が例。


EnumPropertyはintegerとして格納される。事実としてEnumPropertyはIntegerPropertyのサブクラスである。また、既に格納されているEnumの名前を変更することは出来るが、再度採番することはできない。

EnumPropertyは以下のオプションをサポートしている。
・name
・indexed
・repeated
・required
・default
・choices
・validator
・verbose_name

今回はプロパティについて紹介した。
構造化されたプロパティをそのまま格納できるのは便利だと思った。
実は実際に使った事がないので、近いうちに使ってみたい。
次回は、プロパティのサブクラスについて。