Go で Cloud Firestore を使ってみる
NoSQL を使ったことがなくて未だにわからないので使ってみる。
開発環境
- go version go1.16.5 linux/amd64
本文
NoSQL を使ったことがなくてよくわかっていないので使ってみる。
何か作るものを想定しないと試すのもできないため、短文投稿しかできないブログ(要するに自分一人しかいない Twitter)を想定する。
まずDB設計なのだが、一旦下記の記事を参考にしようと思って読んだ。
しかし使ったことがないと理解が難しいので、とりあえず下記のようなデータ構造を考えて Firestore のコンソールから投入した。
/posts/:post_id/
{
"text": "短文投稿の内容",
"created": "2021-07-18 16:18:00",
"updated": "2021-07-18 16:18:00"
}
これを Go から読み取るので、公式のガイドに沿ってソースコードを書く。
「Cloud Firestore を初期化する」のところは「各自のサーバーで初期化する」の方法でやる。
詰まるかもしれない点は
sa := option.WithCredentialsFile("path/to/serviceAccount.json")
ここのパスがカレントディレクトリからのパスなのか、ルートディレクトリからのパスなのかわからない。
ルートディレクトリで実行してればルートディレクトリからのパスでいいと思う。
少なくともこのコードが書いてあるファイルからの相対パスではないことは確か。
次にデータの登録は飛ばして読み取りに進むが
iter := client.Collection("users").Documents(ctx)
for {
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("Failed to iterate: %v", err)
}
fmt.Println(doc.Data())
}
この公式サンプルのためには import に
“google.golang.org/api/iterator”
を追加しないといけないので注意。
doc.Data()
の型は map[string]interface{}
になっていた。
ここまで踏まえて「Cloud Firestore から読み取ったデータを html に出力する」というサンプルが下記のようになる。
(見通しを良くすることとかエラー処理とかは考えてない。)
package main
import (
"context"
"fmt"
"html/template"
"log"
"net/http"
firebase "firebase.google.com/go"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
)
var ctx context.Context
var app *firebase.App
type Page struct {
Title string
Content []map[string]interface{}
}
func rootHandler(w http.ResponseWriter, r *http.Request) {
client, err := app.Firestore(ctx)
if err != nil {
log.Fatalln(err)
}
defer client.Close()
iter := client.Collection("posts").Documents(ctx)
var posts []map[string]interface{}
for {
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("Failed to iterate: %v", err)
}
fmt.Println(doc.Data())
posts = append(posts, doc.Data())
}
p := Page{
Title: "Test",
Content: posts,
}
t, err := template.ParseFiles("./web/layout.html")
if err != nil {
panic(err)
}
err = t.Execute(w, p)
if err != nil {
panic(err)
}
}
func main() {
ctx = context.Background()
sa := option.WithCredentialsFile("path/to/serviceAccount.json")
app, err = firebase.NewApp(ctx, nil, sa)
if err != nil {
log.Fatalln(err)
}
http.HandleFunc("/", rootHandler)
http.ListenAndServe(":3000", nil)
}
この結果、下記のようなテンプレートファイル(web/layout.html)を使うと
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{.Title}}</title>
</head>
<body>
<ul>
{{ range .Content }}
<li>
text: {{ .text }}<br>
created: {{ .created }}<br>
updated: {{ .updated }}
</li>
{{ end }}
</ul>
</body>
</html>
出力が下記のようになる。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<ul>
<li>
text: テスト<br>
created: 2021-07-18 07:18:00 +0000 UTC<br>
updated: 2021-07-18 07:18:00 +0000 UTC
</li>
<li>
text: テスト\nテスト<br>
created: 2021-07-18 07:28:00 +0000 UTC<br>
updated: 2021-07-18 07:28:00 +0000 UTC
</li>
</ul>
</body>
</html>
時刻についてはコンソール上だと JST で入力・表示できるのだが、特に何も気にせず読み取ると UTC になった。
ここについては後で調べることにする。
以上