The server for Open Course World

dyna: add queries for leaderboard searches

mm2srv 36e289fb 24800161

+89 -12
+2 -1
db/dyna/dyna.go
··· 748 748 749 749 func (s *DB) IdsFromPgxQuery(rows pgx.Rows) ([]int64, error) { 750 750 // One liner to get list of ids from pgx query 751 + defer rows.Close() 752 + 751 753 ids := []int64{} 752 754 for rows.Next() { 753 755 var data_id int64 ··· 757 759 } 758 760 ids = append(ids, data_id) 759 761 } 760 - rows.Close() 761 762 762 763 return ids, nil 763 764 }
+83 -9
db/dyna/search_users.go
··· 1 1 package dyna 2 2 3 3 import ( 4 + "context" 5 + "github.com/jackc/pgx/v4" 6 + "smm2_gameserver/nex/connection/user" 4 7 "smm2_gameserver/nex/datastore" 5 8 ) 6 9 10 + func (s *DB) GetUsersAndRanksFromIdRows(pid datastore.Pid, userOption uint32, offset uint32, rows pgx.Rows) ([]datastore.UserInfo, []uint32, bool, error) { 11 + ids, err := s.IdsFromPgxQuery(rows) 12 + if err != nil { 13 + return []datastore.UserInfo{}, []uint32{}, false, err 14 + } 15 + 16 + users := []datastore.UserInfo{} 17 + state := &user.State{Pid: uint64(pid)} 18 + for _, pid := range ids { 19 + u, err := s.GetUserInfoByPid(state, datastore.Pid(pid), userOption) 20 + if err != nil { 21 + return []datastore.UserInfo{}, []uint32{}, false, err 22 + } 23 + users = append(users, u) 24 + } 25 + 26 + if err != nil { 27 + return []datastore.UserInfo{}, []uint32{}, false, err 28 + } 29 + 30 + // this is not real rank but rather list index. same score/rank isn't accounted for 31 + ranks := []uint32{} 32 + for idx, _ := range users { 33 + ranks = append(ranks, offset+uint32(idx+1)) 34 + } 35 + 36 + return users, ranks, true, nil 37 + } 38 + 39 + // Course Makers Weekly 7 40 func (s *DB) SearchUsersTermsRanking(pid datastore.Pid, param datastore.SearchUsersTermsRankingParam) ([]datastore.UserInfo, []uint32, bool, error) { 8 - return []datastore.UserInfo{}, []uint32{}, false, nil 41 + // TODO: select for regions 42 + // this is not really maker stats per last week but maker stats of active users of the last week, 43 + // since we don't have a field maker stats last week 44 + rows, err := s.Conn.Query(context.Background(), "SELECT id FROM user_info WHERE time_registered IS NOT NULL AND last_active > (CURRENT_DATE - INTERVAL '6 days') ORDER BY maker_points DESC LIMIT $1 OFFSET $2", param.Range.Size, param.Range.Offset) 45 + if err != nil { 46 + return []datastore.UserInfo{}, []uint32{}, false, err 47 + } 48 + return s.GetUsersAndRanksFromIdRows(pid, param.Option, param.Range.Offset, rows) 9 49 } 10 50 51 + // Course Makers All Time 11 52 func (s *DB) SearchUsersUserPoint(pid datastore.Pid, param datastore.SearchUsersUserPointParam) ([]datastore.UserInfo, []uint32, bool, error) { 12 - return []datastore.UserInfo{}, []uint32{}, false, nil 53 + // TODO: select for regions 54 + rows, err := s.Conn.Query(context.Background(), "SELECT id FROM user_info WHERE time_registered IS NOT NULL ORDER BY maker_points DESC LIMIT $1 OFFSET $2", param.Range.Size, param.Range.Offset) 55 + if err != nil { 56 + return []datastore.UserInfo{}, []uint32{}, false, err 57 + } 58 + return s.GetUsersAndRanksFromIdRows(pid, param.Option, param.Range.Offset, rows) 13 59 } 14 60 15 61 func (s *DB) SearchUsersClearRanking(pid datastore.Pid, param datastore.SearchUsersClearRankingParam) ([]datastore.UserInfo, []uint32, bool, error) { 16 - return []datastore.UserInfo{}, []uint32{}, false, nil 17 - } 62 + query := "" 63 + switch param.Type { 64 + case 0: // clears 65 + query = "SELECT pid FROM user_stats ORDER BY clears DESC LIMIT $1 OFFSET $2" 66 + case 1: // first clears 67 + query = "SELECT pid FROM user_stats ORDER BY first_clears DESC LIMIT $1 OFFSET $2" 68 + case 2: // world records 69 + query = "SELECT pid FROM user_stats ORDER BY world_records DESC LIMIT $1 OFFSET $2" 70 + } 18 71 19 - func (s *DB) SearchUsersBattleMode(pid datastore.Pid, param datastore.SearchUsersBattleModeParam) ([]datastore.UserInfo, []uint32, bool, error) { 20 - return []datastore.UserInfo{}, []uint32{}, false, nil 72 + // TODO: select for regions 73 + rows, err := s.Conn.Query(context.Background(), query, param.Range.Size, param.Range.Offset) 74 + if err != nil { 75 + return []datastore.UserInfo{}, []uint32{}, false, err 76 + } 77 + return s.GetUsersAndRanksFromIdRows(pid, param.Option, param.Range.Offset, rows) 21 78 } 22 79 23 80 func (s *DB) SearchUsersFolloweeV2(pid datastore.Pid, param datastore.SearchUsersFolloweeV2Param) ([]datastore.UserInfo, []uint32, bool, error) { 24 - return []datastore.UserInfo{}, []uint32{}, false, nil 81 + // TODO: select for regions 82 + rows, err := s.Conn.Query(context.Background(), "SELECT followed_pid FROM player_follow WHERE pid = $1 ORDER BY id DESC LIMIT $2 OFFSET $3", pid, param.Range.Size, param.Range.Offset) 83 + if err != nil { 84 + return []datastore.UserInfo{}, []uint32{}, false, err 85 + } 86 + return s.GetUsersAndRanksFromIdRows(pid, param.Option, param.Range.Offset, rows) 87 + } 88 + 89 + // Players By Endless Difficulty 90 + func (s *DB) SearchUsersEndlessMode(pid datastore.Pid, param datastore.SearchUsersEndlessModeParam) ([]datastore.UserInfo, []uint32, bool, error) { 91 + // TODO: select for regions 92 + rows, err := s.Conn.Query(context.Background(), "SELECT pid FROM endless_stats WHERE difficulty = $1 ORDER BY clears DESC LIMIT $2 OFFSET $3", param.Difficulty, param.Range.Size, param.Range.Offset) 93 + if err != nil { 94 + return []datastore.UserInfo{}, []uint32{}, false, err 95 + } 96 + return s.GetUsersAndRanksFromIdRows(pid, param.Option, param.Range.Offset, rows) 25 97 } 26 98 27 - func (s *DB) SearchUsersOfficial(pid datastore.Pid, param datastore.SearchUsersOfficialParam) ([]datastore.UserInfo, []uint32, bool, error) { 99 + // Versus Players (we have none, return empty list) 100 + func (s *DB) SearchUsersBattleMode(pid datastore.Pid, param datastore.SearchUsersBattleModeParam) ([]datastore.UserInfo, []uint32, bool, error) { 28 101 return []datastore.UserInfo{}, []uint32{}, false, nil 29 102 } 30 103 31 - func (s *DB) SearchUsersEndlessMode(pid datastore.Pid, param datastore.SearchUsersEndlessModeParam) ([]datastore.UserInfo, []uint32, bool, error) { 104 + // Official Nintendo Accounts (we have none, return empty list) 105 + func (s *DB) SearchUsersOfficial(pid datastore.Pid, param datastore.SearchUsersOfficialParam) ([]datastore.UserInfo, []uint32, bool, error) { 32 106 return []datastore.UserInfo{}, []uint32{}, false, nil 33 107 }
+1
db/migrations/000004_user_searches.down.sql
··· 1 + ALTER TABLE IF EXISTS user_info DROP COLUMN maker_points;
+1
db/migrations/000004_user_searches.up.sql
··· 1 + ALTER TABLE IF EXISTS user_info ADD COLUMN maker_points INTEGER DEFAULT 0;
+1 -1
nex/datastore/datastore_smm2.go
··· 736 736 737 737 type SearchUsersEndlessModeParam struct { 738 738 DataStructure uint8 739 - Type uint8 739 + Difficulty uint8 740 740 Option uint32 741 741 IncludeRegions []uint8 742 742 Range ResultRange
+1 -1
nex/datastore/proto/datastore_smm2.proto
··· 326 326 } 327 327 328 328 struct SearchUsersEndlessModeParam { 329 - uint8 type; 329 + uint8 difficulty; 330 330 uint32 option = 0; 331 331 list<uint8> include_regions = []; 332 332 ResultRange range;