本日はメッセージの登録と一覧取得を行います。
データ構造
- 親ドキュメント -
rooms/{room}
- コレクション -
messages
- id - 自動採番
プロパティ | 型 | 説明 |
---|---|---|
from | string | 投稿者 |
text | string | 内容 |
members | array | 参加者リスト |
createdAt | date and time | 投稿日時 |
ルール追加
セキュリティルールの追加も忘れてはいけません。前回テストモードを解除したので追加しないとパーミッションエラーとなります。
基本roomと同様のルールですので解説は割愛します。
参考: Firebaseでチャットアプリを作る日記(7日目)〜 Firestore セキュリティルールを書く - The Round
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { (中略) match /rooms/{room}/messages/{message} { allow create: if request.auth.uid != null && request.auth.token.email_verified && request.auth.token.email == request.resource.data.from && request.auth.token.email in request.resource.data.members; allow read: if request.auth.uid != null && request.auth.token.email_verified && request.auth.token.email in resource.data.members; } } }
メッセージ登録
コード
export function addMessage(room, user, text) { console.log('adding message', room, user, text); let db = firebase.firestore(); let message = { from: user.email, text: text, members: room.members, createdAt: firebase.firestore.FieldValue.serverTimestamp() }; return db.collection("rooms").doc(room.id).collection("messages").add(message) .then(function(docRef) { return docRef.get(); // ここではIDしか取得できないのでgetしなおす }) .then(function(docRef) { let added = docRef.data(); added.id = docRef.id; // 登録したmessageをUIに追加(Svelte) messagesOf(room.id).update(list => list.concat(added)); return added; }); }
roomの登録とほぼ同じなので、大部分の説明は割愛します。
ちょっと特殊な点は、firebase.firestore.FieldValue.serverTimestamp()
でサーバー側の日時を保存していることくらいです。
メッセージ一覧参照
コード
export function getMessages(roomId, email) { let db = firebase.firestore(); return db.collection("rooms").doc(roomId).collection("messages").where("members", "array-contains", email).orderBy("createdAt") .get() .then(function(querySnapshot) { let _messages = []; querySnapshot.forEach(function(msgRef) { let msg = msgRef.data(); msg.id = msgRef.id; _messages = [..._messages, msg]; }); // 登録したmessageをUIに追加(Svelte) messagesOf(roomId).set(_messages); return _messages; }) .catch(function(error) { console.log("Error getting messages: ", error); }); }
こちらもroomの一覧取得とほぼ同様です。
インデックス登録
実は、上記コードをそのまま実行するとエラーとなります。
roomは件数が少ない為全件取得してフロントエンドでソートしているのですが、messageは orderBy("createdAt")
を指定してFirestore側でソートしている為、members
と併せた複合インデックスが必要となっています。
エラーメッセージの中にリンクが貼られているのでクリックすると、該当インデックスを作成する為のコンソール画面へジャンプできます。便利ですね(^^)
「インデックスを作成」ボタンをクリックするとインデックスが作成されました(ビルドに少し時間がかかります)
インデックスビルド完了後にクエリを実行することで無事に結果を取得することができました\(^o^)/
ちなみにインデックスですが、通常はファイル(firestore.indexes.json)に全て記述しておいて、$ firebase deploy --only firestore:indexes
コマンドでデプロイできる様にするのが定石かと思います。
コンソールから作成した場合でも $ firebase firestore:indexes
コマンドでJSON形式を出力できるので、それをfirestore.indexes.jsonにコピーしておくのがよいと思います。
$ firebase firestore:indexes>firestore.indexes.json
生成される内容
{ "indexes": [ { "collectionGroup": "messages", "queryScope": "COLLECTION", "fields": [ { "fieldPath": "members", "arrayConfig": "CONTAINS" }, { "fieldPath": "createdAt", "order": "ASCENDING" } ] } ], "fieldOverrides": [] }
まとめ
messageの登録と一覧取得ができました。
ただ実は、Firestoreアクセスの部分は動作確認できているのですがフロント制御のコード(Svelte)でちょっとハマっていてroom移動時に画面にmessage一覧が出せない状態になっています(´・ω・`)
今週末はプライベートの急用も入ってしまった為、目標の毎日更新途切れる可能性大となっております(´・ω・`) 予め謝っておきます 🙇🙇🙇