インデックス

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

インデックスは次の 2 種類に分けられます。

組み込みインデックス
Datastore モードのデータベースの場合、デフォルトでは、エンティティの種類ごとに各プロパティのインデックスが自動的に事前定義されます。このような単一プロパティのインデックスは単純な種類のクエリに適しています。
複合インデックス
複合インデックスでは、インデックス付けされたエンティティごとに複数のプロパティ値がインデックス付けされます。複雑なクエリをサポートする複合インデックスは、インデックス構成ファイルindex.yaml)で定義されます。

インデックスの種類については、このトピックの後半で詳しく説明します。

インデックスの定義と構造

インデックスは、特定の種類のエンティティのプロパティを、各プロパティの対応する順序(昇順または降順)で並べたリストに対して定義されます。また祖先クエリで使用できるように、オプションでエンティティの祖先を含めることもできます。

インデックス テーブルには、インデックス定義で指定したすべてのプロパティに対応する列が含まれています。テーブルの各行は、インデックス ベースのクエリの結果になるエンティティを表します。インデックスには、インデックスで使用されているすべてのプロパティに対応するインデックス付けされた値セットを持つエンティティのみが含められます。インデックス定義で参照されているプロパティに対応する値を持たないエンティティはインデックスに含まれないため、インデックスに基づくクエリで結果として返されることはありません。

インデックス テーブルの行は祖先の次にプロパティ値という順序で並べ替えられますが、各プロパティ値はインデックス定義で指定された順序になります。クエリを最も効率的に実行できる「完璧なインデックス」とは、次のプロパティをこの順序で定義したものです。

  1. 等式フィルタで使用されるプロパティ
  2. 不等式フィルタ(プロパティを 1 つしか使用しないもの)で使用されるプロパティ
  3. 並べ替えの順序で使用されるプロパティ

これにより、実行される可能性があるすべてのクエリの結果が、テーブルの連続した行に表れます。Datastore モードのデータベースは、次のステップで完全なインデックスを使用してクエリを実行します。

  1. クエリの種類、フィルタのプロパティ、フィルタの演算子、並べ替えの順序に対応するインデックスを特定します。
  2. インデックスの先頭からクエリのフィルタ条件をすべて満たす最初のエンティティまでスキャンします。
  3. インデックスのスキャンを続行し、各エンティティを返します。スキャンは、次の条件のいずれかが満たされるまで続けられます。
    • フィルタ条件を満たさないエンティティを検出する
    • インデックスの末尾に到達する
    • クエリでリクエストされた結果の最大数に達した

たとえば、次のクエリについて考えてみます。

SELECT * FROM Task
WHERE category = 'Personal'
  AND priority < 3
ORDER BY priority DESC

このクエリの完璧なインデックスは、種類 Task のエンティティのキーを含むテーブルで、テーブルの列に categorypriority プロパティの値が含まれます。インデックスはまず category の昇順、次に priority の降順で並べ替えられています。

indexes:
- kind: Task
  properties:
  - name: category
    direction: asc
  - name: priority
    direction: desc

形式が同じでフィルタの値が異なる 2 つのクエリは、同じインデックスを使用します。たとえば、次のクエリは上記の例と同じインデックスを使用します。

SELECT * FROM Task
WHERE category = 'Work'
  AND priority < 5
ORDER BY priority DESC

次のインデックスについては、

indexes:
- kind: Task
  properties:
  - name: category
    direction: asc
  - name: priority
    direction: asc
  - name: created
    direction: asc

形式が異なるにもかかわらず、次の 2 つのクエリでも同じインデックスを使用します。

SELECT * FROM Task
WHERE category = 'Personal'
  AND priority = 5
ORDER BY created ASC

および

SELECT * FROM Task
WHERE category = 'Work'
ORDER BY priority ASC, created ASC

上記の例で作成したインデックスは、どちらのクエリの要件も満たすことができます。

インデックスの構成

Datastore モードの Cloud Firestore では、次の形式のクエリに対して組み込みインデックスまたは自動インデックスを使用できます。

  • 祖先フィルタとキーフィルタだけを使用した種類のないクエリ
  • 祖先フィルタと等式フィルタだけを使用したクエリ
  • 不等式フィルタ(プロパティを 1 つしか使用しないもの)だけを使用したクエリ
  • 祖先フィルタ、プロパティに対する等式フィルタ、キーに対する不等式フィルタだけを使用したクエリ
  • フィルタがなく、プロパティに対する並べ替え順序(昇順か降順)だけを使用したクエリ

たとえば、Datastore モードのデータベースのデフォルトでは、エンティティの個々の種類に対して、1 つのプロパティにつき 2 つの単一プロパティ インデックス(昇順用と降順用)が自動的に事前定義されます。データベースでプロパティのインデックスを維持しない場合は、プロパティをインデックスから除外します。プロパティを除外すると、このプロパティがすべての複合インデックスから削除されることに注意してください。

組み込みインデックスは、等式だけのクエリや単純な不等式クエリなど、多くの単純なクエリを実行するのに十分です。

組み込みインデックスは、Google Cloud Platform Console の [インデックス] ページには表示されません。

複雑なクエリの場合は複合インデックス、つまり手動のインデックスを定義する必要があります。複合インデックスは、次の形式のクエリで必要になります。

  • 祖先フィルタと不等式フィルタを持つクエリ
  • 1 つのプロパティに対して 1 つ以上の不等式フィルタを使用し、他のプロパティに対して 1 つ以上の等式フィルタを使用したクエリ
  • キーの降順という並べ替え順序を使用したクエリ
  • 複数の並べ替え順序を持つクエリ
  • 1 つ以上のフィルタと並べ替え順序を持つクエリ

複合インデックスはアプリケーションのインデックス構成ファイルindex.yaml)で定義されます(組み込みインデックスはインデックス構成ファイルに含まれていません)。

複合インデックスは複数のプロパティで構成されており、個々のプロパティがインデックスから除外されていてはなりません。

複合インデックスは、GCP Console の [インデックス] ページで確認できます。GCP Console を使用して複合インデックスを作成または更新することはできません。

使用可能なインデックス(組み込みインデックスまたはインデックス構成ファイルで指定されているインデックス)を使って実行できないクエリをアプリケーションが実行しようとすると、そのクエリは失敗します。

Datastore モードの API は、ほとんどのアプリケーションに適したインデックスを自動的に提示します。アプリケーションで Datastore モードのデータベースを使用する方法や、データのサイズと形状によっては、インデックスを手動で調整したほうが確実な場合があります。たとえば、複数のプロパティ値を持つエンティティを書き込むと、インデックス爆発が発生してストレージ コストが高くなる可能性があります。

Cloud Datastore エミュレータを使用すると、インデックス構成ファイルを簡単に管理できます。必要なインデックスが存在しない状態でクエリを実行すると失敗しますが、Cloud Datastore エミュレータを使用すればクエリの実行が成功するようにインデックス構成を生成できます。アプリケーションのローカルテストで、フィルタや並べ替え順のすべての組み合わせを使用して、アプリケーションが発行する可能性のあるすべてのクエリが試行されている場合、生成されるエントリはインデックスの完全なセットを表すことになります。テストで可能性のあるすべての形式のクエリが試行されない場合は、インデックスを更新する前にインデックス構成ファイルを確認、調整できます。

index.yaml の詳細については、インデックスの構成をご覧ください。

インデックスのデプロイ、削除

インデックス構成ファイルの変更が完了したら、gcloud datastore create-indexes コマンドを実行してインデックスの使用を開始します。詳細については、インデックスの更新をご覧ください。

以前にデプロイしたインデックスが不要になった場合は、使われていないインデックスを削除できます。

インデックスとプロパティ

ここでは、インデックスについて特に考慮すべき点と、インデックスとエンティティのプロパティとの関連について留意しなければならないことを説明します。

値の型が混在するプロパティ

2 つのエンティティに名前が同じで値の型が異なるプロパティが存在する場合、プロパティのインデックスはまず値の型でエンティティを並べ替え、次に各型に対して適切な順序で並べ替えを行います。たとえば 2 つのエンティティそれぞれに age という名前のプロパティが存在し、一方が整数値を持つプロパティで、もう一方が文字列値を持つプロパティの場合、age プロパティを基準とした並べ替えを行うと、プロパティの値自体には関係なく常に整数値を持つエンティティのほうが文字列値を持つエンティティよりも順序が先になります。

Datastore モードでは整数値と浮動小数点型の数値が別の型として区別されるため、このような動作について特に注意する必要があります。すべての整数値はすべての浮動小数点型の数値よりも順序が前になるため、整数値 38 を持つプロパティは浮動小数点値 37.5 を持つプロパティよりも前になります。

除外されたプロパティ

特定のプロパティに対するフィルタリングや並べ替えが不要な場合は、インデックスから除外することで、Datastore モードのデータベースにプロパティのインデックス エントリが保持されなくなります。これによりインデックス エントリの保持に必要な分だけストレージ サイズが低減されるため、アプリケーションの実行にかかるコストが低く抑えられます。除外されたプロパティを持つエンティティは、そのプロパティが設定されていないかのように振る舞います。除外されたプロパティに対するフィルタまたは並べ替え順を持つクエリが、そのエンティティに一致することはありません。

次の例の description プロパティは、インデックスから除外されています。

C#

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore C# API のリファレンス ドキュメントをご覧ください。

Entity task = new Entity()
{
    Key = _db.CreateKeyFactory("Task").CreateKey("sampleTask"),
    ["category"] = "Personal",
    ["created"] = new DateTime(1999, 01, 01, 0, 0, 0, DateTimeKind.Utc),
    ["done"] = false,
    ["priority"] = 4,
    ["percent_complete"] = 10.0,
    ["description"] = new Value()
    {
        StringValue = "Learn Cloud Datastore",
        ExcludeFromIndexes = true
    },
};

Go

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Go API のリファレンス ドキュメントをご覧ください。

type Task struct {
	Category        string
	Done            bool
	Priority        int
	Description     string `datastore:",noindex"`
	PercentComplete float64
	Created         time.Time
}
task := &Task{
	Category:        "Personal",
	Done:            false,
	Priority:        4,
	Description:     "Learn Cloud Datastore",
	PercentComplete: 10.0,
	Created:         time.Now(),
}

Java

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Java API のリファレンス ドキュメントをご覧ください。

Entity task = Entity.newBuilder(taskKey)
    .set("category", "Personal")
    .set("created", Timestamp.now())
    .set("done", false)
    .set("priority", 4)
    .set("percent_complete", 10.0)
    .set("description",
      StringValue.newBuilder("Learn Cloud Datastore").setExcludeFromIndexes(true).build())
    .build();

Node.js

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Node.js API のリファレンス ドキュメントをご覧ください。

const task = [
  {
    name: 'category',
    value: 'Personal',
  },
  {
    name: 'created',
    value: new Date(),
  },
  {
    name: 'done',
    value: false,
  },
  {
    name: 'priority',
    value: 4,
  },
  {
    name: 'percent_complete',
    value: 10.0,
  },
  {
    name: 'description',
    value: 'Learn Cloud Datastore',
    excludeFromIndexes: true,
  },
];

PHP

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore PHP API のリファレンス ドキュメントをご覧ください。

$task = $datastore->entity(
    $key,
    [
        'category' => 'Personal',
        'created' => new DateTime(),
        'done' => false,
        'priority' => 4,
        'percent_complete' => 10.0,
        'description' => 'Learn Cloud Datastore'
    ],
    ['excludeFromIndexes' => ['description']]
);

Python

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Python API のリファレンス ドキュメントをご覧ください。

task = datastore.Entity(
    key,
    exclude_from_indexes=['description'])
task.update({
    'category': 'Personal',
    'description': 'Learn Cloud Datastore',
    'created': datetime.datetime.utcnow(),
    'done': False,
    'priority': 4,
    'percent_complete': 10.5,
})

Ruby

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Ruby API のリファレンス ドキュメントをご覧ください。

task = datastore.entity "Task" do |t|
  t["category"] = "Personal"
  t["created"] = Time.now
  t["done"] = false
  t["priority"] = 4
  t["percent_complete"] = 10.0
  t["description"] = "Learn Cloud Datastore"
  t.exclude_from_indexes! "description", true
end

GQL

該当なし

description プロパティが除外されている場合、次の例のクエリは結果を一切返しません。

C#

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore C# API のリファレンス ドキュメントをご覧ください。

Query query = new Query("Task")
{
    Filter = Filter.Equal("description", "Learn Cloud Datastore")
};

Go

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Go API のリファレンス ドキュメントをご覧ください。

query := datastore.NewQuery("Tasks").Filter("Description =", "A task description")

Java

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Java API のリファレンス ドキュメントをご覧ください。

Query<Entity> query = Query.newEntityQueryBuilder()
    .setKind("Task")
    .setFilter(PropertyFilter.eq("description", "A task description"))
    .build();

Node.js

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Node.js API のリファレンス ドキュメントをご覧ください。

const query = datastore
  .createQuery('Task')
  .filter('description', '=', 'A task description.');

PHP

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore PHP API のリファレンス ドキュメントをご覧ください。

$query = $datastore->query()
    ->kind('Task')
    ->filter('description', '=', 'A task description.');

Python

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Python API のリファレンス ドキュメントをご覧ください。

query = client.query(kind='Task')
query.add_filter('description', '=', 'Learn Cloud Datastore')

Ruby

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Ruby API のリファレンス ドキュメントをご覧ください。

query = datastore.query("Task").
        where("description", "=", "A task description.")

GQL


# Will not return any results!
SELECT * FROM Task WHERE description = 'A task description.'

後でプロパティを変更して再度インデックス付けできます。

ただし、除外されているプロパティを変更してインデックス付けした場合でも、変更前に作成された既存のエンティティには影響しない点に注意してください。そのようなプロパティに対してフィルタリングを行うクエリは、そうした既存のエンティティを返しません。それらはエンティティの作成時にクエリのインデックスに書き込まれていなかったためです。このようなエンティティに今後実行するクエリでアクセスできるようにするには、データベースにエンティティを書き込み直して、適切なインデックスに含まれるようにする必要があります。つまり、既存のエンティティについては次の手順を行う必要があります。

  1. エンティティを検索または取得する
  2. データベースにエンティティを書き込む

同様に、インデックス付けされているプロパティを変更してインデックスから除外した場合も、変更後にデータベースに書き込まれたエンティティにしか影響しません。そのプロパティを持つ既存のエンティティのインデックス エントリは、エンティティが更新または削除されるまで存在し続けます。目的に合わない結果を回避するには、そのような(現在は除外されている)プロパティを基準としてフィルタリングや並べ替えを行うすべてのクエリのコードを消去する必要があります。

インデックスの制限

Datastore モードの Cloud Firestore では、単一のエンティティに関連付けることができるインデックス エントリの数と全体的なサイズに制限があります。これらの制限は十分な余裕があるため、ほとんどのアプリケーションは影響を受けません。ただし、場合によっては制限に達することもあります。

上記で説明したように、Datastore モードのデータベースはすべてのエンティティのすべてのプロパティについて、事前定義されたインデックスにエントリを作成します。ただし、インデックスから除外することを明示的に宣言したものは除きます。また、インデックス構成ファイルindex.yaml)で宣言した追加のカスタム インデックスにプロパティを含めることもできます。エンティティにリスト プロパティが存在しない場合、そのようなエンティティはカスタム インデックスごとに最大で 1 つのエントリ(非祖先インデックスの場合)、またはエンティティの祖先ごとに 1 つのエントリ(祖先インデックスの場合)を持つことになります。このようなインデックス エントリは、プロパティの値が変化するたびに更新しなければなりません。

エンティティごとに 1 つの値を持つプロパティについては、そのプロパティの事前定義インデックスにエンティティごとに値を 1 回保存するだけで済みます。ただし、そのような単一値のプロパティを大量に持つエンティティの場合は、インデックスのエントリ数またはサイズの制限を超過する可能性があります。同様に、同じプロパティに対して複数の値を持つエンティティは値ごとに個別のインデックス エントリが必要になるため、値の数が大量に存在する場合は、やはりエントリの制限を超過する可能性があります。

複数のプロパティが存在するエンティティで、それぞれのプロパティが複数の値を取る場合、状況はさらに悪化します。そのようなエンティティに対処するには、可能性のあるすべてのプロパティ値の組み合わせに対応するエントリをインデックスに含める必要があります。カスタム インデックスが複数のプロパティを参照し、それぞれのプロパティが複数の値を持つ場合、組み合わせ数が爆発的に増加する可能性があり、比較的少数のプロパティ値しか持たないエンティティでもエントリが大量に必要になります。このような「インデックス爆発」が起こると、大量のインデックス エントリを格納しなければならなくなるため、エンティティのストレージ サイズが大幅に増加する場合があります。またインデックス爆発は、インデックスのエントリ数またはサイズの制限を超過する原因となりやすい状況でもあります。

たとえば、次のようなコードについて考えてみましょう。

C#

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore C# API のリファレンス ドキュメントをご覧ください。

Entity task = new Entity()
{
    Key = _db.CreateKeyFactory("Task").CreateKey("sampleTask"),
    ["tags"] = new ArrayValue() { Values = { "fun", "programming", "learn" } },
    ["collaborators"] = new ArrayValue() { Values = { "alice", "bob", "charlie" } },
    ["created"] = DateTime.UtcNow
};

Go

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Go API のリファレンス ドキュメントをご覧ください。

task := &Task{
	Tags:          []string{"fun", "programming", "learn"},
	Collaborators: []string{"alice", "bob", "charlie"},
	Created:       time.Now(),
}

Java

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Java API のリファレンス ドキュメントをご覧ください。

Entity task = Entity.newBuilder(taskKey)
    .set("tags", "fun", "programming", "learn")
    .set("collaborators", "alice", "bob", "charlie")
    .set("created", Timestamp.now())
    .build();

Node.js

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Node.js API のリファレンス ドキュメントをご覧ください。

const task = {
  method: 'insert',
  key: datastore.key('Task'),
  data: {
    tags: ['fun', 'programming', 'learn'],
    collaborators: ['alice', 'bob', 'charlie'],
    created: new Date(),
  },
};

PHP

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore PHP API のリファレンス ドキュメントをご覧ください。

$task = $datastore->entity(
    $datastore->key('Task'),
    [
        'tags' => ['fun', 'programming', 'learn'],
        'collaborators' => ['alice', 'bob', 'charlie'],
        'created' => new DateTime(),
    ]
);

Python

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Python API のリファレンス ドキュメントをご覧ください。

task = datastore.Entity(client.key('Task'))
task.update({
    'tags': [
        'fun',
        'programming',
        'learn'
    ],
    'collaborators': [
        'alice',
        'bob',
        'charlie'
    ],
    'created': datetime.datetime.utcnow()
})

Ruby

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。詳細については、Cloud Datastore Ruby API のリファレンス ドキュメントをご覧ください。

task = datastore.entity "Task" do |t|
  t["tags"] = ["fun", "programming", "learn"]
  t["collaborators"] = ["alice", "bob", "charlie"]
  t["created"] = Time.now
end

GQL

該当なし

このコードでは、プロパティ tags に 3 つの値、プロパティ collaborators に 3 つの値、さらに created には現在の日付を設定して、Task エンティティが作成されます。この場合、プロパティ値の組み合わせとして可能性のあるものそれぞれに対応するには、9 つのインデックス エントリが必要になります。

('fun', 'alice', NOW())
('fun', 'bob', NOW())
('fun', 'charlie', NOW())

('programming', 'alice', NOW())
('programming', 'bob', NOW())
('programming', 'charlie', NOW())

('learn', 'alice', NOW())
('learn', 'bob', NOW())
('learn', 'charlie', NOW())

同じプロパティが複数回繰り返し使われている場合、Datastore モードの Cloud Firestore がインデックス爆発を検出し、代替のインデックスを推奨する場合があります。ただし、それ以外のすべての状況(この例で定義されているクエリなど)の場合、Datastore モードのデータベースでインデックス爆発が発生することになります。この場合、インデックス構成ファイルのインデックスを手動で次のように構成することにより、インデックス爆発を回避できます。

indexes:
- kind: Task
  properties:
  - name: tags
  - name: created
- kind: Task
  properties:
  - name: collaborators
  - name: created

このようにすることで必要なエントリ数が (|tags| * |created| + |collaborators| * |created|) のみになり、9 エントリから 6 エントリに減少します。

('fun', NOW())
('programming', NOW())
('learn', NOW())

('alice', NOW())
('bob', NOW())
('charlie', NOW())

commit オペレーションによってインデックスのエントリ数またはサイズの制限が超過した場合、そのオペレーションは失敗します。どの制限を超過し、どのカスタム インデックスが原因となったのかは、エラーのテキスト("Too many indexed properties" または "Index entries too large" など)によって示されます。構築時にエンティティの制限を超える新しいインデックスを作成すると、そのインデックスに対するクエリが失敗し、インデックスが GCP Console に Error 状態で表示されます。このような Error 状態のインデックスに対処するには、次の手順に従います。

  1. 該当するインデックスをインデックス構成ファイル(index.yaml)から削除します。
  2. 使用されていないインデックスの削除の説明に沿って、gcloud コマンドライン ツールに vacuum_indexes オプションを指定してインデックスをバキュームします。
  3. 次のいずれかを実行します。
    • インデックスの定義および対応するクエリを再構成する
    • インデックス爆発の原因となっているエンティティを削除する
  4. インデックスを index.yaml に追加し直します。
  5. インデックスの更新の説明に沿って、gcloud コマンドライン ツールに update_indexes オプションを指定してインデックスを更新します。

インデックス爆発は、リスト プロパティを使用するカスタム インデックスが必要なクエリを避けることで回避できます。すでに説明したように、このようなクエリとしては複数の並べ替え順序を持つクエリや、等式フィルタと不等式フィルタが混在するクエリなどが挙げられます。

射影のインデックス

射影クエリでは、射影で指定されているすべてのプロパティがインデックスに含まれるようにする必要があります。Cloud Datastore エミュレータを使用すると、必要なインデックスがインデックス構成ファイル(index.yaml)内に自動生成されます。このファイルはアプリケーションと一緒にアップロードされます。

必要なインデックスの数を最小限に抑える方法の 1 つとして、必ずしもすべてが必要でない場合であっても、同じ複数のプロパティを一貫して射影するという方法が挙げられます。たとえば、次のクエリはそれぞれ個別のインデックスを必要とします。

SELECT priority, percent_complete FROM Task

SELECT priority, percent_complete, created FROM Task

ただし created が不要であっても、常に prioritypercent_completecreated の各プロパティを射影することで、必要なインデックスは 1 つだけになります。

既存のクエリを射影クエリに変換する際、射影されたプロパティがクエリの別の部分に含まれていないときは、新しいインデックスを構築しなければならない場合があります。たとえば、次のような既存のクエリがあるとします。

SELECT * FROM Task
WHERE priority > 1
ORDER BY priority, percent_complete

必要なインデックスは次のとおりです。

indexes:
- kind: Task
  properties:
  - name: priority
  - name: percent_complete

このクエリを次のいずれかの射影クエリに変換します。

SELECT created FROM Task
WHERE priority > 1
ORDER BY priority, percent_complete

SELECT priority, percent_complete, created FROM Task
WHERE priority > 1
ORDER BY priority, percent_complete

この場合、新しいプロパティ(created)を伴うため、新しいインデックスを構築する必要があります。

indexes:
- kind: Task
  properties:
  - name: priority
  - name: percent_complete
  - name: created

ただし、次のクエリ

SELECT priority, percent_complete FROM Task
WHERE priority > 1
ORDER BY priority, percent_complete

では必要なインデックスは変わりません。これは射影される priority プロパティと percent_complete プロパティが既存のクエリにすでに含まれているためです。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Cloud Datastore ドキュメント