Datastore クエリ数

注: 新しいアプリケーションを作成する際は、NDB クライアント ライブラリを使用することを強くおすすめします。NDB クライアント ライブラリには、Memcache API によるエンティティの自動キャッシュをはじめ、このクライアント ライブラリにはないメリットがあります。古い DB クライアント ライブラリを現在使用している場合は、DB から NDB への移行ガイドをお読みください。

Datastore のクエリとは、指定した一連の条件を満たすエンティティを Cloud Datastore から取得する処理です。

一般的なクエリは次のような要素で構成されます。

クエリを実行すると、指定した種類のすべてのエンティティの中で指定したすべてのフィルタを満たすものが取得され、指定した順序で並べ替えられます。クエリは読み取り専用として実行されます。

このページでは、Cloud Datastore からデータを取得するために App Engine で使用するクエリの構造と種類について説明します。

フィルタ

クエリのフィルタでは、取得するエンティティのプロパティキー祖先に対する制約を設定します。

プロパティ フィルタ

プロパティ フィルタでは、次の要素を指定します。

  • プロパティ名
  • 比較演算子
  • プロパティ値
例:

q = Person.all()
q.filter("height <=", max_height)

プロパティ値はアプリケーションで提供する必要があります。他のプロパティを参照することも、他のプロパティを基準として計算することもできません。エンティティが指定された名前のプロパティを持ち、その値がフィルタで指定された値との比較演算子で示された条件に合致する場合、そのエンティティはフィルタを満たすものとみなされます。

次の比較演算子を使用できます。

演算子 意味
= 等しい
< 次の値より小さい
<= 次の値以下
> 次の値より大きい
>= 次の値以上
!= 等しくない
IN 含まれる(指定されたリストのいずれかの値と等しい)

「等しくない」(!=)演算子では、実際には 2 つのクエリが実行されます。一方のクエリでは、他のすべてのフィルタは変更せずに、「等しくない」フィルタを「より小さい」(<)フィルタで置き換え、もう一方のクエリでは「等しくない」フィルタを「より大きい」(>)フィルタで置き換えます。それらの結果が適切な順序でマージされます。1 つのクエリに複数の「等しくない」フィルタを含めることはできません。またそのフィルタを含むクエリに別の不等式フィルタを含めることもできません。

IN 演算子も複数のクエリを実行します。具体的には、指定されたリストに含まれる項目ごとに、他のすべてのフィルタは変更せずに IN フィルタを「等しい」(=)フィルタで置き換えたクエリを実行します。それらの結果が、リスト内の項目の順にマージされます。クエリに複数の IN フィルタが含まれる場合は、IN リスト内の値の可能な組み合わせごとにクエリが繰り返されます。

「等しくない」(!=)または IN 演算子が含まれる 1 つのクエリでは、サブクエリの数が 30 個以下に制限されます。

主なフィルタ

エンティティのキーの値をフィルタリングするには、特殊なプロパティ __key__:

q = Person.all()
q.filter('__key__ >', last_seen_key)

異なる要素を比較する場合、キーは次の条件に従って次の順に順序付けられます。

  1. 祖先パス
  2. エンティティの種類
  3. 識別子(キー名または数値 ID)

祖先パスの各要素も同様に比較され、最初に種類(文字列)、次にキー名または数値 ID を基準として比較されます。種類とキー名は文字列であり、バイト値を基準として順序付けられます。また数値 ID は整数値であり、その数値の順に順序付けられます。同じ親と種類を持つエンティティが文字列のキー名と数値 ID を混用している場合、数値 ID を持つエンティティのほうがキー名を持つエンティティよりも優先されます。

キーを基準としたクエリではプロパティを基準としたクエリと同じようにインデックスを使用し、カスタム インデックスを必要とする場面も基本的に同じですが、いくつかの例外があります。キーに対して不等式フィルタを使用する場合、または昇順の並べ替え順を使用する場合はカスタム インデックスを必要としませんが、降順の並べ替え順を使用する場合は必要になります。すべてのクエリと同様に、開発用ウェブサーバーは、カスタム インデックスが必要なクエリをテストするときに、適切なエントリをインデックス構成ファイルに作成します。

祖先フィルタ

Datastore クエリは、指定した祖先でフィルタリングできます。これにより、返される結果には、その祖先の子孫のみが含まれるようになります。

q = Person.all()
q.ancestor(ancestor_key)

特別なタイプのクエリ

特別なタイプのクエリについては、次の点に注意してください。

種類を指定しないクエリ

種類も祖先フィルタも指定していないクエリは、アプリケーションのすべてのエンティティを Datastore から取得します。これには、統計情報エンティティBlobstore メタデータ エンティティなど、他の App Engine 機能によって作成、管理されるエンティティ(存在する場合)も含まれます。このような「種類を指定しないクエリ」では、プロパティ値に対するフィルタや並べ替え順序を含めることはできません。ただし、プロパティ名として __key__ を指定することで、エンティティ キーによるフィルタリングが可能になります。

q = db.Query()
q.filter('__key__ >', last_seen_key)

Python では、クエリによって返されるすべてのエンティティに、そのエンティティの種類に対応するモデルクラスが定義されている必要があります。統計情報エンティティの種類に対してモデルクラスを定義するには、stats パッケージをインポートする必要があります。

from google.appengine.ext.db import stats

アプリケーションに Blobstore 値がある場合は、次のコードを追加して、クエリ API に __BlobInfo__ エンティティの種類を認識させる必要があります。(Blobstore API をインポートしてもこのクラスは定義されません。)

from google.appengine.ext import db

class BlobInfo(db.Expando):
  @classmethod
  def kind(cls):
    return '__BlobInfo__'

祖先クエリ

祖先フィルタを含むクエリの結果は、指定されたエンティティとその子孫に限定されます。

tom = Person(key_name='Tom')

wedding_photo = Photo(parent=tom)
wedding_photo.image_url='http://domain.com/some/path/to/wedding_photo.jpg'
wedding_photo.put()

baby_photo = Photo(parent=tom)
baby_photo.image_url='http://domain.com/some/path/to/baby_photo.jpg'
baby_photo.put()

dance_photo = Photo(parent=tom)
dance_photo.image_url='http://domain.com/some/path/to/dance_photo.jpg'
dance_photo.put()

camping_photo = Photo()
camping_photo.image_url='http://domain.com/some/path/to/camping_photo.jpg'
camping_photo.put()


photo_query = Photo.all()
photo_query.ancestor(tom)


# This returns wedding_photo, baby_photo, and dance_photo,
# but not camping_photo, because tom is not an ancestor
for photo in photo_query.run(limit=5):
  # Do something with photo

種類を指定しない祖先クエリ

祖先フィルタを含む「種類を指定しない」クエリは、種類にかかわりなく、指定した祖先と、その子孫のすべてを取得します。この種類のクエリにカスタム インデックスは必要ありません。すべての「種類を指定しないクエリ」と同様に、プロパティ値に対するフィルタや並べ替え順を含めることはできませんが、エンティティのキーに対するフィルタリングは可能です。

q = db.Query()
q.ancestor(ancestor_key)
q.filter('__key__ >', last_seen_key)

(App Engine 管理コンソール内にある、または GqlQuery クラスを使用している)GQL を使用して、種類を指定しない祖先クエリを実行するには、FROM 句を省略します。

q = db.GqlQuery('SELECT * WHERE ANCESTOR IS :1 AND __key__ > :2',
                ancestor_key,
                last_seen_key)

次の例は、特定の祖先を持つ子孫のエンティティをすべて取得する方法を示しています。

tom = Person(key_name='Tom')

wedding_photo = Photo(parent=tom)
wedding_photo.image_url='http://domain.com/some/path/to/wedding_photo.jpg'
wedding_photo.put()

wedding_video = Video(parent=tom)
wedding_video.video_url='http://domain.com/some/path/to/wedding_video.avi'
wedding_video.put()

# The following query returns both weddingPhoto and weddingVideo,
# even though they are of different entity kinds
media_query = db.query_descendants(tom)
for media in media_query.run(limit=5):
  # Do something with media

キーのみのクエリ

キーのみのクエリは、結果のエンティティ自体ではなく、エンティティのキーのみを返すため、エンティティ全体を取得するよりも、レイテンシとコストを抑えられます。

q = Person.all(keys_only=True)

一般的なクエリを実行すると、実際に必要なものよりも多くのエンティティがフェッチされる場合があります。それに対して、まずキーのみのクエリを実行し、その結果からエンティティのサブセットをフェッチしたほうが、多くの場合経済的です。

射影クエリ

クエリの結果で実際に必要なのは、ごくわずかな特定のプロパティの値だけという場合もあります。このような場合、エンティティ全体を取得するのではなく、射影クエリを使用して実際に必要なプロパティだけを取得すると、レイテンシとコストを抑えることができます。詳細については、射影クエリをご覧ください。

並べ替え順序

クエリの並べ替え順序では、次の要素を指定します。

  • プロパティ名
  • 並べ替えの方向(昇順または降順)

Python では、降順の並べ替えはプロパティ名の前のハイフン(-)で示されます。ハイフンを省略すると、デフォルトで昇順の並べ替えが適用されます。例:

# Order alphabetically by last name:
q = Person.all()
q.order('last_name')

# Order by height, tallest to shortest:
q = Person.all()
q.order('-height')

クエリに複数の並べ替え順序が含まれている場合は、指定されている順序で適用されます。次の例では、最初に名字の昇順で並べ替えてから、身長の降順で並べ替えます。

q = Person.all()
q.order('lastName')
q.order('-height')

並べ替え順序が指定されていない場合、結果は Datastore から取得された順序で返されます。

注: あるプロパティに対して不等式フィルタを指定し、別のプロパティに対して並べ替え順序を指定する場合は、不等式フィルタで使用されているプロパティを他のプロパティよりも前に並べる必要があります。これは Datastore がクエリを実行する仕組みに原因があります。

インデックス

Datastore クエリでは、常に 1 つ以上のインデックスを使用して結果が計算されます。インデックスには、インデックスのプロパティとエンティティの祖先(省略可)によって指定されている順序でエンティティ キーが格納されています。インデックスは、アプリケーションがエンティティに加えた変更を反映して増分的に更新されます。そのため追加の計算を行うことなく、すべてのクエリの正しい結果が得られます。

App Engine は、エンティティの各プロパティに単純なインデックスを事前定義します。さらに、App Engine アプリケーションでは、カスタム インデックスをインデックス構成ファイル index.yaml の中で定義することもできます。開発用サーバーは、既存のインデックスでは実行できないクエリが出現したときに、このファイルに自動的に候補を追加します。アプリケーションをアップロードする前にこのファイルを編集して、インデックスを手動で調整できます。

クエリ インターフェースの例

Python Datastore API には、クエリを作成して実行するための 2 つのクラスが用意されています。

  • Queryでは、メソッド呼び出しを使用してクエリが作成されます。
  • GqlQuery では、GQL という SQL に似たクエリ言語を使用して、クエリ文字列からクエリが作成されます。
class Person(db.Model):
  first_name = db.StringProperty()
  last_name = db.StringProperty()
  city = db.StringProperty()
  birth_year = db.IntegerProperty()
  height = db.IntegerProperty()


# Query interface constructs a query using instance methods
q = Person.all()
q.filter("last_name =", "Smith")
q.filter("height <=", max_height)
q.order("-height")


# GqlQuery interface constructs a query using a GQL query string
q = db.GqlQuery("SELECT * FROM Person " +
                "WHERE last_name = :1 AND height <= :2 " +
                "ORDER BY height DESC",
                "Smith", max_height)


# Query is not executed until results are accessed
for p in q.run(limit=5):
  print "%s %s, %d inches tall" % (p.first_name, p.last_name, p.height)

次のステップ