Firestore学び直しスクラップ
Day1
公式ドキュメントを読もう
Cloud Firestoreについて
- コレクションとドキュメントの関係性
- 個人アプリで作る設計を想定して構成を書き出してみる
- Rulesをしっかり設定すること(勉強します)
- 読み取り回数に気を付ける必要がありそう(料金に影響するので)
Rulesについて調べる
su-さんのブログ最高、、!
こちらも読む
実践Firestoreもポチってみたのであとで読む
Firebase プロジェクトの作成+Flutter経由で軽くFirestoreを触ってみる
まずは匿名認証
Auth側の設定でanonymousログインを許可することを忘れずに
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
@override
void initState() {
Future(() async {
var user = await signInAnon();
print('+++++USER INFO+++++');
print(user.user?.uid);
print(user.user?.isAnonymous);
});
super.initState();
}
Future<UserCredential> signInAnon() async {
UserCredential user = await firebaseAuth.signInAnonymously();
return user;
}
データを追加してみる
Swiftが読みやすかった
/users/{uid}
に追加してみる
Setの場合は更新やデータがなければ追加をしてくれる
(追加ならAddでもOK)
CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> addUser() {
return users.doc(uid).set({
'id': uid,
'name': name,
'age': age,
})
.then((value) => print("success set user"))
.catchError((error) => print("Failed to add user: $error"));
}
users/{uid}
だけでなく、中身のusersのドキュメントにもid
にuid
を割り振るようにしてみた。
参考
上記ページを参考にRulesも少し改変してみることに。
createとupdateは認証時のuidとuserの中身のidが一致する場合のみ許可するようにしてみた。
match /users/{userId} {
allow get: if true;
allow create: if request.auth.uid == userId && request.auth.uid == request.resource.data.id;
allow update: if request.auth.uid == userId && request.auth.uid == request.resource.data.id;
allow delete: if request.auth.uid == userId;
}
User→Collectionにデータを追加
今度は作成したUserに紐づくCollectionを作成してみる
user→worksというような関係
worksのドキュメントIDを自動割り振りではなくuidと同じように一致させた方が良いのかな?
と思い、UUIDを生成してセットしてみた。
この辺はちゃんと調べた方が良さそう。
追記:データ取得時にこのような形で取得できるので不要そう。
キーでドキュメント指定したいならキーが必要、そうで無いなら不要でOKらしい!
id: xxx
doc: { ... }
CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> addWork() {
var id = const Uuid().v1();
return users.doc(uid).collection('works').doc(id)
.set({
'id': id,
'name': 'AAAA',
'value': 4500,
'date': DateTime.now(),
})
.then((value) => print("Work Added"))
.catchError((error) => print("Failed to add Data: $error"));
}
今日はここまで
Day2
セキュリティルールについて
Firebase CLIでローカルテストを行う
Firebase CLIの設定
こちらから
FirestoreのSecurity RuleをLocalでテスト~実施
$ // インストール
$ curl -sL https://firebase.tools | bash
$ // 適宜
$ firebase logout
$ firebase login
$ // プロジェクト一覧
$ firebas projects:list
Firestore エミュレータをインストール
公式動画があるので見ながら設定
- npmを使えるように事前に用意しておく
- mochaを使ってテスト
https://www.youtube.com/watch?list=TLGGLqWsKDAn4T0xMzAyMjAyMg&v=VDulvfBpzZE
途中の進捗
テストコードを書いてみる
get()は非同期なのでawaitを書き忘れずに
it("Can read items in the read=only collection", async() => {
const db = firebase.initializeTestApp({projectId: MY_PROJECT_ID}).firestore();
const testDoc = db.collection("readonly").doc("testDoc");
await firebase.assertSucceeds(testDoc.get());
})
失敗テスト
it("Can't write items in the read=only collection", async() => {
const db = firebase.initializeTestApp({projectId: MY_PROJECT_ID}).firestore();
const testDoc = db.collection("readonly").doc("testDoc");
await firebase.assertFails(testDoc.set({foo: "bar"}));
})
認証しているユーザーのみ作成できるかどうか
match /users/{userId} {
allow get: if true;
allow create: if request.auth.uid == userId && request.auth.uid == request.resource.data.id;
allow update: if request.auth.uid == userId && request.auth.uid == request.resource.data.id;
allow delete: if request.auth.uid == userId;
}
it("認証情報(UID)無しでユーザー作成できない", async() => {
const db = firebase.initializeTestApp({projectId: MY_PROJECT_ID}).firestore();
const testDoc = db.collection("users").doc("user_abc");
await firebase.assertFails(testDoc.set({foo: "bar"}));
})
it("認証情報(UID)ありでユーザー作成できる", async() => {
const UID = "user_abc";
const myAuth = {uid: UID, email: "abc@gmail.com"};
const db = firebase.initializeTestApp({projectId: MY_PROJECT_ID, auth: myAuth}).firestore();
const testDoc = db.collection("users").doc(UID);
await firebase.assertSucceeds(testDoc.set({id: UID, foo: "bar"}));
})
関数化しておくと良い👍
const myId = "user_abc";
const thierId = "user_xyz";
const myAuth = {uid: myId, email: "abc@gmail.com"};
function getFirestore(auth) {
return firebase.initializeTestApp({projectId: MY_PROJECT_ID, auth: auth}).firestore();
}
queryでの取得ができるかテスト
match /posts/{postId} {
allow read: if (resource.data.visiblity == "public") || (resource.data.authorId == request.auth.uid);
}
it("posts/でpublicになっているものを取得できる", async() => {
const db = getFirestore(null);
const testQuery = db.collection("posts").where("visiblity", "==", "public");
await firebase.assertSucceeds(testQuery.get());
})
it("authorの場合posts/を取得できる", async() => {
const db = getFirestore(myAuth);
const testQuery = db.collection("posts").where("authorId", "==", myId);
await firebase.assertSucceeds(testQuery.get());
})
http://localhost:4000を開く
エミュレータ上のデータを編集できる
Day3
- 冗長化の必要性について
冗長化の必要性について
~学習中
最近やってなかったので復活
Cloud Functionsでランキング形式のデータを取得するようにする
$ firebase login
$ firebase init functions
必要な情報を設定して作成
functions/index.js
を編集していく
const functions = require('firebase-functions');
// Create and Deploy Your First Cloud Functions
// https://firebase.google.com/docs/functions/write-firebase-functions
exports.helloWorld = functions.https.onRequest((request, response) => {
response.send("Hello from Firebase!");
});
$ firebase deploy --only functions
デプロイが成功するとFirebaseのコンソール上に関数が追加される。
Day5
定期実行もしくはAPI経由での実行、トリガー経由での実行などFunctionsはさまざまなタイミングで呼び出せる
今回は定期実行として発火させていく。
5分ごと、60分ごと、毎週月曜の何時など、具体的に指定ができるので書いておく。
exports.updateRankingFunction = functions
.region('asia-northeast1')
.pubsub.schedule('every 60 mins')
.onRun(async (context) => {
// code here
});
Day6
rankingのcollectionsを想定したデータの更新を作ってみる
const rankingRef = admin.firestore().collection('ranking');
async function updateRanking() {
usersRef
.orderBy('score', 'desc')
.limit(100)
.get()
.then((qSnapshot) =>
{
qSnapshot.forEach(async (ref) => {
try {
var result = await rankingRef.add({
name : ref.data()['name'],
score : ref.data()['score'],
});
functions.logger.log("Rank Result:", result);
} catch (e) {
console.log(`Error: ${JSON.stringify(e)}`)
}
});
});
}
料金について
クエリについて
データ削除について
基本は1件ずつ再帰処理で削除する
Admin SDKを使えばrecursiveDeleteで一括削除できるらしい