Cloud Firestore のインデックスの種類

インデックスは、データベースのパフォーマンスにおける重要な要素です。書籍内のトピックをページ番号に対応付ける書籍の索引(インデックス)と同様に、データベースのインデックスはデータベース内のアイテムをデータベース内の場所にマッピングします。データベースにクエリが送信される際、データベースはインデックスを使用して、リクエストされたアイテムの場所をすばやく検索します。

このページでは、Cloud Firestore で使用される、単一フィールド インデックス複合インデックスの 2 種類のインデックスについて説明します。

すべてのクエリの背後にインデックスが存在

クエリを実行する際にインデックスが存在しないと、ほとんどのデータベースはアイテムごとにデータベース内のコンテンツ全体をクロールすることになるため、データベースが大きくなるにつれて処理がますます遅くなります。Cloud Firestore では、すべてのクエリに対してインデックスを使用することで、クエリの高パフォーマンスを確保します。結果的に、クエリのパフォーマンスは、データベース内のアイテム数ではなく、結果セットのサイズに依存することになります。

インデックス管理の手間を減らし、アプリ開発により注力する

Cloud Firestore には、インデックスの管理にかかる時間を短縮できる機能があります。最も基本的なクエリに必要なインデックスは、自動的に作成されます。Cloud Firestore は、アプリを使用してテストする際に、アプリで必要な追加のインデックスを識別して作成するのに役立ちます。

インデックスの種類

Cloud Firestore では、単一フィールド インデックスと複合インデックスという 2 つのタイプのインデックスを使用します。インデックス登録されるフィールドの数の他に、単一フィールド インデックスと複合インデックスの管理方法は異なります。

単一フィールド インデックス

単一フィールド インデックスは、特定の 1 つのフィールドを含む、コレクション内のすべてのドキュメントの並べ替え済みマッピングを保持します。単一フィールド インデックスの各エントリには、ドキュメント内の特定のフィールドの値と、そのドキュメントのデータベース内での位置が記録されます。Cloud Firestore はこうしたインデックスを使用して、多くの基本的なクエリを実行します。データベースの自動インデックスの設定とインデックス除外を構成して、単一フィールド インデックスを管理します。

自動インデックス

デフォルトでは、Cloud Firestore はドキュメント内のフィールドおよびマップ内のサブフィールドごとに単一フィールド インデックスを自動的に維持します。Cloud Firestore では単一フィールド インデックスに次のデフォルト設定が使用されます。

  • 配列でもなくマップでもないフィールドに対しては、2 つの単一フィールド インデックス(1 つは昇順モード、1 つは降順モード)が定義されます。

  • マップ フィールドに対しては、マップ内の配列でもなくマップでもないサブフィールドごとに、1 つの昇順インデックスと 1 つの降順インデックスが作成されます。

  • ドキュメント内の配列フィールドに対しては、コレクションのスコープ配列の内容インデックスが作成、維持されます。

  • コレクション グループのスコープがある単一フィールド インデックスは、デフォルトでは維持されません。

単一フィールド インデックス除外

単一フィールド インデックス除外を作成することで、自動インデックス設定からフィールドを除外できます。インデックス除外は、データベース全体の自動インデックス設定より優先されます。除外すると、自動インデックス設定で無効になる単一フィールド インデックスを有効にしたり、自動インデックスで有効になる単一フィールド インデックスを無効したりできます。除外が役立つケースについては、インデックスに関するベスト プラクティスをご覧ください。

マップ フィールドの単一フィールド インデックス除外を作成すると、マップのサブフィールドはそれらの設定を継承します。ただし、特定のサブフィールドに対して単一フィールド インデックス除外を定義することはできます。サブフィールドの除外を削除すると、サブフィールドはその親の除外設定が存在する場合はそれを継承し、親の除外が存在しない場合はデータベース全体の設定を継承します。

単一フィールド インデックス除外を作成および管理する方法については、Cloud Firestore でのインデックス管理をご覧ください。

複合インデックス

複合インデックスには、1 つだけではなく複数の特定のフィールドが含まれ、コレクション内のすべてのドキュメントの並べ替え済みマッピングが格納されます。

Cloud Firestore では複合インデックスを使用して、単一フィールド インデックスではサポートされないクエリをサポートします。

Cloud Firestore では、可能なフィールドの組み合わせの数が多くなるため、単一フィールド インデックスの場合とは異なり、複合インデックスの自動作成は行いません。代わりに、Cloud Firestore は、アプリを作成する際に必要な複合インデックスを特定して作成できるようにします。

最初に必要なインデックスを作成せずに上記のクエリを実行しようとすると、Cloud Firestore はリンクが含まれたエラー メッセージを返します。そのリンク先にアクセスすることによって、不足しているインデックスを作成できます。この動作は、インデックスでサポートされていないクエリを試行するたびに発生します。また、コンソールまたは Firebase CLI で複合インデックスを手動で定義して管理することもできます。複合インデックスの作成と管理の詳細については、インデックスの管理方法をご覧ください。

インデックス モードとクエリのスコープ

単一フィールド インデックスと複合インデックスの構成方法は異なりますが、どちらもインデックス モードとインデックスのクエリのスコープを構成する必要があります。

インデックス モード

インデックスを定義するときは、インデックスを作成するフィールドごとにインデックス モードを選択します。各フィールドのインデック スモードでは、そのフィールドに対する特定のクエリ句がサポートされます。次のインデックス モードから選択できます。

インデックス モード 説明
昇順 フィールドでの <<===>=> の各クエリ句の使用と、そのフィールド値に基づいた結果の並べ替え(昇順)がサポートされます。
降順 フィールドでの <<===>=> の各クエリ句の使用と、そのフィールド値に基づいた結果の並べ替え(降順)がサポートされます。
配列の内容 フィールドでの array_contains クエリ句の使用がサポートされます。

クエリのスコープ

各インデックスは、コレクションまたはコレクション グループのいずれかをスコープとします。これはインデックスのクエリのスコープと呼ばれます。

コレクションのスコープ
Cloud Firestore では、デフォルトでコレクションのスコープがあるインデックスが作成されます。こうしたインデックスでは、単一のコレクションから結果を返すクエリがサポートされます。

コレクション グループのスコープ
コレクション グループには、同じコレクション ID があるすべてのコレクションが含まれます。コレクション グループからフィルタ処理された結果、もしくは並べ替えられた結果を返すコレクション グループ クエリを実行するには、コレクション グループのスコープに対応するインデックスを作成する必要があります。

インデックスの例

Cloud Firestore は単一フィールド インデックスを自動的に作成するため、アプリケーションでは最も基本的なデータベース クエリを迅速にサポートできます。単一フィールド インデックスを使用すると、フィールドの値と、比較演算子 <<===>=> に基づいて単純なクエリを実行できます。配列フィールドについては array_contains クエリを実行できます。

たとえば、次の例をインデックス作成の観点から確認します。次のスニペットでは、cities コレクションにいくつかの city ドキュメントを作成し、各ドキュメントの namestatecountrycapitalpopulationtags フィールドを設定します。

ウェブ
var citiesRef = db.collection("cities");

citiesRef.doc("SF").set({
    name: "San Francisco", state: "CA", country: "USA",
    capital: false, population: 860000,
    regions: ["west_coast", "norcal"] });
citiesRef.doc("LA").set({
    name: "Los Angeles", state: "CA", country: "USA",
    capital: false, population: 3900000,
    regions: ["west_coast", "socal"] });
citiesRef.doc("DC").set({
    name: "Washington, D.C.", state: null, country: "USA",
    capital: true, population: 680000,
    regions: ["east_coast"] });
citiesRef.doc("TOK").set({
    name: "Tokyo", state: null, country: "Japan",
    capital: true, population: 9000000,
    regions: ["kanto", "honshu"] });
citiesRef.doc("BJ").set({
    name: "Beijing", state: null, country: "China",
    capital: true, population: 21500000,
    regions: ["jingjinji", "hebei"] });

デフォルトの自動インデックス設定を想定する場合、Cloud Firestore は、配列以外の 1 つのフィールドにつき 1 つの昇順単一フィールド インデックス、配列以外の 1 つのフィールドにつき 1 つの降順単一フィールド インデックス、配列フィールドにつき 1 つの配列の内容の単一フィールド インデックスを更新します。次の表の各行は、単一フィールド インデックスのエントリを表しています。

コレクション インデックス登録されるフィールド クエリのスコープ
cities name コレクション
cities state コレクション
cities country コレクション
cities capital コレクション
cities population コレクション
cities name コレクション
cities state コレクション
cities country コレクション
cities capital コレクション
cities population コレクション
cities array-contains regions コレクション

単一フィールド インデックスでサポートされるクエリ

自動的に作成されるこれらの単一フィールド インデックスを使用すると、次のようなシンプルなクエリを実行できます。

ウェブ
citiesRef.where("state", "==", "CA")
citiesRef.where("population", "<", 100000)
citiesRef.where("name", ">=", "San Francisco")

array_contains インデックスを使用すると、regions 配列フィールドをクエリできます。

ウェブ
citiesRef.where("regions", "array-contains", "west_coast")

また、等式(==)に基づいて複合クエリを作成することもできます。

ウェブ
citiesRef.where("state", "==", "CO").where("name", "==", "Denver")
citiesRef.where("country", "==", "USA").where("capital", "==", false).where("state", "==", "CA").where("population", "==", 860000)

範囲比較(<<=>>=)を使用する複合クエリを実行する場合や、他のフィールドに基づいて並べ替える場合は、そのクエリ用の複合インデックスを作成する必要があります。

複合インデックスでサポートされるクエリ

Cloud Firestore では複合インデックスを使用して、単一フィールド インデックスではサポートされない複合クエリをサポートします。たとえば、次のクエリには複合インデックスが必要です。

ウェブ
citiesRef.where("country", "==", "USA").orderBy("population", "asc")
citiesRef.where("country", "==", "USA").where("population", "<", 3800000)
citiesRef.where("country", "==", "USA").where("population", ">", 690000)

これらのクエリには、以下に示す複合インデックスが必要です。これらのクエリでは country フィールドに対して等式を使用しているため、このフィールドのインデックス モードは降順と昇順のどちらでもかまいません。デフォルトでは、不等式クエリは、不等式句のフィールドに基づいて昇順を適用します。

コレクション インデックス登録されるフィールド クエリのスコープ
cities (or ) country, population コレクション

同じクエリを実行するけれども並べ替えが降順の場合は、population 用に降順方向の複合インデックスを追加する必要があります。

ウェブ
citiesRef.where("country", "==", "USA").orderBy("population", "desc")

citiesRef.where("country", "==", "USA")
         .where("population", "<", 3800000)
         .orderBy("population", "desc")

citiesRef.where("country", "==", "USA")
         .where("population", ">", 690000)
         .orderBy("population", "desc")
コレクション インデックス登録されるフィールド クエリのスコープ
cities country, population コレクション
cities country, population コレクション

array_contains クエリと他の句を組み合わせる場合も、複合インデックスを作成する必要があります。

ウェブ
citiesRef.where("regions", "array_contains", "east_coast")
         .where("capital", "==", true)
コレクション インデックス登録されるフィールド クエリのスコープ
cities array-contains tags, (or ) capital コレクション

コレクション グループ インデックスでサポートされるクエリ

コレクション グループのスコープがあるインデックスを示すために、いくつかの city ドキュメントに landmarks サブコレクションを追加するとします。

ウェブ
var citiesRef = db.collection("cities");

citiesRef.doc("SF").collection("landmarks").doc().set({
    name: "Golden Gate Bridge",
    category : "bridge" });
citiesRef.doc("SF").collection("landmarks").doc().set({
    name: "Golden Gate Park",
    category : "park" });

citiesRef.doc("DC").collection("landmarks").doc().set({
    name: "National Gallery of Art",
    category : "museum" });
citiesRef.doc("DC").collection("landmarks").doc().set({
    name: "National Mall",
    category : "park" });

次のコレクションのスコープの単一フィールド インデックスを使用して、単一の都市内の park ランドマークに対してクエリを実行できます。

コレクション インデックス登録されるフィールド クエリのスコープ
landmarks (or ) category コレクション
ウェブ
citiesRef.doc("SF").collection("landmarks").where("category", "==", "park")

たとえば、各都市の park ランドマークに興味があるとします。すべての landmarks コレクションで構成されるコレクション グループに対してこのクエリを実行するには、コレクション グループのスコープがある landmarks 単一フィールド インデックスを有効にする必要があります。

コレクション インデックス登録されるフィールド クエリのスコープ
landmarks (or ) category コレクション グループ

このインデックスを有効にすると、landmarks コレクション グループに対してクエリを実行できます。

ウェブ
var landmarksGroupRef = db.collectionGroup("landmarks");

landmarksGroupRef.where("category", "==", "park")

フィルタ処理された結果、もしくは並べ替えられた結果を返すコレクション グループ クエリを実行するには、コレクション グループのスコープに対応する単一フィールド インデックスまたは複合インデックスを有効にする必要があります。ただし、結果のフィルタ処理や並べ替えを行わないコレクション グループ クエリでは、追加のインデックス定義は必要ありません。

たとえば、追加のインデックスを有効にせずに次のコレクション グループ クエリを実行できます。

ウェブ
db.collectionGroup("landmarks").get()

インデックスと料金

インデックスはアプリケーションのストレージの費用の対象です。インデックスのストレージ サイズの計算方法の詳細については、インデックス エントリのサイズをご覧ください。

インデックス マージの活用

Cloud Firestore ではすべてのクエリにインデックスを使用しますが、クエリごとに必ず 1 つのインデックスが必要になるわけではありません。複数の等式(==)句(およびオプションで orderBy 句)が含まれるクエリに対しては、Cloud Firestore は既存のインデックスを再利用できます。Cloud Firestore では、シンプルな等式フィルタのインデックスをマージして、より大規模な等式クエリに必要な複合インデックスを作成できます。

インデックス マージを利用できる状況を特定すると、インデックスの費用を削減できます。たとえば、レストランを評価するアプリに restaurants というコレクションがある場合を考えます。

  • restaurants

    • burgerthyme

      name : "Burger Thyme"
      category : "burgers"
      city : "San Francisco"
      editors_pick : true
      star_rating : 4

ここで、このアプリは以下のようなクエリを使用するとします。categorycityeditors_pick に等式句の組み合わせを使用し、並べ替えは常に star_rating の昇順です。

ウェブ
db.collection("restaurants").where("category", "==", "burgers")
                            .orderBy("star_rating")

db.collection("restaurants").where("city", "==", "San Francisco")
                            .orderBy("star_rating")

db.collection("restaurants").where("category", "==", "burgers")
                            .where("city", "==", "San Francisco")
                            .orderBy("star_rating")

db.collection("restaurants").where("category", "==", "burgers")
                            .where("city", "==" "San Francisco")
                            .where("editors_pick", "==", true )
                            .orderBy("star_rating")

それぞれのクエリに対してインデックスを作成するという方法が考えられます。

コレクション インデックス登録されるフィールド クエリのスコープ
restaurants category, star_rating コレクション
restaurants city, star_rating コレクション
restaurants category, city, star_rating コレクション
restaurants category, city, editors_pick, star_rating コレクション

より適切なソリューションとして、等式句のインデックスをマージする Cloud Firestore の機能を利用することにより、インデックスの数を減らすことができます。

コレクション インデックス登録されるフィールド クエリのスコープ
restaurants category, star_rating コレクション
restaurants city, star_rating コレクション
restaurants editors_pick, star_rating コレクション

このインデックス セットはサイズが小さくなっただけでなく、次のような追加のクエリもサポートします。

ウェブ
db.collection("restaurants").where("editors_pick", "==", true)
                            .orderBy("star_rating")

インデックスの制限

インデックスには以下の制限が適用されます。すべての割り当てと制限の詳細については、割り当てと制限をご覧ください。

上限 詳細
データベース 1 つあたりの複合インデックスの最大数 200
データベース 1 つあたりの単一フィールド インデックス除外の最大数 200

ドキュメントごとのインデックス エントリの最大数

40,000

インデックス エントリの数は、ドキュメントに関する次の数の合計です。

  • 単一フィールド インデックス エントリ数
  • 複合インデックス エントリ数
インデックス エントリの最大サイズ

7.5 KiB

Cloud Firestore でインデックス エントリのサイズを計算する方法については、インデックス エントリのサイズをご覧ください。

ドキュメントのインデックス エントリの最大合計サイズ

8 MiB

合計サイズは、ドキュメントに関する次の数の合計です。

  • ドキュメントの単一フィールド インデックス エントリの合計サイズ
  • ドキュメントの複合インデックス エントリの合計サイズ
  • インデックス登録されるフィールド値の最大サイズ

    1,500 バイト

    1,500 バイトを超えるフィールド値は切り捨てられます。切り捨てられたフィールド値が含まれるクエリでは、結果が矛盾する場合があります。

    インデックスに関するベスト プラクティス

    ほとんどのアプリでは、自動インデックス作成とエラー メッセージのリンクを使用してインデックスを管理できます。ただし、以下のケースについては、単一フィールド除外を追加したほうがいい場合があります。

    ケース 説明
    大きな文字列フィールド

    クエリには使用しない長い文字列値を保持することが多い文字列フィールドがある場合は、インデックス作成からそのフィールドを除外することでストレージの費用を削減できます。

    コレクションへの書き込みレートが高く、連続した値を持つドキュメントが含まれる

    コレクション内のドキュメント間で順次に増加または減少するフィールド(タイムスタンプなど)のインデックスを作成する場合は、コレクションへの最大書き込みレートは 500 回/秒です。連続した値を持つフィールドに基づいてクエリを実行することがない場合は、そのフィールドをインデックス作成から除外すれば、この制限を回避できます。

    たとえば、書き込みレートが高い IoT のユースケースでは、タイムスタンプ フィールドを持つドキュメントを含むコレクションは、500 回/秒の書き込み上限に近づく可能性があります。

    大規模な配列フィールドまたはマップ フィールド

    大規模な配列フィールドまたはマップ フィールドの場合、ドキュメントごとに 20,000 件のインデックス エントリの上限に近づく可能性があります。大規模な配列フィールドまたはマップ フィールドに基づいてクエリを実行することがない場合は、そのフィールドをインデックス作成から除外してください。