package repository import ( "context" "github.com/Kichiyaki/gopgutil/v10" "github.com/pkg/errors" "github.com/tribalwarshelp/shared/tw/twmodel" "strings" "github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10/orm" "github.com/tribalwarshelp/api/player" ) type pgRepository struct { *pg.DB } func NewPGRepository(db *pg.DB) player.Repository { return &pgRepository{db} } func (repo *pgRepository) Fetch(ctx context.Context, cfg player.FetchConfig) ([]*twmodel.Player, int, error) { var err error data := make([]*twmodel.Player, 0) total := 0 query := repo. WithParam("SERVER", pg.Safe(cfg.Server)). Model(&data). Context(ctx). Limit(cfg.Limit). Offset(cfg.Offset). Apply(cfg.Filter.WhereWithRelations). Apply(gopgutil.OrderAppender{ Orders: cfg.Sort, MaxDepth: 4, }.Apply) if cfg.Count && cfg.Select { total, err = query.SelectAndCount() } else if cfg.Select { err = query.Select() } else if cfg.Count { total, err = query.Count() } if err != nil && err != pg.ErrNoRows { if strings.Contains(err.Error(), `relation "`+cfg.Server) { return nil, 0, errors.New("Server not found") } return nil, 0, errors.New("Internal server error") } return data, total, nil } type fetchPlayerServersQueryResult struct { PlayerID int Servers []string `pg:",array"` } func (repo *pgRepository) FetchNameChanges(ctx context.Context, code twmodel.VersionCode, playerID ...int) (map[int][]*twmodel.PlayerNameChange, error) { data := make([]*twmodel.PlayerNameChange, 0) if err := repo.Model(&data). Context(ctx). Where("version_code = ?", code). Where(gopgutil.BuildConditionArray("player_id"), pg.Array(playerID)). Order("change_date ASC"). Select(); err != nil && err != pg.ErrNoRows { return nil, errors.New("Internal server error") } m := make(map[int][]*twmodel.PlayerNameChange) for _, res := range data { m[res.PlayerID] = append(m[res.PlayerID], res) } return m, nil } func (repo *pgRepository) FetchPlayerServers(ctx context.Context, code twmodel.VersionCode, playerID ...int) (map[int][]string, error) { data := make([]*fetchPlayerServersQueryResult, 0) if err := repo.Model(&twmodel.PlayerToServer{}). Context(ctx). Column("player_id"). ColumnExpr("array_agg(server_key) as servers"). Relation("Server._"). Where("version_code = ?", code). Where(gopgutil.BuildConditionArray("player_id"), pg.Array(playerID)). Group("player_id"). Select(&data); err != nil && err != pg.ErrNoRows { return nil, errors.New("Internal server error") } m := make(map[int][]string) for _, res := range data { m[res.PlayerID] = res.Servers } return m, nil } func (repo *pgRepository) SearchPlayer(ctx context.Context, cfg player.SearchPlayerConfig) ([]*twmodel.FoundPlayer, int, error) { var servers []*twmodel.Server if err := repo. Model(&servers). Context(ctx). Column("key"). Where("version_code = ?", cfg.Version). Select(); err != nil { return nil, 0, errors.New("Internal server error") } var query *orm.Query whereClause := "player.id = ?1 OR player.name ILIKE ?0" if cfg.ID <= 0 { whereClause = "player.name ILIKE ?0" } else if cfg.Name == "" { whereClause = "player.id = ?1" } for _, server := range servers { safeKey := pg.Safe(server.Key) otherQuery := repo. Model(). Context(ctx). ColumnExpr("? AS server", server.Key). ColumnExpr("tribe.tag as tribe_tag"). Column("player.id", "player.name", "player.most_points", "player.best_rank", "player.most_villages", "player.tribe_id"). TableExpr("?0.players as player", safeKey). Join("LEFT JOIN ?0.tribes as tribe ON player.tribe_id = tribe.id", safeKey). Where(whereClause, cfg.Name, cfg.ID) if query == nil { query = otherQuery } else { query = query.UnionAll(otherQuery) } } var err error res := make([]*twmodel.FoundPlayer, 0) count := 0 if query != nil { base := repo. Model(). Context(ctx). With("union_q", query). Table("union_q"). Limit(cfg.Limit). Offset(cfg.Offset). Apply(gopgutil.OrderAppender{ Orders: cfg.Sort, MaxDepth: 4, }.Apply) if cfg.Count { count, err = base.SelectAndCount(&res) } else { err = base.Select(&res) } if err != nil && err != pg.ErrNoRows { return nil, 0, errors.New("Internal server error") } } return res, count, nil }