🦑

(golang)firestore databaseのページネーションによるコレクション全件取得

2024/02/21に公開

背景

  • 今まではコレクション内の全データを一括取得していたが、下記のエラーが発生した。
rpc error: code = Unavailable desc = Query 
timed out. Please try either limiting the entities scanned, or run with an updated index configuration.
  • ドキュメントのデータが増え過ぎたことにより、一括取得できなくなったっぽい。
  • なので、一括取得ではなく、ページネーションによる分割取得をデータを取得する必要があった

やりたいこと

  • firestore databaseのドキュメント取得時にページネーションを使って取得したい
  • 取得したコレクションは指定する型のスライスに詰めたい

結論

type Users struct {
	ID             string   `json:"id"`
	CreatedAt      string   `json:"createdAt"`
}

func  FetchUsers() ([]*Users, error) {
	collection := firestoreClient.Collection("users")
	var slice []*Uses
	var lastDocument *fs.DocumentSnapshot
	size := 100
	for {
		query := collection.OrderBy("createdAt", fs.Asc).Limit(size)
		if lastDocument != nil {
			query = query.StartAfter(lastDocument.Data()["createdAt"])
		}
		documents, err := query.Documents(ctx).GetAll()
		if err != nil {
			return nil, errors.Wrap(err)
		}
		for _, doc := range documents {
			b, err := json.Marshal(doc.Data())
			if err != nil {
				return nil, errors.Wrap(err)
			}
			resp := &Users{}
			if err := json.Unmarshal(b, resp); err != nil {
				return nil, errors.Wrap(err)
			}
			slice = append(slice, resp)
		}
		if len(documents) != size {
			break
		}
		lastDocument = documents[(len(documents) - 1)]
	}
	return slice, nil
}

その他

  • 上記のコードはこのリンクの良コードを参考に構造体でパース+スライスに詰め詰めの処理を追加しただけです。
    • この記事で素晴らしいなと思った点は
      • 公式にもページネーションを用いた実装はあるが、初回処理とそれ以降の処理をfor文で綺麗にまとめてる
      • ページネーションの終了のトリガーをlimit実際に取得した件数を比較しているアイディアが素敵

Discussion