Firebaseのgo実装を試す
こんにちは!eurekaのAPIチームでエンジニアをやっている@takochuu です。
Firebaseはクライアントアプリからサーバーレスで機能を実現する時に使うもの、というイメージがあったのですが、先日リポジトリを漁っていたら Firebase SDKのgo実装が公開されていることに気づいたので早速試してみます。
※決してモテることを期待しているわけではありません
FirebaseのGoDocを参照すると、SDKを通してアクセスできるサービスは複数あることがわかります。今回は個人でも試しやすいFirestoreを試すことにします。
※まだベータ実装なので様々な制限があるため、Production環境で利用する際は気をつけてください
そもそもFirestoreとは、それまで提供されていたRealtime Databaseの後継に当たるクラウドホストのNoSQLデータベースです。RDBMSと違い、Tableとrowsでデータを表現するのではなく、collection / document / data という3つの概念でデータを表現します。
階層型のデータ構造が扱えるのもRDBMSとは大きな違いです(BigqueryとかDynamoDBも扱えますよね)
では、さっそく実装に入ります。go getコマンドでパッケージを取得しておいてください。
package main
import (
"context"
"cloud.google.com/go/firestore"
"github.com/golang/glog"
)
func main() {
ctx := context.Background()
app := initFirebase()
client, err := app.Firestore(ctx)
if err != nil {
glog.Errorln(err)
}
}func initFirebase() *firebase.App {
opt := option.WithCredentialsFile("/path/to/json.json")
app, err := firebase.NewApp(context.Background(), nil, opt)
if err != nil {
glog.Errorln("Error")
}
return app
}
mainパッケージを用意して、Clientを作成します。今回はCredentialファイルを配置してAppをinitする方法で実装を行います。
これで初期化は完了したので、次はusers-devというcollection内のtestというdocumentにネストしたデータを書き込みます。
type state struct {
Height int `firestore:"height"`
Sex string `firestore:"sex"`
Age int `firestore:"age"`
}
type writeData struct {
UserID int64 `firestore:"user_id"`
Email string `firestore:"email"`
State state `firestore:"state"`
} // users-devというCollection内の
collection := client.Collection("users-dev")
doc := collection.Doc("test")
result, err := doc.Set(ctx, writeData{
UserID: 40000,
Email: "takochuu@hogehoge.jp",
State: state{
Sex: "male",
Height: 160,
Age: 25,
},
})
if err != nil {
glog.Errorln(err)
}
}
上記のコードを実行すると、管理画面上で下記のようにデータが挿入されているのがわかります。
今度は、書き込んだデータを取得してみます。
SDKでは複数種類データ取得の方法が実装されているのでそちらを紹介します。
collection := client.Collection("users-dev")
// documentを指定して取得
doc := collection.Doc("test")
snapshot, err := doc.Get(ctx)
if err != nil {
glog.Errorln(err)
}
dump.Dump("===========================")
data := snapshot.Data()
if err != nil {
glog.Errorln(err)
}
dump.Dump(data["user_id"])
// 構造体にマッピング
ent := writeData{}
if err = snapshot.DataTo(&ent); err != nil {
glog.Errorln(err)
}
dump.Dump("===========================")
dump.Dump(ent)
// Queryで取得
q := client.Collection("users-dev").Where("user_id", "=", 40000)
dump.Dump("===========================")
dump.Dump(q.Documents(ctx))
dump.Dump("===========================")
結果はこのようになります。
"===========================”
40000 (int64)
“===========================”
writeData {
UserID: 40000 (int64),
Email: “takochuu@hogehoge.jp”,
State: state {
Height: 160 (int),
Sex: “male”,
Age: 25 (int)
}
}
“===========================”
(0xc420302140) &DocumentIterator {
iter: interface(docIterator) ,
err: interface(error)
}
“===========================”
ここまででわかるように、かなり柔軟にデータを取得できます。
サービスで適用する際は、構造体にマッピングする方法以外は型が明確ではないので、パフォーマンスが気になるのでベンチマークを取って比較してみることにします。
※Queryは複数件なので除外します
package main
import (
"context"
"testing"
"github.com/golang/glog"
)
func BenchmarkSnapshot(t *testing.B) {
ctx := context.Background()
app := initFirebase()
client, err := app.Firestore(ctx)
if err != nil {
glog.Errorln(err)
}
collection := client.Collection("users-dev")
// documentを指定して取得
doc := collection.Doc("test")
snapshot, err := doc.Get(ctx)
if err != nil {
glog.Errorln(err)
}
t.ResetTimer()
for i := 0; i < t.N; i++ {
_ = snapshot.Data()
}
t.StopTimer()
}
func BenchmarkMapping(t *testing.B) {
ctx := context.Background()
app := initFirebase()
client, err := app.Firestore(ctx)
if err != nil {
glog.Errorln(err)
}
type state struct {
Height int `firestore:"height"`
Sex string `firestore:"sex"`
Age int `firestore:"age"`
}
type writeData struct {
UserID int64 `firestore:"user_id"`
Email string `firestore:"email"`
State state `firestore:"state"`
}
collection := client.Collection("users-dev")
// documentを指定して取得
doc := collection.Doc("test")
snapshot, err := doc.Get(ctx)
ent := writeData{}
t.ResetTimer()
for i := 0; i < t.N; i++ {
_ = snapshot.DataTo(&ent)
}
t.StopTimer()
}
意外にも、型が明確なBenchmarkMappingの方が実行速度は遅いという結果になりました。allocateの数も少ないですね。
> go test -v -bench . -benchmem
goos: darwin
goarch: amd64
pkg: github.com/takochuu/firestore-sample
BenchmarkSnapshot-4 1000000 1206 ns/op 728 B/op 9 allocs/op
BenchmarkMapping-4 1000000 1860 ns/op 488 B/op 16 allocs/op
PASS
ok github.com/takochuu/firestore-sample 12.098s
今回はこのあたりで、次回はGoのBenchについてでも書いてみたいと思います。
エウレカのAPIチームでは、Goで実装したくてたまらねぇぜ!って方やビジネスを成功させたいぜ!と思ってるエンジニアと一緒に働きたいと思っています。メンバーを常に募集していますので、少しでも興味を持っていただけましたら、下記Wantedlyからご応募をよろしくおねがいします。