公式ガイドで教えてくれなかったFirestoreのtransaction処理で連番IDを振る方法
本記事の投稿動機
- Firestoreのtransaction処理実装で公式ガイドには載っていないやり方での実装が必要だったため、知識整理として投稿
- おそらく連番IDを振るという実装はFirestore的にも推奨をしていませんので、この実装を実運用する場合は自己責任でおねがいいたします
本記事の対象読者
- Cloud Firestoreを使用したことがあり、Firestoreにデータ追加、更新、削除といったオペレーションの想像がつく方
- アンチパターンかもだけどfirestoreで連番IDを扱いたい方
- 素直にauto-incrementを使うのが吉と分かってはいるが、連番IDを他通信と競合せずに扱いたい方
- Cloud Firestoreを使用したことがあり、Firestoreにデータ追加、更新、削除といったオペレーションの想像がつく方
- アンチパターンかもだけどfirestoreで連番IDを扱いたい方
- 素直にauto-incrementを使うのが吉と分かってはいるが、連番IDを他通信と競合せずに扱いたい方
※ 実装はNuxt.js×TypeScriptです。
Firestoreのtransaction処理とは
- 1つ以上のドキュメントに対して読み書きを行う一連のオペレーション
- 最大 500 のドキュメントに書き込みを行うことができる
公式ガイドでのtransaction
公式ガイドの例では、コレクションのドキュメントが分かっている状態で、データを取得し、一部を編集して再度更新するといったサンプルとなっています。
また、以下の注意が必要です。
- 読み取りオペレーションは書き込みオペレーションの前に実行する必要があります。
- トランザクションが読み取るドキュメントに対して同時編集が影響する場合は、トランザクションを呼び出す関数(トランザクション関数)が複数回実行されることがあります。
- トランザクション関数はアプリケーションの状態を直接変更してはなりません。
- クライアントがオフラインの場合、トランザクションは失敗します。
今回、実装したいこと
- 特定のコレクション(ドキュメント名は不明)から
createdBy
を降順にし、limitをかけた上でデータを取得。
- そのデータのtodoId(number)に
1
を足して、連番ID(newTodoId)として新規データオブジェクトと共に追加
createdBy
を降順にし、limitをかけた上でデータを取得。1
を足して、連番ID(newTodoId)として新規データオブジェクトと共に追加なので、
- transaction処理内でデータ取得(.get)とデータ新規追加(.set)をする必要があります。
※通常であれば新規のデータ追加は.add
を使用します。transactionの場合は.add
が用意されておらず。代わりに.set
を使用します。.set
は既にデータが有れば更新。無ければ新規追加となりますので注意が必要です。
transaction内でやること
1. まず連番IDの元となるIDを取得する
transactionでは読み
と書き
が必須となっており、まずは読み
から実装します。
runTransactionメソッド内でtransactionによるオペレーションが使用できるようになります。
今回、ドキュメント名は分かっておらず、orderBy
で降順に並び替えた後に最新の1件のみを取得します。
newTodoRef
では空docを指定し、referenceを作ります。
export async function addTodo(userId: string, todo: Todo) {
await Firestore.runTransaction(async (transaction) => {
const todoRef = await Firestore.collection('todos')
const newTodoRef = await todoRef.doc()
await transaction.get(newTodoRef).then(async () => {
// 最新のtodoを1件のみ取得
const querySnapshot = await todoRef.orderBy("createdAt", "desc").limit(1).get()
})
})
}
2. 連番IDを計算する
今回の場合は、元のIDに1を足すだけです。
先に変数newTodoId
を用意し、Promise.all内でtodo.todoId + 1
を代入します。
if(querySnapshot) {
let newTodoId!: number
await Promise.all(querySnapshot.docs.map(async (todoDoc) => {
const todo = await todoDoc.data()
// 新しいnewTodoIdを作る
newTodoId = await todo.todoId + 1
}))
}
3. Firestoreに書き込む
あとはデータを整形して書き込むだけです。
書き込みは.add
ではなく、.set
を用いることに注意が必要です。
前述の通り、.set
は既にデータが有れば更新。無ければ新規追加となります。
const todoData: TodoData = {
userId: userId,
todoId: newTodoId,
title: todo.title,
content: todo.content
}
// TODOを新規追加
await transaction.set(newTodoRef, todoData)
実際の全体コード
/firestore/todo.ts
export async function addTodo(userId: string, todo: Todo) {
await Firestore.runTransaction(async (transaction) => {
const todoRef = await Firestore.collection('todos')
const newTodoRef = await todoRef.doc()
await transaction.get(newTodoRef).then(async () => {
// 最新のtodoを1件のみ取得
const querySnapshot = await todoRef.orderBy("createdAt", "desc").limit(1).get()
if(querySnapshot) {
let newTodoId!: number
await Promise.all(querySnapshot.docs.map(async (todoDoc) => {
const todo = await todoDoc.data()
// 新しいnewTodoIdを作る
newTodoId = await todo.todoId + 1
}))
const todoData: TodoData = {
userId: userId,
todoId: newTodoId,
title: todo.title,
content: todo.content
}
// TODOを新規追加
await transaction.set(newTodoRef, todoData)
}
})
})
}
最後に
export async function addTodo(userId: string, todo: Todo) {
await Firestore.runTransaction(async (transaction) => {
const todoRef = await Firestore.collection('todos')
const newTodoRef = await todoRef.doc()
await transaction.get(newTodoRef).then(async () => {
// 最新のtodoを1件のみ取得
const querySnapshot = await todoRef.orderBy("createdAt", "desc").limit(1).get()
if(querySnapshot) {
let newTodoId!: number
await Promise.all(querySnapshot.docs.map(async (todoDoc) => {
const todo = await todoDoc.data()
// 新しいnewTodoIdを作る
newTodoId = await todo.todoId + 1
}))
const todoData: TodoData = {
userId: userId,
todoId: newTodoId,
title: todo.title,
content: todo.content
}
// TODOを新規追加
await transaction.set(newTodoRef, todoData)
}
})
})
}
transactionに一度慣れる、Firestoreを用いたのアプリ実装の幅がぐっと広がる気がします。
私も個人開発が趣味なのでどんどん取り入れていきたいです。
また、Firestoreを使用しておりませんが、個人開発で「thanks-mentions」というPWAをリリースしました!
Qiitaの記事をメンション付きで共有できるPWAとなっております。
Qiitaの記事を共有する際は「thanks-mentions」をよろしくお願いいたします!
TopページにTwitterシェアボタンとPCビューの改善をした🎉https://t.co/lse1BalZSL#thanks_mentions#wakate_mokumoku
— ワツヨ@PWAのthanks-mentions (@watsuyo_2) November 9, 2019
Author And Source
この問題について(公式ガイドで教えてくれなかったFirestoreのtransaction処理で連番IDを振る方法), 我々は、より多くの情報をここで見つけました https://qiita.com/watsuyo_2/items/3f59cc44cc5c60cd208c著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .