【Firestore rules】documentのpathを生成する便利関数

Friday, July 6, 2018

Firestoreのruleでdocumentのpathを記述するとき、毎回
/databases/$(database)/documents/... って書くの大変…。なのでこれをなるべく楽に、安全に記述できる便利関数を紹介する。

第1段階

ひとまず、rootからみて、collection/documentIDとなるdocumentのpathの生成を考える。
次のような関数を、match /databases/{database}/documentsの中に記述する。

service cloud.firestore {
  match /databases/{database}/documents {
    function documentPath(collection, documentID) {
      return /databases/$(database)/documents/$(collection)/$(documentID);
    }
  }
}

これで、例えばpostモデルのpostIDと合致するpathを生成する場合は、

allow create: if get(documentPath('post', request.resource.data.postID)).data.name == '...';

と記述することができる。

第2段階

第一段階のままだと、root(もしくは任意の基点となるpath)から1階層分のdocmentのpathしか生成できないので、そこから更にサブコレクションのpathを生成しようと思うと都合が悪い。 なので、任意のパラメータを複数渡すことで、多階層のdocumentのpathにも対応できるようにしてみる。

service cloud.firestore {
  match /databases/{database}/documents {
    function documentPath(paths) {
      return path([['databases', database, 'documents'].join('/'), paths.join('/')].join('/'));
    }
  }
}

解説

path型の変数のままだと、path同士の連結が(現状)うまくできないので、path()関数を使って、文字列からpathを作るようにする。

['databases', database, 'documents'].join('/')

の部分で、 /databases/$(database)/documents に相当する文字列を作り、

paths.join('/')

の部分で、rootからの任意のドキュメントのpathの文字列を作り、
そして最後に連結して、path()関数にてpathを生成する。

例えば、['post', postID, 'authors', authorID] を与えると、
/databases/$(database)/documents/post/$(postID)/authors/$(authorID)
というpathが生成される。

allow create: if get(documentPath(['post', request.resource.data.postID, 'authors', request.auth.uid])).data.name == '...';

これで、毎回 /databases/から書くことに解放され、任意のdocumentのpathを生成しやすくなった。

TechFirebaseCloud FirestoreSecurity RuleTips

【Firestore rules】listのルールでorderByのクエリをチェックする

【Firestore rules】fieldがnullかundefinedか判定する