Firestoreでusersドキュメントにuidを含めるか
定石
定石ではFirebase AuthenticationのUser UIDをFirestoreのドキュメントIDにして、usersコレクションに自分だけが書けるドキュメントを作成します。(一意かつドキュメントIDに使える文字列なので)
書き込み
db.collection('users').doc(userId).set({name:'hoge'}) // userId = uid
読み込み
let userData = {}
const docRef = db.collection('users').doc(userId)
docRef.get().then((doc) => {
if (doc.exists) {
useData = doc.data()
}
})
セキュリティルール
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if true;
allow create, update: if request.auth.uid == userId;
}
}
}
このときuserData.uidでUser UIDを取得できるとプログラムが非常に捗ります。 特にSNS的な機能を持つサービスで他人のuidを取得するときです。(ひとのコードを読んでみるとuidを含めている場合とそうでない場合がありました)
やり方は2通りあります。
1.ドキュメント取得時にObjectに自分でuidを追加
読み込み
let userData = {}
const docRef = db.collection('users').doc(userId)
docRef.get().then((doc) => {
if (doc.exists) {
userData.uid = doc.id // ここ
useData = doc.data()
}
})
実際のFirestore上のドキュメントと対応してないので分かりにくさはありますが、1行追加するだけです。onSnapshotやwhere句によるQueryの時も同様です。
2.ドキュメント作成時にあらかじめuidを書いておく
書き込み
db.collection('users').doc(userId).set({name:'hoge', uid: userId})
この場合セキュリティルールが複雑になります
ユーザーのuidが一致した場合だけcreateできるようにし、update時にuidを含めるのを禁止(もしくはuidが一致)する必要があります。
冗長さが気になりますが、NoSQLとはそのようなものです。
セキュリティルール
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if true;
allow create: if request.auth.uid == userId && request.auth.uid == request.resource.data.uid;
allow update: if request.auth.uid == userId && !('uid' in request.writeFields);
}
}
}
単純にusersコレクションのケースではどちらの方針でもよさそうですです。
いかなる場合もuidを書いても書かなくてもいいと言えるのか?
どこかのサブコレクションにuidをドキュメントIdとしてユーザーに関するドキュメントを作るというのも定石ですが、その中からCollectionGroupで横断的に検索するときに差がでます。
ドキュメントId指定でCollectionGroup検索することは(今のところ)できないからです。
データ構造の例: roomsコレクション以下のmembersサブコレクションにuidをドキュメントIdとしてそのルームでのメンバー情報を書いておき、自分の各ルームでのメンバー情報を取得したい場合
/rooms/{roomId}/members/{userId} // そのルームでのユーザーの権限などを書く
ダメなコード
db.collectionGroup('members')
.where(firebase.firestore.FieldPath.documentId(), '==', userId) // ダメ
いいコード(ドキュメント中にuidが書いてある前提)
db.collectionGroup('members')
.where('uid', '==', userId)
uidを書いておいて助かりました。ダメなほうは不等号は通るようですが、等号では検索できませんでした。同じことはユーザーIdに限らずドキュメントIdで横断的にサブコレクションから検索したい場合に発生します。
アジャイルな開発をする場合、これが原因で将来DB変更するようなことが起こりえます。
結論
きわめて簡単なデータ構造で済む場合を除いて、userIdのドキュメントにuidありとなしを混在させるぐらいなら一貫してuidをドキュメント中に書いておくのがいいと思いました。
ご覧いただきありがとうございました。
Discussion