こんにちは、KOUKIです。
GolangのWebフレームワークであるfiberを使って、APIを開発しています。
前回は、LinkとStatsの実装を行いました。
今回は、Ranking APIとRedisのソート処理の実装を行います。
尚、本記事は「React, NextJS and Golang: A Rapid Guide – Advanced」コースを参考にしています。解釈は私が勝手に付けているので、本物をみたい場合は受講をお勧めします!
<目次>
前回
事前準備
フォルダ/ファイルの作成
1 2 |
mkdir src/commands/redis touch src/commands/redis/updateRankings.go |
作るもの
Ambassdor機能を作りたいと思います。エンドポイントは、次の通りです。
- GET /api/ambassador/products/frontend
- GET /api/ambassador/products/backend
- POST /api/ambassador/links
- GET /api/ambassador/stats
- GET /api/ambassador/rankings
今回は、「/api/ambassador/rankings」への処理を実装します。
Ranking
ユーザーごとの売り上げランキングを返すAPIを実装しましょう。
ルートの追加
「/api/ambassador/rankings」へのルートを追加しましょう。
1 2 3 4 5 6 7 8 |
// routes/routes.go ... func Setup(app *fiber.App) { ... // Ambassador ... ambassadorAuthentication.Get("rankings", controllers.Ranking) } |
コントローラーの追加
Rankingを算出するコントローラーを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// userController.g func Ranking(ctx *fiber.Ctx) error { var users []models.User // ambassadorデータを検索 database.DB.Find(&users, models.User{ IsAmbassador: true, }) var results []interface{} for _, user := range users { // userにambassadorの型を持たせる ambassador := models.Ambassador(user) // Revenueを計算 ambassador.CalculateRevenue(database.DB) // key: ユーザー名, value: お買い上げ金 results = append(results, fiber.Map{ user.Name(): ambassador.Revenue, }) } return ctx.JSON(results) } |
「models.Ambassador(user)」の処理ですが、以前作成したAmbassadorリンクをuserデータに紐づけています。こんな書き方ができることを初めて知りました。
1 2 |
// models/user.go type Ambassador User |
また、userモデルにNameメソッドを追加します。
1 2 3 4 |
// models/user.go func (u *User) Name() string { return u.FirstName + " " + u.LastName } |
検証
以下のパラメーターで、検証してみましょう。
- URL: http://localhost:8000/api/ambassador/rankings
- 形式: GET

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
[ { "Orie Pfeffer": 96.3 }, { "Lilliana Cartwright": 0 }, { "Rusty Rohan": 6.7 }, { "Leonel Bechtelar": 0 }, { "Mohammed Sawayn": 0 }, { "Joannie Turner": 58 }, { "Jermey Donnelly": 0 }, { "Felix Gleason": 0 }, { "Vernie O\"Hara": 34.6 }, { "Thea Bruen": 18.700000000000003 }, { "Charley Legros": 65.80000000000001 }, { "Harold Barton": 0 }, { "Carmelo Nikolaus": 0 }, { "Rowan Cartwright": 6.800000000000001 }, { "Lorna Cormier": 0 }, { "Imelda Kunde": 0 }, { "Shaun Powlowski": 28.800000000000004 }, { "Melvin Marvin": 0 }, { "Jefferey Bernhard": 0 }, { "Kenna Kiehn": 0 }, { "Reggie Johnston": 0 }, { "Howard Turner": 74.9 }, { "Josianne Reynolds": 0 }, { "Sage Frami": 0 }, { "Curt Stehr": 0 }, { "Ford McClure": 38.2 }, { "Nikko Bauch": 5.2 }, { "Larry Denesik": 0 }, { "Dion Oberbrunner": 0 }, { "Peyton Fay": 7 } ] |
OKですね。
補足: データの投入について
MySQLのデータは、.dbdataに保存されています。MySQLのデータを削除したい時は、このフォルダを削除してください。
削除後に、一度Dockerを立ち上げてMySQLの初期化を済ませます。次にもう一度Dockerを立ち上げ直して、以下のコマンドを実行するとテストデータを投入することが可能です。
1 2 3 |
docker-compose run --rm backend sh -c "go run src/commands/user/populateUsers.go" docker-compose run --rm backend sh -c "go run src/commands/product/populateProducts.go" docker-compose run --rm backend sh -c "go run src/commands/order/populateOrders.go" |
Redisのソート
Redisには、データをソートする機能があります。
※ 参考 – Sorted sets 101
この機能を使って、先ほど取得したRankingデータをアルファベット順に並び替えてみましょう。
ソートプログラムの作成
Redisのソートプログラムを実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// commands/redis/updateRankings.go package main import ( "admin/src/database" "admin/src/models" "context" "github.com/go-redis/redis/v8" ) func main() { database.Connect() database.SetupRedis() ctx := context.Background() var users []models.User database.DB.Find(&users, models.User{ IsAmbassador: true, }) for _, user := range users { ambassador := models.Ambassador(user) ambassador.CalculateRevenue(database.DB) // ZADD key // http://mogile.web.fc2.com/redis/commands/zadd.html#sorted-sets-101 database.Cache.ZAdd(ctx, "rankings", &redis.Z{ Score: *ambassador.Revenue, Member: user.Name(), }) } } |
ここでは、”rankings”というキーワードを関連付けたRankingデータを作成しています。※先ほど実装したRanking関数と同じようなものです。※ 参考 – ZAdd
Rankingコントローラーの修正
キャッシュ(Redis)からデータを取得する必要があるので、先ほど実装したRankingコントローラーを修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
func Ranking(ctx *fiber.Ctx) error { // zrevrangebyscore //http://mogile.web.fc2.com/redis/commands/zrevrangebyscore.html rankings, err := database.Cache.ZRevRangeByScoreWithScores( context.Background(), "rankings", // updateRankings.goでkeyとして設定した値 &redis.ZRangeBy{ Min: "-inf", Max: "+inf", }).Result() if err != nil { return err } result := make(map[string]float64) for _, ranking := range rankings { result[ranking.Member.(string)] = ranking.Score } return ctx.JSON(result) } |
ZRevRangeByScoreWithScoresは、min/maxの間にある全ての対象要素のデータを返却するようです。ちなみに、infは保持している値全てを取得するキーワードで、min/maxに「-inf,+inf」の形式で指定できます。
Makefile更新
Makefileに以下のコマンドを追加しましょう。
1 2 3 4 5 6 7 8 9 10 |
.PHONY: create-user create-product create-order update-ranking up # テストデータ作成 ... update-ranking: docker-compose run --rm backend sh -c "go run src/commands/redis/updateRankings.go" # コンテナ起動 ... |
検証
以下のコマンドを実行してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
$ make update-ranking docker-compose run --rm backend sh -c "go run src/commands/redis/updateRankings.go" Creating go-admin_backend_run ... done ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
次に、以下のパラメーターで、検証してみましょう。
- URL: http://localhost:8000/api/ambassador/rankings
- 形式: GET

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
{ "Carmelo Nikolaus": 0, "Charley Legros": 65.80000000000001, "Curt Stehr": 0, "Dion Oberbrunner": 0, "Felix Gleason": 0, "Ford McClure": 38.2, "Harold Barton": 0, "Howard Turner": 74.9, "Imelda Kunde": 0, "Jefferey Bernhard": 0, "Jermey Donnelly": 0, "Joannie Turner": 58, "Josianne Reynolds": 0, "Kenna Kiehn": 0, "Larry Denesik": 0, "Leonel Bechtelar": 0, "Lilliana Cartwright": 0, "Lorna Cormier": 0, "Melvin Marvin": 0, "Mohammed Sawayn": 0, "Nikko Bauch": 5.2, "Orie Pfeffer": 96.3, "Peyton Fay": 7, "Reggie Johnston": 0, "Rowan Cartwright": 6.800000000000001, "Rusty Rohan": 6.7, "Sage Frami": 0, "Shaun Powlowski": 28.800000000000004, "Thea Bruen": 18.700000000000003, "Vernie O\"Hara": 34.6 } |
OK! 名前のアルファベット順で、ソートされましたね。
次回
次回は、Checkout APIを実装しましょう。
Go言語まとめ
ソースコード
ここまでのソースコードを以下に記載します。
updateRankings.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// commands/redis/updateRankings.go package main import ( "admin/src/database" "admin/src/models" "context" "github.com/go-redis/redis/v8" ) func main() { database.Connect() database.SetupRedis() ctx := context.Background() var users []models.User database.DB.Find(&users, models.User{ IsAmbassador: true, }) for _, user := range users { ambassador := models.Ambassador(user) ambassador.CalculateRevenue(database.DB) // ZADD key // http://mogile.web.fc2.com/redis/commands/zadd.html#sorted-sets-101 database.Cache.ZAdd(ctx, "rankings", &redis.Z{ Score: *ambassador.Revenue, Member: user.Name(), }) } } |
userController.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
// userController.go package controllers import ( "admin/src/database" "admin/src/models" "context" "github.com/go-redis/redis/v8" "github.com/gofiber/fiber/v2" ) func Ambassadors(ctx *fiber.Ctx) error { var users []models.User database.DB.Where("is_ambassador = true").Find(&users) return ctx.JSON(users) } func Ranking(ctx *fiber.Ctx) error { // zrevrangebyscore //http://mogile.web.fc2.com/redis/commands/zrevrangebyscore.html rankings, err := database.Cache.ZRevRangeByScoreWithScores( context.Background(), "rankings", // updateRankings.goでkeyとして設定した値 &redis.ZRangeBy{ Min: "-inf", Max: "+inf", }).Result() if err != nil { return err } result := make(map[string]float64) for _, ranking := range rankings { result[ranking.Member.(string)] = ranking.Score } return ctx.JSON(result) } |
Makefile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
.PHONY: create-user create-product create-order update-ranking up # テストデータ作成 create-user: docker-compose run --rm backend sh -c "go run src/commands/user/populateUsers.go" create-product: docker-compose run --rm backend sh -c "go run src/commands/product/populateProducts.go" create-order: docker-compose run --rm backend sh -c "go run src/commands/order/populateOrders.go" update-ranking: docker-compose run --rm backend sh -c "go run src/commands/redis/updateRankings.go" # コンテナ起動 up: docker-compose up |
コメントを残す
コメントを投稿するにはログインしてください。