【GCP】Firestoreのクエリで「Arrayフィールドに特定の要素が含まれない場合」を指定できない仕様に対するベストプラクティス【開発備忘録】

GCP 開発備忘録

背景

Firestoreのクエリでは、whereを用いて取得の条件を追加することが可能です。
そこで、私は以下の実装をしたい場面に遭遇しました。

  • Array型のusersフィールドにuidを格納
  • そのusersフィールドに自身のuidが含まれていないドキュメントのみをフィルタリングして取得

そこで、配列に特定の要素が含まれてないドキュメントを抽出する演算子がないか調べてみました。

whereで使用可能な条件は以下の通りです。

演算子意味使い方
isEqualTo〜と等しい.where('name', isEqualTo: '山田太郎')
isNotEqualTo〜と等しくない.where('name', isNotEqualTo: '山田太郎')
isLessThan〜より小さい.where('age', isLessThan: 20)
isLessThanOrEqualTo〜以下.where('age', isLessThanOrEqualTo: 20)
isGreaterThan〜より大きい.where('age', isGreaterThan: 20)
isGreaterThanOrEqualTo〜以上.where('age', isGreaterThanOrEqualTo: 20)
arrayContains配列内に右辺の要素を含む.where('hobbies', arrayContains: '釣り')
arrayContainsAny配列内に右辺の要素のいずれかを含む.where('hobbies', arrayContainsAny: ['釣り', 'ギター'])
whereIn右辺のいずれかが含まれている.where('name', whereIn: ['山田太郎', '山田花子'])
whereNotIn右辺のいずれも含まれない.where('name', whereNotIn: ['山田太郎', '山田花子'])
isNull右辺がnullである・ない.where('name', isNull: true)
表はFlutter大学さんが書かれた記事「【Flutter×Firebase】Cloud Firestoreクエリ一覧」より引用

この表をご覧にいただければお分かりのとおり、私の要求を叶えてくれる演算子は見事にありません!

困った!

どうしたら良いのでしょうか。

GitHub Copilotに聞いてみても、

limitを使って数件取得したのち、自身でフィルタリングしてください。
もし条件に一致するものがなければ、範囲を広げて取得してください。

としか言ってくれません。

解決策

最終的に私が辿り着いた解決策をお教えします。

それは、
ルールを利用する!!! です。

端的に言えば、Firestoreのルールで、usersに自身のuidが含まれるドキュメントのreadをfalseにしました。

これによって、usersに自身のuidが含まれるドキュメントはそもそも読み取れなくなるため、getする際にそのドキュメントを省いた上で取得できるのです。

この手法の問題点

もちろん問題点はいくつかあります。

  • usersに自身のuidが含まれるドキュメントをいかなる場合でも取得できなくなってしまう
  • ユーザが直接ドキュメントを読み取る場合にしか使えない(Cloud Functions上からgetする場合はこの手法を使えない)

私の場合は、これらの問題点を受け入れられる状況だったため、この手法を取ることにしました。

arrayContainsの否定バージョンの演算子を早く実装して欲しいものです。。。Googleさん、お願いします!