使用golang将数据快速以json格式插入到Firestore的故事
首先
我正在玩一款名为魔法少女的手机游戏,随着魔法少女的数量增加,搜索变得越来越困难。
由于游戏内的搜索功能有一些不完善的地方,为了兼顾学习,我开始使用Firebase和Nuxt.js实现了一个可以通过各种方式搜索魔法少女列表的功能。
在准备Firestore的主数据时,我考虑通过管理界面输入数据,但这显然太麻烦了,而且维护也很困难。
考虑到在制作过程中已经创建了用于模拟的JSON文件,所以我决定直接将JSON保存到Firestore中。
因为平时对Golang有兴趣,我试着使用Golang编写了一个可以读取JSON并将其插入或更新到Firestore的应用程序,一切都以JSON为主。
(怎么实现呢?当然是用Go语言写啦!!(仿Mr. Parker Jr的口吻))
在本文中,投入指的是将数据插入或更新到Firestore中。
预谋之物
假设能达到以下状态。
-
- Firebaseでプロジェクトを作成し、Firestore Databaseまでは作成している
- golangが導入済みである(筆者環境は12.6で、動作を確認しています)
准备json文件
原始数据
魔法少女們擁有以下資訊(為了說明而部分省略)
-
- Key(データを探索するときに使用、Firestoreではドキュメント名にも割当します。)
-
- 名前
-
- 属性(火、水、木、光、闇、無)
-
- タイプ(アタック、ディフェンス、バランス、ヒールなど)
- HP、攻撃力、防御力といったステータス
json数据
根据魔法少女的数据创建 JSON。虽然示例只有一个人,但实际上我们要注册多个人,所以将其放入一个数组中。
[
{
"key" : "kaname madoka",
"name" : "鹿目 まどか",
"attribute" : "光",
"type" : "ヒール",
"status" : {
"hp" : 24336,
"attack": 6832,
"defense" : 9276
}
}
]
准备Firestore
有关Firestore的结构
这之后将会向Firestore中插入数据,确定将文档放置在哪个位置以及如何命名文档是处理Firestore时的一个关键点。本次将按以下结构进行创建。
-
- jsonの中身は’private/v1/magicalGirls’に配置する
- ドキュメント名はKey名と同一とする
为了使配置更容易在正式运行时设置安全规则,并且为了在升级版本时能够进行适配,我也还在摸索中,如果有任何建议,请留言。
将文档名称与键名相同是为了方便搜索和更新作为主数据。
如果是交易,可以自动分配文档名称,但主数据更容易发生更改,每次更改后搜索文档名称效率较低。
此外,由于golang端也根据文档名称进行插入和更新操作,因此文档名称与键名相同。
使用Golang将数据插入Firestore。
将JSON数据转换为Golang的类型
首先,我们需要在golang端准备好能够读取所需数据的设定。
在以下網站上,可以根據JSON文件即時轉換成任何語言。快速解析JSON。
这个画面分为左、中、右三个窗格。
在左侧窗格中,输入golang类型名称(由于是数组,可以输入多个名称)到Name。
在左侧窗格中央,输入创建的json。
在右侧窗格中,选择要转换的目标语言,选择Go。
选择后,golang源代码将在中间窗格中生成。
获取Firestore的连接密钥
要想将数据投入Firestore,需要使用与服务帐户关联的包含秘密密钥的JSON数据。获取并不困难,但由于它是秘密密钥,绝不能公开。不能在git或其他地方管理它。
使用Golang连接到Firestore
我想您应该已经注意到了,在生成密钥时,示例源代码中包含有golang的代码。这个示例源代码是关于Firestore的身份验证部分。我们将根据这个示例源代码创建身份验证部分。
首先,我们需要安装Firebase库。
go get -u firebase.google.com/go
对Firebase的身份验证
首先进行Firebase的认证。
创建main.go文件,将示例代码放在main函数中。
若发生错误,将显示错误消息并结束程序。
package main
import (
"context"
"fmt"
"google.golang.org/api/option"
firebase "firebase.google.com/go"
)
func main() {
// Use a service account
ctx := context.Background()
sa := option.WithCredentialsFile("path/to/serviceAccountKey.json")
app, err := firebase.NewApp(ctx, nil, sa)
if err != nil {
fmt.Printf("error initializing app: %v", err)
return
}
}
连接到Firestore
在main函数中添加一个连接到Firestore的客户端。
client, err := app.Firestore(ctx)
if err != nil {
fmt.Printf("error create Firestore client: %v", err)
return
}
defer client.Close()
准备要放入Firestore的数据
将生成的将 JSON 数据转换为 Golang 类型的源代码粘贴到 main.go 文件中。
package main
import (
"context"
"encoding/json"
"fmt"
"google.golang.org/api/option"
firebase "firebase.google.com/go"
)
type MagicalGirls []MagicalGirl
func UnmarshalMagicalGirls(data []byte) (MagicalGirls, error) {
var r MagicalGirls
err := json.Unmarshal(data, &r)
return r, err
}
func (r *MagicalGirls) Marshal() ([]byte, error) {
return json.Marshal(r)
}
type MagicalGirl struct {
Key string `json:"key"`
Name string `json:"name"`
Attribute string `json:"attribute"`
Type string `json:"type"`
Status Status `json:"status"`
}
type Status struct {
HP int64 `json:"hp"`
Attack int64 `json:"attack"`
Defense int64 `json:"defense"`
}
func main() {
.....
将数据导入到Firestore中。
使用Firestore客户端加载和创建的json文件,实现将数据插入的操作。数据将被插入到Firestore准备好的’private/v1/magicalGirls’路径中,以文档名称作为键名。
import (
...
"io/ioutil"
...
)
...
// load json file
bytes, err := ioutil.ReadFile("path/to/magicalGirls.json")
if err != nil {
fmt.Printf("error load magicalGirls.json : %v", err)
}
magicalGirls, err := UnmarshalMagicalGirls(bytes)
if err != nil {
fmt.Printf("error unmarshal magicalGirls.json : %v", err)
}
for i := range magicalGirls {
_, err = client.Collection("private/v1/magicalGirls").Doc(magicalGirls[i].Key).Set(ctx, magicalGirls[i])
if err != nil {
fmt.Printf("error adding magicalGirl: %v", err)
return
}
}
确认投入的数据
在输入数据之后,获取集合中的所有文档并确认是否已注册。
虽然可以在Firebase界面上进行确认,但既然已经开始了,还是从golang端进行获取吧。
通过使用迭代器,可以获取和确认Firestore的数据。
import(
...
"google.golang.org/api/iterator"
...
)
...
iter := client.Collection("private/v1/magicalGirls").Documents(ctx)
for {
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
fmt.Printf("error get magicalGirl documents : %v", err)
}
fmt.Println(doc.Data())
}
创建的 main.go
以下是创建的 main.go。
为了连接 Firebase,我们将秘钥从外部接收。
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"os"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
firebase "firebase.google.com/go"
)
type MagicalGirls []MagicalGirl
func UnmarshalMagicalGirls(data []byte) (MagicalGirls, error) {
var r MagicalGirls
err := json.Unmarshal(data, &r)
return r, err
}
func (r *MagicalGirls) Marshal() ([]byte, error) {
return json.Marshal(r)
}
type MagicalGirl struct {
Key string `json:"key"`
Name string `json:"name"`
Attribute string `json:"attribute"`
Type string `json:"type"`
Status Status `json:"status"`
}
type Status struct {
HP int64 `json:"hp"`
Attack int64 `json:"attack"`
Defense int64 `json:"defense"`
}
func main() {
flag.Parse()
if flag.NArg() != 1 {
fmt.Println("firebase key file not set for Args")
return
}
args := flag.Args()
file := args[0]
_, err := os.Stat(file)
if err != nil {
fmt.Println("error : firebase key file not found")
return
}
// Use a service account
ctx := context.Background()
sa := option.WithCredentialsFile(file)
app, err := firebase.NewApp(ctx, nil, sa)
if err != nil {
fmt.Printf("error initializing app: %v", err)
return
}
client, err := app.Firestore(ctx)
if err != nil {
fmt.Printf("error create Firestore client: %v", err)
return
}
defer client.Close()
// load json file
bytes, err := ioutil.ReadFile("path/to/magicalGirls.json")
if err != nil {
fmt.Printf("error load magicalGirls.json : %v", err)
}
magicalGirls, err := UnmarshalMagicalGirls(bytes)
if err != nil {
fmt.Printf("error unmarshal magicalGirls.json : %v", err)
}
for i := range magicalGirls {
_, err = client.Collection("private/v1/magicalGirls").Doc(magicalGirls[i].Key).Set(ctx, magicalGirls[i])
if err != nil {
fmt.Printf("error adding magicalGirl: %v", err)
return
}
}
iter := client.Collection("private/v1/magicalGirls").Documents(ctx)
for {
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
fmt.Printf("error get magicalGirl documents : %v", err)
}
fmt.Println(doc.Data())
}
}
我們試著實際輸入數據。
在执行之前,我会做一个go get操作。(将GO111MODULE设置为on。)
go get
go ran main.go firebasekeyfile.json
通过Firebase界面查看Firestore时,显示如下:
它会自动为私有(private)和v1等集合(collection),以及文档(document)生成。
最後
我认为很少有机会首次输入数据到JSON中。
但是,我认为能够在没有界面的情况下进行主数据的输入和维护,并且能够进行批量操作,比我想象中的更加方便。
我认为在处理非事务性数据的网站(如列表和计算工具等)中,可能会有一些用途。
额外赠品
由于可以通过JSON插入数据,所以我试了很多东西,感觉很有趣。我将其留作我的备忘录。
-
- ドキュメント名にはスペースを含めることが可能
-
- ドキュメント名に日本語を使ってもOK
- ドキュメント名のみで、ドキュメントの中身がないものは生成できない
请提供更多的上下文。
在制作这个之前,我参考了很多资料。非常感谢。
使用Golang将数据写入Firebase的实时数据库 – Qiita
终于登场了Firebase Admin SDK Go! – Qiita