Firestoreで複数フィールドに対する範囲・不等式フィルタが使えるようになる!

2024/04/12に公開

はじめに

2024年3月27日にFirestoreの複数フィールドに対する範囲・不等式フィルタ機能がプレビュー版としてリリースされました。
https://cloud.google.com/firestore/docs/query-data/multiple-range-fields

この機能によって「employeesコレクションからsalaryが100,000より大きい かつ experienceが5未満のデータを取得する」ということがクエリだけでできるようになります。

iter := client.Collection("employees").
    Where("salary", ">", "100000").
    Where("experience", "<", "5").
    orderBy("experience").Documents(ctx)

今までのクエリでの課題

これまでは範囲・不等式フィルタを1つのフィールドにしか適用できませんでした。
そのため、複数のフィールドを組み合わせてフィルタリングする際は、クエリでの絞り込みだけでなく、クライアントサイドでの処理が必要でした。

この方法には以下のような課題がありました。

  • クライアントサイドロジックの増加
    • フィルタリング条件が複雑になると、クライアントサイドでのロジックも複雑化する
  • パフォーマンスとコストへの影響
    • 最終的に除外するデータもFirestoreから読み込むため、パフォーマンスの低下やコストが増加することがある

複数フィールドでの範囲・不等式フィルタ機能が追加されることで、クエリだけでフィルタリングが可能となり、上記の課題が解消されます。

インデックス最適化のベストプラクティス

この機能を使ったクエリのパフォーマンスとコストを抑えるためには、インデックスを最適化する必要があります。

1. インデックスのフィールドの並び順

複合インデックスを定義する際には、どのフィールドをインデックスの左端に配置するかが重要になります。

Firestoreは複合インデックスの左端のフィールドを使用して、orderBy句の最初のフィールドに対する等価制約および範囲・不等式制約を満たします。
この制約により、スキャンするインデックスエントリの数を減らすことができます。

その後、インデックスの残りのフィールドを使用して、クエリの他の制約を満たします。
これらの制約では、Firestoreがスキャンするインデックスエントリの数は減りませんが、クライアントに返すドキュメント数は少なくなります。

次の順序でインデックスを定義するのが最も効率的になります。

  1. 等式フィルタで使うフィールド
  2. orderBy句で使うフィールド
  3. 範囲・不等式フィルタが使うフィールド(2に含まれていない)
  4. 集約クエリで使うフィールド(2・3に含まれてない)

2. クエリの制約条件のselectivityが高い順にフィールドを並べ替える

クエリの制約条件のselectivity(選択性)の高い順でorderBy句を指定することで、Firestoreが最適なインデックスを選択するようになります。
selectivityが高い方が一致するドキュメント数が少なくなるため、インデックス順序の早い段階でselectivityの高いフィールドを選択した方が効率的です。

また、以下の条件を満たす場合は、クエリでソートするよりクライアントサイドのロジックでソートする方が良いです。

  • orderByでソートをするとインデックスがselectivityの降順にならない
  • クエリで取得するドキュメント数が少ない

例えば、employeesコレクションからsalaryが100,000を超える人を検索し、experience順に並べるとします。
salaryが100,000を超える人が少ない場合は、以下のようなクエリにするとFirestoreが(salary [...])のインデックスを利用するので効率的です。

iter := client.Collection("employees").
    Where("salary", ">", "100000").
    orderBy("salary").Documents(ctx)

// experience順でソートするロジック

このクエリのorderByをexperienceにすると、Firestoreは(experience [...], salary [...])のインデックスを利用します。
experienceはwhere句でフィルタリングしていないため、employeesコレクションの全てのインデックスエントリを読み取ることになります。
最終的にsalaryで除外されるインデックスエントリも読み取ることになるので、クエリの実行時間とコストが増加します。

料金

複数フィールドに対する範囲・不等式フィルタはドキュメントの読み取り数インデックスエントリの読み取り数に基づいて課金されます。
インデックスエントリは1000個ごとに1回の読み取りオペレーションとなります。

等式フィルターや単一フィールドに対する範囲・不等式フィルタはインデックスエントリの読み取りについては課金されなかったので、複数フィールドで範囲フィルタを使う場合はインデックスの最適化がこれまで以上に重要になります。

まとめ

Firestoreの複数フィールドに対する範囲・不等式フィルタ機能によってクエリの柔軟性が向上します。
複合条件でのデータ絞り込みを行えるようになることで、開発効率とアプリケーションのパフォーマンス向上が期待できます。

GA(一般提供)されるのを楽しみに待ちたいと思います!

参考

カウシェ Tech Blog

Discussion