Firestoreで array-contains-anyin のクエリが登場し、条件に合致するドキュメントの一覧取得がより便利になりました。
今までOR検索的なものはなかったので、上記のようなクエリと同等のことをしようとするとその分並列に条件を変えて取る、といった具合に非効率だったりもしました。
今回は、新たに登場したクエリを使う際に セキュリティルールをどう書けば良いのか 、簡単にご紹介します。

事前定義

今回は下記のような情報を持つ posts コレクションに対して、categoryhashtagsにクエリを適応するケースを想定することにします。

  • ドキュメントの定義

    type Category = 'tech' | 'lifestyle' | 'game'
    interface Post {
    title: string
    body: string
    category: Category
    hashtags: string[]
    }
    
  • array-contains-any を実行する

    // hastagsに 'firebase' もしくは 'tips'が含まれるものを取得
    // arrayに対するOR検索を可能にする
    const result = await firebase.firestore()
    .where('hashtags', 'array-contains-any', ['firebase', 'tips'])
    .get()
    
  • in を実行する

    // categoryが 'tech' もしくは 'game' であるものを取得
    // フィールドに対するOR検索を可能にする
    const result = await firebase.firestore()
    .where('category', 'in', ['tech', 'game'])
    .get()
    

array-contains-any の場合

array-contains-any のクエリで、上記の例のように、 ['firebase', 'tips'] を指定して取得することを保証したい場合は、
hasAny 関数を使用します。

match /posts/{postID} {
  allow list: if resource.data.hashtags.hasAny(['firebase', 'tips']);
}

これによって、 array-contains-anyで指定する値が

  • ['firebase']
  • ['tips']
  • ['firebase', 'tips']

であることが保証され、以下の指定はエラーになります。

  • ['foo']
  • ['firebase', 'foo']
  • ['firebase', 'tips', 'foo']

ちなみに、['firebase', 'tips'] と、指定する値をぴったり指定した場合のみ許可する、みたいなルールは試した感じ、 hasAllhasOnly が動作しなくなるため、
現状では不可能そうです。 (Function not found error: Name: [hasAll]. for 'list' @ Lxxx) のようなエラーが出てしまうんです。。

in の場合

in クエリの場合は、 in オペレータ(ちょっとややこしいですね)を使うことで保証できます。

match /posts/{postID} {
  allow list: if resource.data.category in ['tech', 'game'];
}

こちらも、これによって、 in で指定する値が

  • ['tech']
  • ['game']
  • ['tech', 'game']

であることが保証されます。
あるいは、category が例えば3つ tech, game, lifestyle のみとシステム上絞れる場合には、以下のように書いても保証できます。

match /posts/{postID} {
  allow list: if resource.data.category != 'lifestyle';
}

こちらの書き方であれば、例えばユーザーのアクセス権限レベルが admin 以外の権限を持つユーザーのみ一覧取得する、みたいなのもかけるんじゃないかなと思います。

まとめ

新しいクエリを使うときも、セキュリティルールはしっかり書きましょう 🙏

array-contains-anyinのクエリ自体の使用方法については下記の記事がとてもわかり易いです。