firestore-simple v5をリリースしました
追記: v7(v6は欠番)についてのエントリはこちら
FirestoreをTypeScriptから使いやすくするfirestore-simpleのv5をリリースしました。v4は2019/06にリリースだったので、半年も経ってしまったのですね・・・。
firestore-simpleの基本的な使い方や、コンセプトは過去の記事を見てもらうのが早いです。
- Firestoreをもっと手軽に使えるfirestore-simpleがバージョン2になりました
- TypeScriptからFirestoreを使いやすくするfirestore-simple v4をリリースしました
新機能
collectionGroup
collectionGroup機能は、2019年のFirestoreの新機能の中でも最もインパクトがあった機能ではないでしょうか。
時間がかかってしまいましたが、firestore-simpleからでもcollectionGroupを使えるようになりました。
const ROOT_PATH = 'example/ts_admin_collection_group'
const firestore = admin.firestore()
interface Review {
id: string,
userId: string,
text: string,
created: Date,
}
const userNames = ['alice', 'bob', 'john']
const collectionId = 'review'
// CollectionGroup用のDAOを作成
const firestoreSimple = new FirestoreSimple(firestore)
const reviewCollectionGroup = firestoreSimple.collectionGroup<Review>({
collectionId: 'review',
decode: (doc) => {
return {
id: doc.id,
userId: doc.userId,
text: doc.text,
created: doc.created.toDate() // timestamp型をDate型に変換
}
}
})
// CollectionGroupのドキュメントを取得
const reviews = await reviewCollectionGroup.fetch()
FirestoreSimple.Collection
と同様に、ジェネリクスに型の情報を渡すことで fetch
したときのオブジェクトに型がちゃんと付きます。
もちろん、decode
も使えるのでサンプルコードのようにfetchしたときにFirestoreのtimestamp型を自動的にDate型に変換するということも可能です。
runBatch
今年のはじめぐらいまではFirestoreにおけるbatchの必要性を理解していなかったのですが、FJUGでのFirestore利用事例を聞いたり、hecateballさんのFirestore Masteryを読んだ際にbatchを積極的に利用すべきだと思い直しました。
ただ元々のFirestoreのbatchのAPIは以下のようにbatch()
でbatchオブジェクトを生成し、このオブジェクトのset()
などを呼び出して最後にcommit()
するという、通常のドキュメント更新とは少々異なる使い方を強制されます。
const batch = admin.firestore().batch()
batch.set({ ... })
await batch.commit()
このbatchのAPIは単純なのですが、それゆえに以下のようなミスや問題が起きやすいと自分は考えています。
-
batch.set()
のつもりが誤って通常のset()
を使ってしまう(delete()
なども同様) - 最後に
batch.commit()
を実行するのを忘れてしまう -
set()
などを別のメソッドで行う場合、batchオブジェクトを引き回す必要がある
firestore-simpleではrunBatch
というrunTransaction
と似たような使い方ができるオリジナルのメソッドを導入することでこれらの問題を解決します。
runBatch
はrunTransaction
と同様にcallback関数を引数に取ります。そしてこのcallback関数の中で行ったadd
, set
, update
, delete
は自動的にbatch実行として扱われ、このcallback関数を正常に抜けたタイミングで自動的にcommit()されます。
つまり、batchで処理したい更新ロジックをrunBatch
の内部に書くだけで済むため、どの処理がbatchとして更新されるのかコードを読むのが非常に簡単になります。
const ROOT_PATH = 'example/ts_admin_batch'
const firestore = admin.firestore()
interface User {
id: string,
name: string,
rank: number,
}
const userNames = ['bob', 'alice', 'john', 'meary', 'king']
const firestoreSimple = new FirestoreSimple(firestore)
const userDao = firestoreSimple.collection<User>({ path: `${ROOT_PATH}/user` })
await firestoreSimple.runBatch(async (_batch) => {
let rank = 1
for (const name of userNames) {
await userDao.add({ name, rank }) // runBatch内でのadd()は内部的にbatch.add()に変換されて実行される
rank += 1
}
console.dir(await userDao.fetchAll()) // まだcommit()されていないため、このタイミングではfetchAllした結果は空
})
// <- runBatchのcallbackを抜けた直後にbatch.commit()が実行される
let users = await userDao.orderBy('rank').fetch()
console.dir(users)
// [ { id: '5ouYv8W5yiku0ztWtqA0', name: 'bob', rank: 1 },
// { id: 'PvoggKKdthKQgAFXtQb3', name: 'alice', rank: 2 },
// { id: '6E9oCximPhqN9uvNnPVi', name: 'john', rank: 3 },
// { id: 'PLv2JLyBRpkq7Xn6F23g', name: 'meary', rank: 4 },
// { id: 'PIWntOJi4YM1IkpG4lLG', name: 'king', rank: 5 } ]
// update(), delete()も同様にrunBatchの中では自動的にbatchに対しての実行に変換される
// 今回は登場しないが、set()も可能
await firestoreSimple.runBatch(async (_batch) => {
let rank = 0
for (const user of users) {
if (user.rank < 4) {
// rankを0スタートの順列に変更
await userDao.update({ id: user.id, rank })
} else {
// ただし、元々rank 4以上だったドキュメントは削除
await userDao.delete(user.id)
}
rank += 1
}
})
// <- ここでbatch.commit()によりupdateとdeleteがまとめて反映される
users = await userDao.orderBy('rank').fetch()
console.dir(users)
// [ { id: '5ouYv8W5yiku0ztWtqA0', name: 'bob', rank: 0 },
// { id: 'PvoggKKdthKQgAFXtQb3', name: 'alice', rank: 1 },
// { id: '6E9oCximPhqN9uvNnPVi', name: 'john', rank: 2 } ]
bulkAdd追加
runBatch
だけでも元々のFirestoreより便利だと思いますが、単にパフォーマンスの問題で単一のコレクションに対して配列でまとめてadd
やdelete
したいというケースは多いと思います。
firestore-simpleでは以前からこのようなユースケースのために、シンプルに配列を受け取ってbatchで更新してくれるbulkSet
, bulkDelete
というオリジナルの機能を用意していました。
今回、同様の機能であるbulkAdd
も追加しました。
BREAKING CHANGES
updateの引数の型のバグ修正
以前はupdate
の引数はジェネリクスのTでしたが、これは誤りでSが正でした。js側とFirestore側でキー名が異なるなどの理由でencode
, decode
を利用してキー名を変換している場合において、update
にencode
後のキー名を渡そうとすると型の不一致でエラーになっていました。
firestore-simpleはadd
, set
でFirestoreに更新を行う前にencode
が実行されるのですが、いくつかの要因によりupdate
だけはencode
を通さずに直接Firestore側のドキュメントのキー名を指定する必要があります。ですが、このバグによりencode
で変換後のキー名を渡そうとすると型エラーとなってしまっていため、そのようなキーで実質的にupdate
を実行できませんでした。
実際にどのようなケースで問題だったかは修正のpull-reqを見てもらうと早いと思います。
fetchByQueryの削除
FirestoreSimple.collection
に存在したfetchByQuery
を削除しました。
collectionGroupを実装するにあたり、Query周りを修正する必要があったのですがその過程で内部的に不要となったので削除しました。
Queryが関係するwhere
やorderBy
を使う場合にはFirestoreSimple.collection.where
などを使っていると思いますので、元々使うケースはほとんどなかったはずです。
今後の方向性
今回のcollectionGroupとrunBatchの追加により、FirestoreのAdmin SDKに存在する機能はほぼカバーできたかなと思います。自分が欲しかったfirestore-simpleオリジナルの機能も一旦実装し終えたので、Firestore自体に新機能が追加されない限りおそらく大幅な機能追加はないと思います。
ただ、サンプルとしてexampleに置いてあるコードが少々古くなっているので直したり、たまに失敗する不安定なテストをFirestoreのエミュレータを使うようにするなど内部的な改善の予定は残っていたりします。
一方で、2019年にWeb SDK対応の手を付けることが結局できなかったので、2020年はこれを優先的に行いたいと考えています。
おそらく、Web SDKに対応させるためにはfirestore-simpleのパッケージ構造を変える必要があるだろうと考えています。AdminとWebでクラス名を分けるとか、monorepoにするなど方法はいくつかあるはずですが、その方法をこれから検討していこうと思います。
というわけで、firestore-simpleはこれからも開発を続けていく予定です。皆さんも2020年はさらにFirestoreを使いこなしてバリバリ開発していきましょう!
Author And Source
この問題について(firestore-simple v5をリリースしました), 我々は、より多くの情報をここで見つけました https://qiita.com/Kesin11/items/999011de9b6aeba37e78著者帰属:元の著者の情報は、元の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 .