FC2ブログ

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

[Google App Engine] クエリの ORDER BY 制限を回避する

Google App Engine の仕様に苦しめられたので、今日自分がやったことを簡単にまとめる。

1. クエリの制限
Google App Engine のデータストアでは、実行できるクエリに制限がある。

主な制限は以下のとおり
・不等式が使用できるのは1つのプロパティ(DBで言うところの列名)に限られる
# これはOK
SELECT * FROM Test WHERE date = DATE('2010-12-01') AND cost >= 100
# これは BadFilterError 例外が発生する
SELECT * FROM Test WHERE date >= DATE('2010-12-01') AND cost >= 100

・不等式を使用したクエリで ORDER BY を使用する場合、ORDER BY の先頭は不等式を使用したプロパティにする必要がある
# これはOK
SELECT * FROM Test WHERE date >= DATE('2010-12-01') ORDER BY date
# これは BadArgumentError 例外が発生する
SELECT * FROM Test WHERE date >= DATE('2010-12-01') ORDER BY item

自分が作っているプログラムで、「月単位で item の cost をまとめる」みたいなことをする必要があった。
普通のDBでは、以下のようなプログラムにすればよい。
from google.appengine.ext import db
# test データモデル
class Test(db.Model):
  item = db.StringProperty(required=True)
  cost = db.IntegerProperty(default=0)
  date = db.DateProperty(required=True)
(中略)
# 月単位で item の cost をまとめる処理
query = db.GqlQuery("SELECT * FROM Test WHERE date >= DATE('2010-12-01') AND date < DATE('2011-01-01') ORDER BY item")
for entity in query:
  # entity.item が前の値と同じなら entity.cost を加算
しかし Google App Engine では、上記の ORDER BY 制限のため BadArgumentError 例外が発生する。

2. ORDER BY 制限を回避する
ORDER BY の制限だけなら、
query = db.GqlQuery("SELECT * FROM Test WHERE item != '' AND date >= DATE('2010-12-01') AND date < DATE('2011-01-01') ORDER BY item")
で回避できそうだが、これは上記の「不等式が使用できるのは1つのプロパティ」に反するので BadFilterError 例外が発生する。

仕方なくリストを使ってソートした。
# クエリから ORDER BY を削除
query = db.GqlQuery("SELECT * FROM Test WHERE date >= DATE('2010-12-01') AND date < DATE('2011-01-01')")
entityL = [] # ソートするための暫定リスト
for entity in query:
  # 値として有り得ない文字列を使って、値を結合
  entityL.append(entity.item + '$$$$' + str(entity.cost))
entityL.sort()
for test in entityL:
  # 文字列を分割して item と cost を取得
  testSep = test.split('$$$$',1)
  item = testSep[0]
  cost = int(testSep[1])
  # item が前の値と同じなら cost を加算
エンティティ数が少ないからこれで何とかなったが、数が多い場合は処理時間や負荷が心配になる。

クエリで不等式を使わないよう、プロパティを追加する方法も考えられる。
# month プロパティを追加
class Test(db.Model):
  item = db.StringProperty(required=True)
  cost = db.IntegerProperty(default=0)
  date = db.DateProperty(required=True)
  month = db.StringProperty()
(中略)
# データ登録時に年月を設定
test.month = '%4d%2d' % (test.date.year, test.date.month)
test.put()

この場合、クエリで ORDER BY が使用できるので以下のように簡潔になる。
query = db.GqlQuery("SELECT * FROM Test WHERE month = '201012' ORDER BY item")
for entity in query:
  # entity.item が前の値と同じなら entity.cost を加算

ここらへんを考慮してデータ設計しなきゃいけないってことだな。
Google App Engine のデータストアには他にもいろいろ考慮すべき点がありそうだから、ちゃんと調べるか。
スポンサーサイト

コメント

コメントの投稿

非公開コメント

プロフィール

himax64

Author: 南西
30代後半の無職です。
就活もせずダラダラ生きてます。
作ったもの

最新記事
人気記事
検索フォーム
カテゴリ
月別アーカイブ
最新コメント
最新トラックバック
RSSリンクの表示
QRコード
QRコード
カウンター
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。