ソースコードから理解する技術-UnderSourceCode

手を動かす(プログラムを組む)ことで技術を理解するブログ

Firestoreにコレクションをネストしたデータを登録する

はじめに

前回に引き続き、Firestoreについてです。
今回は公式ドキュメントの「サブコレクション」を参考に、コレクションをネストして登録し、参照してみました。

https://firebase.google.com/docs/firestore/data-model?authuser=0

登録するデータの構成は以下のようになります。

「rooms」コレクション
    + 「roomA」ドキュメント
        + 「messages」コレクション
            + 「message1」ドキュメント
                - From: "alex"
		        - Msg:  "Hello World!"
            + 「message2」ドキュメント
                - From: "tom"
		        - Msg:  "Hello Tom!"
    + 「roomB」ドキュメント
        + 「messages」コレクション
            + 「message3」ドキュメント
                - From: "ken"
		        - Msg:  "Hello Ken!"

サンプルプログラム

今回実装したプログラムは以下になります。

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"google.golang.org/api/iterator"

	"cloud.google.com/go/firestore"
	firebase "firebase.google.com/go"
	"google.golang.org/api/option"
)

type Room struct {
	Name string `firestore:"name,omitempty"`
}

type Message struct {
	From string `firestore:"from,omitempty"`
	Msg  string `firestore:"msg,omitempty"`
}

func main() {
	fmt.Println("----- main start -----")

	jsonPath := os.Getenv("FIREBASE_JSON_PATH")

	opt := option.WithCredentialsFile(jsonPath)
	ctx := context.Background()
	app, err := firebase.NewApp(context.Background(), nil, opt)
	if err != nil {
		log.Fatalln(err)
	}

	client, err := app.Firestore(ctx)
	if err != nil {
		log.Fatalln(err)
	}
	defer client.Close()

	err = create(ctx, client)
	if err != nil {
		log.Fatalln(err)
	}

	err = reference(ctx, client)
	if err != nil {
		log.Fatalln(err)
	}

	fmt.Println("----- main end -----")
}

func reference(ctx context.Context, client *firestore.Client) error {
	docs := client.Collection("rooms").Documents(ctx)
	for {
		doc, err := docs.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return err
		}
		fmt.Printf("document id = %s\n", doc.Ref.ID)
		msgDocs := doc.Ref.Collection("messages").Documents(ctx)
		for {
			msgDoc, err := msgDocs.Next()
			if err == iterator.Done {
				break
			}
			if err != nil {
				return err
			}
			fmt.Println(msgDoc.Data())
		}

	}

	return nil
}

func create(ctx context.Context, client *firestore.Client) error {
	roomA := Room{
		Name: "my chat room",
	}
	message1 := Message{
		From: "alex",
		Msg:  "Hello World!",
	}
	_, err := client.Collection("rooms").Doc("roomA").Set(ctx, roomA)
	if err != nil {
		return err
	}
	_, err = client.Collection("rooms").Doc("roomA").Collection("messages").Doc("message1").Set(ctx, message1)
	if err != nil {
		return err
	}

	message2 := Message{
		From: "tom",
		Msg:  "Hello Tom!",
	}
	_, err = client.Collection("rooms").Doc("roomA").Collection("messages").Doc("message2").Set(ctx, message2)
	if err != nil {
		return err
	}

	roomB := Room{
		Name: "beta room",
	}
	message3 := Message{
		From: "ken",
		Msg:  "Hello Ken!",
	}
	_, err = client.Collection("rooms").Doc("roomB").Set(ctx, roomB)
	if err != nil {
		return err
	}
	_, err = client.Collection("rooms").Doc("roomB").Collection("messages").Doc("message3").Set(ctx, message3)
	if err != nil {
		return err
	}

	return nil
}

前回と同様に、秘密鍵jsonのパスを環境変数「FIREBASE_JSON_PATH」に保持しました。
create()で登録を行い、reference()で参照を行なっています。

create()では上記のデータ構成に従ってデータを登録しています。
reference()では固定値として「rooms」「messages」以外はIDを動的に取得して参照するようにしています。

作成したプログラムは以下にも登録しました。
GitHub - SrcHndWng/go-learning-firestore-samples