対象読者
複合インデックスを登録しているのに取得できない方
解決方法
まず、エラー内容を確認します。firebase Functionsを使っている方はfirebaseのコンソールに入って、Functionsのログからエラーを確認してください。もし使われていない方は、例外処理によってエラー内容を返して確認してみてください。以下のエラー(一部抜粋)が出力されていると思います。
"details": "The query requires an index. You can create it here: https://console.firebase.google.com/v1/r/project/[projectID]/firestore/indexes?create_composite=[文字列]",
「クエリを実行するにはインデックスが必要なので作ってくださいね」と書かれており、親切にURLも記載してくれています。アクセスすると、Google Cloud PlatFormがブラウザで開き、インデックスを登録するかを聞かれますので、そのままインデックスを登録をしてください。
登録が成功された状態でAPIを実行すると、無事にデータが取得できるようになります。(この解決方法は公式ドキュメントにも記載されています。)
実際につまったところと原因
以下のような構造をとったデータが保存されているとします。
interface Log {
user_id: string
message_id: string
created_at: string
}
そのデータを以下の条件で取得したいと仮定します。
①あるユーザーのログをメッセージごとに並び替え、さらに作成日の順に並び替えて取得する
②あるメッセージのログをユーザーごとに並び替え、さらに作成日の順に並び替えて取得する
// ①の場合のクエリ
snapshot = await docRef
.where('user_id', '==', userId)
.orderBy('message_id')
.orderBy('created_at')
.get()
// ②の場合のクエリ
snapshot = await docRef
.where('message_id', '==', messageId)
.orderBy('user_id')
.orderBy('created_at')
.get()
このクエリに対し、以下のインデックスを事前に登録していました。
コレクションID | インデックス登録されるフィールド | クエリのスコープ |
---|---|---|
collection_id | user_id 昇順 message_id 昇順 created_at 昇順 | コレクション |
※表の列の名前は、それぞれfirebaseのコンソールでFirestoreにアクセスし、インデックスに書かれているものです。Google Cloud PlatFormで見た場合は
インデックス登録されるフィールド → フィールド
クエリのスコープ → Query scope
となっています。
この状態で実際にAPIを実行すると、①は取得できて②ではエラーになります。
そして、上記の解決方法に記載の通りクエリを作成すると、インデックスが以下のようになります。
コレクションID | インデックス登録されるフィールド | クエリのスコープ |
---|---|---|
collection_id | user_id 昇順 message_id 昇順 created_at 昇順 | コレクション |
collection_id | message_id 昇順 user_id 昇順 created_at 昇順 | コレクション |
ここで、もう一度APIを実行すると先ほどエラーだった②も取得できるようになります。
つまり、原因はクエリで指定している複数のインデックスをその順にインデックスに登録していなかったこととなります。今回のケースでは、使用したフィールドは同じでしたが、厳密には条件が異なっており、インデックスも別々に定義する必要があったようです。
このように条件が違えばインデックスを新たに定義すればいいと言うことが分かったのですが、やみくもにインデックスを作成していると利用料金(インデックスはストレージの料金として計上されるみたいです)が無駄にかかってしまうので、インデックス マージを活用して重複するインデックスを削減する、と言ったことも可能みたいです。
公式ドキュメントを読んでみて「複合インデックスを登録すると複数のフィールドを組み合わせたインデックスを作成でき、それによってデータが取得できるようになる」と自分なりに解釈をしてみたのですが、組み合わせであっても順番を担保しないといけないのは何故なのでしょうか、、詳しい方いらっしゃいましたら教えていただきたいです、、