Datastore クエリ数

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

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

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

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

フィルタ

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

プロパティ フィルタ

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

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

q := datastore.NewQuery("Person").Filter("Height <=", maxHeight)

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

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

演算子 意味
= 次に等しい
< 次より小さい
<= 次の値以下
> 次より大きい
>= 次の値以上

主なフィルタ

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

q := datastore.NewQuery("Person").Filter("__key__ >", lastSeenKey)

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

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

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

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

祖先フィルタ

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

q := datastore.NewQuery("Person").Ancestor(ancestorKey)

特別なタイプのクエリ

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

種類を指定しないクエリ

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

q := datastore.NewQuery("").Filter("__key__ >", lastSeenKey)

祖先クエリ

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

// Create two Photo entities in the datastore with a Person as their ancestor.
tomKey := datastore.NewKey(ctx, "Person", "Tom", 0, nil)

wPhoto := Photo{URL: "http://example.com/some/path/to/wedding_photo.jpg"}
wKey := datastore.NewKey(ctx, "Photo", "", 0, tomKey)
_, err := datastore.Put(ctx, wKey, wPhoto)
// check err

bPhoto := Photo{URL: "http://example.com/some/path/to/baby_photo.jpg"}
bKey := datastore.NewKey(ctx, "Photo", "", 0, tomKey)
_, err = datastore.Put(ctx, bKey, bPhoto)
// check err

// Now fetch all Photos that have tomKey as an ancestor.
// This will populate the photos slice with wPhoto and bPhoto.
q := datastore.NewQuery("Photo").Ancestor(tomKey)
var photos []Photo
_, err = q.GetAll(ctx, &photos)
// check err
// do something with photos

種類なしの祖先クエリ

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

q := datastore.NewQuery("").Ancestor(ancestorKey).Filter("__key__ >", lastSeenKey)

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

tomKey := datastore.NewKey(ctx, "Person", "Tom", 0, nil)

weddingPhoto := &Photo{URL: "http://example.com/some/path/to/wedding_photo.jpg"}
_, err := datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "Photo", tomKey), weddingPhoto)

weddingVideo := &Video{URL: "http://example.com/some/path/to/wedding_video.avi"}
_, err = datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "Video", tomKey), weddingVideo)

// The following query returns both weddingPhoto and weddingVideo,
// even though they are of different entity kinds.
q := datastore.NewQuery("").Ancestor(tomKey)
t := q.Run(ctx)
for {
	var x interface{}
	_, err := t.Next(&x)
	if err == datastore.Done {
		break
	}
	if err != nil {
		log.Errorf(ctx, "fetching next Photo/Video: %v", err)
		break
	}
	// Do something (e.g. switch on types)
	doSomething(x)
}

キーのみのクエリ

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

q := datastore.NewQuery("Person").KeysOnly()

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

キーのみのクエリでは 1,000 を超える結果が返されることがありますが、GetAll では一度に 1,000 個のキーしか取得できず、大きな結果を呼び出すとエラーが発生して失敗します。そのため、クエリには 1,000 キーという上限を追加することをおすすめします。

射影クエリ

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

並べ替え順序

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

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

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

// Order alphabetically by last name:
q := datastore.NewQuery("Person").Order("LastName")

// Order by height, tallest to shortest:
q = datastore.NewQuery("Person").Order("-Height")

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

q := datastore.NewQuery("Person").Order("LastName").Order("-Height")

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

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

インデックス

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

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

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

Go Datastore API には、クエリを作成して実行するためのクエリタイプが用意されています。

type Person struct {
	FirstName string
	LastName  string
	City      string
	BirthYear int
	Height    int
}

func handle(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	// The Query type and its methods are used to construct a query.
	q := datastore.NewQuery("Person").
		Filter("LastName =", "Smith").
		Filter("Height <=", maxHeight).
		Order("-Height")

	// To retrieve the results,
	// you must execute the Query using its GetAll or Run methods.
	var people []Person
	if _, err := q.GetAll(ctx, &people); err != nil {
		// Handle error.
	}
	// ...
}

次のステップ