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について。

0 件のコメント:

コメントを投稿