2011年5月 Archives

« 2011年3月 2011年6月 »

GAEのBigtableから複数の検索結果を取得してみる

例えば下記のようなエンティティがあるとして..

nameage
Spike Spigel0
Jet Black1
Faye Valentine2
Spike Spigel3
Jet Black0
Faye Valentine1
Spike Spigel2
Jet Black3
Faye Valentine0
Spike Spigel1

ここから
ageが2以上の"Spike Spigel"、
ageが3以上の"Jet Black"、
aqeが2の"Faye Valentine"
を検索し列挙したい場合、上記3つのクエリを実行し、結果を結合する。

具体的なコードはこんな感じになる
a1 = Accounts.all().filter("name =", "Spike Spigel").filter("age >=", 2)
a2 = Accounts.all().filter("name =", "Jet Black").filter("age >=", 3)
a3 = Accounts.all().filter("name =", "Faye Valentine").filter("age ==", 2)
ac = chain(a1,a2,a3)    
結合しておけばその後のループ処理などで何かと便利。(特にページングが発生する場合など)
for a in ac:
    self.response.out.write(a.name+" age"+str(a.age))
    self.response.out.write("
")
念のため付け加えると、
a1 = Accounts.all().filter("name =", "Spike Spigel").filter("age >=", 2)
a1.filter("name =", "Jet Black").filter("age >=", 3)
a1.filter("name =", "Faye Valentine").filter("age ==", 2)
と処理した場合は絞り込み検索になってしまうので、期待する結果は得られない。

MySQLならSQL文で「OR」を使えばOKのはず。
しかしGAEのGQL及びQueryにはそういった機能は無さげなので個別に検索した後、結合しなければならない。
もしかするともっとエレガントな方法があるかもしれないので、ご存知の方がいらっしゃったら是非教えてください。

[ソースをDownload]

[追記]
イテレータの結合は標準ライブラリで可能です。
こちらのページが参考になりますよ。
itertools - 効率的なループ処理のためのイテレータ関数

http://www.python.jp/doc/release/library/itertools.html#itertools.chain

結合したQueryへのfetch()的アクセス

[ご注意!]
以下のエントリーは、状況により大きく負荷がかかる場合があります。詳しくはこちらをお読みください

前回のエントリーでBigtableから複数のQueryを結合する方法を紹介ました。 この方法は非常に便利ですが、chain()メソッドにより結合したQueryはitertools.chainオブジェクトに変換されてしまうのでQuery固有のメソッドが使えません。

うっかりfetch()とかcount()を実行すると
AttributeError: 'itertools.chain' object has no attribute 'count'
とお叱りを受けます。

特にfetch()が使えないのは致命的に思えます。 例えば1000件の検索結果の中から200番から210番だけ取り出したい場合に途方に暮れるじゃないですか。

で、ここでキラーメソッドの登場です。 itertools.islice()メソッドです。

islice('ABCDEFG', 2, 4) 
と実行するとC,Dを返すイテレータを生成してくれます。 Queryに対して使う場合はこんな感じです。
q1 = HogeModel.all().filter("name =", "Spike Spigel")
q2 = HogeModel.all().filter("name =", "Jet Black")
q3 = HogeModel.all().filter("name =", "Faye Valentine")
qchain = chain(q1,q2,q3)    

 #10番から20番のQuery結果を得る
islice(qchain,10,20)
これにより実質的にfetch()メソッドが利用可能となり、万々歳なわけですが、ホントにitertoolsはスゲー奴ですね。

ちなみにitertoolsのソースコードを見てやろうと/System/Library/Frameworks/Python.framework/Versions/ の中を探してみたけど見つかりませんでした。いけずな奴です。

ActionScriptにはビルトインなイテレータが存在しないし、 実際のコーディングでもワザワザ定義して使う事は稀だと思います。 最近、周囲のFlasherさんたちがGAE/Pyに参入し始めていますが、僕のようにイテレータに不慣れな人も結構多いんじゃないのかと思ったりする今日この頃でした。

次回は実際にchain()、islice()を使って複数Queryの検索結果をPagenateさせてみたいと思います。

[お詫びと訂正]結合したQueryへのfetch()的アクセス

前回のエントリーでqueryをitertools.chain()してitertools.islice()すると複数のqueryを結合してfetch()できるよ。と書きましたが、その後の検証で非常に非効率な処理が走る事が判明したので訂正します。
具体的にはitertools.islice()でイテレーターをスキップさせると内部的にはスキップさせた分だけnext()が走るみたいです。
つまり
islice('ABCDEFG', 2, 4) 
はC、Dを返すのですが、A、Bに関して戻り値に含まれないだけで内部では走査されちゃうようです。

例えば
q1 = HogeModel.all().filter("name =", "Spike Spigel")
q2 = HogeModel.all().filter("name =", "Jet Black")
q3 = HogeModel.all().filter("name =", "Faye Valentine")
qchain = chain(q1,q2,q3)    

 #1000番から1020番のQuery結果を得る
islice(qchain,1000,1020)
を実行した場合、queryが1000回、next()を実行する事になります。 という訳でこの方法はトンでもないバッドノウハウだったので、この場を借りて深くお詫びいたします。

ちなみに余談ですが、
q1 = HogeModel.all().filter("name =", "Spike Spigel")
q1[1000:1020]
と実行した場合はfetch()が走り、next()をぶん回す事は無いようです。

RSS + Contuct

  • rss
  • email

Credit

Copyright (C) 2007 kappa-lab.com.
All Rights Reserved.