[WIP] - refactor limit, sort, offset

- update the Player/Ennoblement repository and usecase
This commit is contained in:
Dawid Wysokiński 2020-11-21 10:08:22 +01:00
parent 01fdd80f7f
commit a854a4b80a
17 changed files with 266 additions and 75 deletions

View File

@ -31,8 +31,8 @@ func (repo *pgRepository) Fetch(ctx context.Context, cfg dailyplayerstats.FetchC
Order(cfg.Sort...).
Limit(cfg.Limit).
Offset(cfg.Offset)
playerRequired := utils.FindStringWithPrefix(cfg.Sort, "player") != ""
tribeRequired := utils.FindStringWithPrefix(cfg.Sort, "tribe") != ""
playerRequired := utils.FindStringWithPrefix(cfg.Sort, "player.") != ""
tribeRequired := utils.FindStringWithPrefix(cfg.Sort, "tribe.") != ""
if cfg.Filter != nil {
query = query.

View File

@ -31,7 +31,7 @@ func (repo *pgRepository) Fetch(ctx context.Context, cfg dailytribestats.FetchCo
Order(cfg.Sort...).
Limit(cfg.Limit).
Offset(cfg.Offset)
tribeRequired := utils.FindStringWithPrefix(cfg.Sort, "tribe") != ""
tribeRequired := utils.FindStringWithPrefix(cfg.Sort, "tribe.") != ""
if cfg.Filter != nil {
query = query.

View File

@ -10,6 +10,9 @@ type FetchConfig struct {
Server string
Filter *models.EnnoblementFilter
Count bool
Sort []string
Limit int
Offset int
}
type Repository interface {

View File

@ -7,7 +7,7 @@ import (
"github.com/tribalwarshelp/shared/models"
)
func appendEnnoblementFilterOr(or *models.EnnoblementFilterOr) func(*orm.Query) (*orm.Query, error) {
func appendOrFilter(or *models.EnnoblementFilterOr) func(*orm.Query) (*orm.Query, error) {
return func(q *orm.Query) (*orm.Query, error) {
if or != nil {
if len(or.NewOwnerID) > 0 {

View File

@ -8,6 +8,7 @@ import (
"github.com/go-pg/pg/v10"
"github.com/pkg/errors"
"github.com/tribalwarshelp/api/ennoblement"
"github.com/tribalwarshelp/api/utils"
"github.com/tribalwarshelp/shared/models"
)
@ -23,19 +24,39 @@ func (repo *pgRepository) Fetch(ctx context.Context, cfg ennoblement.FetchConfig
var err error
total := 0
data := []*models.Ennoblement{}
query := repo.WithParam("SERVER", pg.Safe(cfg.Server)).Model(&data).Context(ctx)
query := repo.
WithParam("SERVER", pg.Safe(cfg.Server)).
Model(&data).
Context(ctx).
Order(cfg.Sort...).
Limit(cfg.Limit).
Offset(cfg.Offset)
villageRequired := utils.FindStringWithPrefix(cfg.Sort, "village.") != ""
newOwnerRequired := utils.FindStringWithPrefix(cfg.Sort, "new_owner.") != ""
newOwnerTribeRequired := utils.FindStringWithPrefix(cfg.Sort, "new_owner_tribe.") != ""
oldOwnerRequired := utils.FindStringWithPrefix(cfg.Sort, "old_owner.") != ""
oldOwnerTribeRequired := utils.FindStringWithPrefix(cfg.Sort, "old_owner_tribe.") != ""
if cfg.Filter != nil {
query = query.
WhereStruct(cfg.Filter).
Limit(cfg.Filter.Limit).
Offset(cfg.Filter.Offset)
WhereGroup(appendOrFilter(cfg.Filter.Or))
}
if cfg.Filter.Sort != "" {
query = query.Order(cfg.Filter.Sort)
}
query = query.WhereGroup(appendEnnoblementFilterOr(cfg.Filter.Or))
if villageRequired {
query = query.Relation("Village._")
}
if newOwnerRequired {
query = query.Join("LEFT JOIN ?SERVER.players AS new_owner ON new_owner.id = ennoblement.new_owner_id")
}
if newOwnerTribeRequired {
query = query.Join("LEFT JOIN ?SERVER.tribes AS new_owner_tribe ON new_owner_tribe.id = ennoblement.new_owner_tribe_id")
}
if oldOwnerRequired {
query = query.Join("LEFT JOIN ?SERVER.players AS old_owner ON old_owner.id = ennoblement.old_owner_id")
}
if oldOwnerTribeRequired {
query = query.Join("LEFT JOIN ?SERVER.tribes AS old_owner_tribe ON old_owner_tribe.id = ennoblement.old_owner_tribe_id")
}
if cfg.Count {

View File

@ -7,5 +7,5 @@ import (
)
type Usecase interface {
Fetch(ctx context.Context, server string, filter *models.EnnoblementFilter) ([]*models.Ennoblement, int, error)
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.Ennoblement, int, error)
}

View File

@ -17,17 +17,23 @@ func New(repo ennoblement.Repository) ennoblement.Usecase {
return &usecase{repo}
}
func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.EnnoblementFilter) ([]*models.Ennoblement, int, error) {
if filter == nil {
filter = &models.EnnoblementFilter{}
func (ucase *usecase) Fetch(ctx context.Context, cfg ennoblement.FetchConfig) ([]*models.Ennoblement, int, error) {
if cfg.Filter == nil {
cfg.Filter = &models.EnnoblementFilter{}
}
if !middleware.CanExceedLimit(ctx) && (filter.Limit > ennoblement.PaginationLimit || filter.Limit <= 0) {
filter.Limit = ennoblement.PaginationLimit
if cfg.Filter.Limit > 0 {
cfg.Limit = cfg.Filter.Limit
}
filter.Sort = utils.SanitizeSortExpression(filter.Sort)
return ucase.repo.Fetch(ctx, ennoblement.FetchConfig{
Server: server,
Filter: filter,
Count: true,
})
if cfg.Filter.Offset > 0 {
cfg.Offset = cfg.Filter.Offset
}
if cfg.Filter.Sort != "" {
cfg.Sort = append(cfg.Sort, cfg.Filter.Sort)
}
if !middleware.CanExceedLimit(ctx) && (cfg.Limit > ennoblement.PaginationLimit || cfg.Limit <= 0) {
cfg.Limit = ennoblement.PaginationLimit
}
cfg.Sort = utils.SanitizeSortExpressions(cfg.Sort)
return ucase.repo.Fetch(ctx, cfg)
}

View File

@ -218,13 +218,13 @@ type ComplexityRoot struct {
Query struct {
DailyPlayerStats func(childComplexity int, server string, filter *models.DailyPlayerStatsFilter, limit *int, offset *int, sort []string) int
DailyTribeStats func(childComplexity int, server string, filter *models.DailyTribeStatsFilter, limit *int, offset *int, sort []string) int
Ennoblements func(childComplexity int, server string, filter *models.EnnoblementFilter) int
Ennoblements func(childComplexity int, server string, filter *models.EnnoblementFilter, limit *int, offset *int, sort []string) int
LangVersion func(childComplexity int, tag models.VersionCode) int
LangVersions func(childComplexity int, filter *models.VersionFilter) int
LiveEnnoblements func(childComplexity int, server string) int
Player func(childComplexity int, server string, id int) int
PlayerHistory func(childComplexity int, server string, filter *models.PlayerHistoryFilter) int
Players func(childComplexity int, server string, filter *models.PlayerFilter) int
Players func(childComplexity int, server string, filter *models.PlayerFilter, limit *int, offset *int, sort []string) int
Server func(childComplexity int, key string) int
ServerStats func(childComplexity int, server string, filter *models.ServerStatsFilter) int
Servers func(childComplexity int, filter *models.ServerFilter) int
@ -581,9 +581,9 @@ type PlayerHistoryRecordResolver interface {
type QueryResolver interface {
DailyPlayerStats(ctx context.Context, server string, filter *models.DailyPlayerStatsFilter, limit *int, offset *int, sort []string) (*DailyPlayerStats, error)
DailyTribeStats(ctx context.Context, server string, filter *models.DailyTribeStatsFilter, limit *int, offset *int, sort []string) (*DailyTribeStats, error)
Ennoblements(ctx context.Context, server string, filter *models.EnnoblementFilter) (*EnnoblementList, error)
Ennoblements(ctx context.Context, server string, filter *models.EnnoblementFilter, limit *int, offset *int, sort []string) (*EnnoblementList, error)
LiveEnnoblements(ctx context.Context, server string) ([]*models.LiveEnnoblement, error)
Players(ctx context.Context, server string, filter *models.PlayerFilter) (*PlayerList, error)
Players(ctx context.Context, server string, filter *models.PlayerFilter, limit *int, offset *int, sort []string) (*PlayerList, error)
Player(ctx context.Context, server string, id int) (*models.Player, error)
PlayerHistory(ctx context.Context, server string, filter *models.PlayerHistoryFilter) (*PlayerHistory, error)
Servers(ctx context.Context, filter *models.ServerFilter) (*ServerList, error)
@ -1501,7 +1501,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return 0, false
}
return e.complexity.Query.Ennoblements(childComplexity, args["server"].(string), args["filter"].(*models.EnnoblementFilter)), true
return e.complexity.Query.Ennoblements(childComplexity, args["server"].(string), args["filter"].(*models.EnnoblementFilter), args["limit"].(*int), args["offset"].(*int), args["sort"].([]string)), true
case "Query.langVersion":
if e.complexity.Query.LangVersion == nil {
@ -1573,7 +1573,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return 0, false
}
return e.complexity.Query.Players(childComplexity, args["server"].(string), args["filter"].(*models.PlayerFilter)), true
return e.complexity.Query.Players(childComplexity, args["server"].(string), args["filter"].(*models.PlayerFilter), args["limit"].(*int), args["offset"].(*int), args["sort"].([]string)), true
case "Query.server":
if e.complexity.Query.Server == nil {
@ -3482,12 +3482,27 @@ input EnnoblementFilter {
or: EnnoblementFilterOr
offset: Int
@deprecated(
reason: "Use a new variable added to the query ennoblements - ` + "`" + `offset` + "`" + `."
)
limit: Int
@deprecated(
reason: "Use a new variable added to the query ennoblements - ` + "`" + `limit` + "`" + `."
)
sort: String
@deprecated(
reason: "Use a new variable added to the query ennoblements - ` + "`" + `sort` + "`" + `."
)
}
extend type Query {
ennoblements(server: String!, filter: EnnoblementFilter): EnnoblementList!
ennoblements(
server: String!
filter: EnnoblementFilter
limit: Int
offset: Int
sort: [String!]
): EnnoblementList!
}
`, BuiltIn: false},
{Name: "schema/live_ennoblement.graphql", Input: `type LiveEnnoblement {
@ -3640,12 +3655,27 @@ input PlayerFilter {
tribeFilter: TribeFilter
offset: Int
@deprecated(
reason: "Use a new variable added to the query players - ` + "`" + `offset` + "`" + `."
)
limit: Int
@deprecated(
reason: "Use a new variable added to the query players - ` + "`" + `limit` + "`" + `."
)
sort: String
@deprecated(
reason: "Use a new variable added to the query players - ` + "`" + `sort` + "`" + `."
)
}
extend type Query {
players(server: String!, filter: PlayerFilter): PlayerList!
players(
server: String!
filter: PlayerFilter
limit: Int
offset: Int
sort: [String!]
): PlayerList!
player(server: String!, id: Int!): Player
}
`, BuiltIn: false},
@ -4445,6 +4475,33 @@ func (ec *executionContext) field_Query_ennoblements_args(ctx context.Context, r
}
}
args["filter"] = arg1
var arg2 *int
if tmp, ok := rawArgs["limit"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("limit"))
arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp)
if err != nil {
return nil, err
}
}
args["limit"] = arg2
var arg3 *int
if tmp, ok := rawArgs["offset"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("offset"))
arg3, err = ec.unmarshalOInt2ᚖint(ctx, tmp)
if err != nil {
return nil, err
}
}
args["offset"] = arg3
var arg4 []string
if tmp, ok := rawArgs["sort"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("sort"))
arg4, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp)
if err != nil {
return nil, err
}
}
args["sort"] = arg4
return args, nil
}
@ -4562,6 +4619,33 @@ func (ec *executionContext) field_Query_players_args(ctx context.Context, rawArg
}
}
args["filter"] = arg1
var arg2 *int
if tmp, ok := rawArgs["limit"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("limit"))
arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp)
if err != nil {
return nil, err
}
}
args["limit"] = arg2
var arg3 *int
if tmp, ok := rawArgs["offset"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("offset"))
arg3, err = ec.unmarshalOInt2ᚖint(ctx, tmp)
if err != nil {
return nil, err
}
}
args["offset"] = arg3
var arg4 []string
if tmp, ok := rawArgs["sort"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("sort"))
arg4, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp)
if err != nil {
return nil, err
}
}
args["sort"] = arg4
return args, nil
}
@ -9048,7 +9132,7 @@ func (ec *executionContext) _Query_ennoblements(ctx context.Context, field graph
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().Ennoblements(rctx, args["server"].(string), args["filter"].(*models.EnnoblementFilter))
return ec.resolvers.Query().Ennoblements(rctx, args["server"].(string), args["filter"].(*models.EnnoblementFilter), args["limit"].(*int), args["offset"].(*int), args["sort"].([]string))
})
if err != nil {
ec.Error(ctx, err)
@ -9129,7 +9213,7 @@ func (ec *executionContext) _Query_players(ctx context.Context, field graphql.Co
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().Players(rctx, args["server"].(string), args["filter"].(*models.PlayerFilter))
return ec.resolvers.Query().Players(rctx, args["server"].(string), args["filter"].(*models.PlayerFilter), args["limit"].(*int), args["offset"].(*int), args["sort"].([]string))
})
if err != nil {
ec.Error(ctx, err)

View File

@ -3,6 +3,7 @@ package resolvers
import (
"context"
"github.com/tribalwarshelp/api/ennoblement"
"github.com/tribalwarshelp/api/graphql/generated"
"github.com/tribalwarshelp/shared/models"
)
@ -47,9 +48,29 @@ func (r *ennoblementResolver) Village(ctx context.Context, obj *models.Ennobleme
return getVillage(ctx, obj.VillageID), nil
}
func (r *queryResolver) Ennoblements(ctx context.Context, server string, f *models.EnnoblementFilter) (*generated.EnnoblementList, error) {
func (r *queryResolver) Ennoblements(ctx context.Context, server string,
f *models.EnnoblementFilter,
limit *int,
offset *int,
sort []string) (*generated.EnnoblementList, error) {
defLimit := 0
defOffset := 0
if limit == nil {
limit = &defLimit
}
if offset == nil {
offset = &defOffset
}
var err error
list := &generated.EnnoblementList{}
list.Items, list.Total, err = r.EnnoblementUcase.Fetch(ctx, server, f)
list.Items, list.Total, err = r.EnnoblementUcase.Fetch(ctx, ennoblement.FetchConfig{
Server: server,
Filter: f,
Sort: sort,
Limit: *limit,
Offset: *offset,
Count: true,
})
return list, err
}

View File

@ -5,6 +5,7 @@ import (
"github.com/tribalwarshelp/api/graphql/generated"
"github.com/tribalwarshelp/api/middleware"
"github.com/tribalwarshelp/api/player"
"github.com/tribalwarshelp/shared/models"
"github.com/tribalwarshelp/shared/tw"
)
@ -45,10 +46,31 @@ func (r *playerResolver) NameChanges(ctx context.Context, obj *models.Player) ([
return []*models.PlayerNameChange{}, nil
}
func (r *queryResolver) Players(ctx context.Context, server string, filter *models.PlayerFilter) (*generated.PlayerList, error) {
func (r *queryResolver) Players(ctx context.Context,
server string,
f *models.PlayerFilter,
limit *int,
offset *int,
sort []string) (*generated.PlayerList, error) {
defLimit := 0
defOffset := 0
if limit == nil {
limit = &defLimit
}
if offset == nil {
offset = &defOffset
}
var err error
list := &generated.PlayerList{}
list.Items, list.Total, err = r.PlayerUcase.Fetch(ctx, server, filter)
list.Items, list.Total, err = r.PlayerUcase.Fetch(ctx, player.FetchConfig{
Server: server,
Filter: f,
Sort: sort,
Limit: *limit,
Offset: *offset,
Count: true,
})
return list, err
}

View File

@ -42,10 +42,25 @@ input EnnoblementFilter {
or: EnnoblementFilterOr
offset: Int
@deprecated(
reason: "Use a new variable added to the query ennoblements - `offset`."
)
limit: Int
@deprecated(
reason: "Use a new variable added to the query ennoblements - `limit`."
)
sort: String
@deprecated(
reason: "Use a new variable added to the query ennoblements - `sort`."
)
}
extend type Query {
ennoblements(server: String!, filter: EnnoblementFilter): EnnoblementList!
ennoblements(
server: String!
filter: EnnoblementFilter
limit: Int
offset: Int
sort: [String!]
): EnnoblementList!
}

View File

@ -137,11 +137,26 @@ input PlayerFilter {
tribeFilter: TribeFilter
offset: Int
@deprecated(
reason: "Use a new variable added to the query players - `offset`."
)
limit: Int
@deprecated(
reason: "Use a new variable added to the query players - `limit`."
)
sort: String
@deprecated(
reason: "Use a new variable added to the query players - `sort`."
)
}
extend type Query {
players(server: String!, filter: PlayerFilter): PlayerList!
players(
server: String!
filter: PlayerFilter
limit: Int
offset: Int
sort: [String!]
): PlayerList!
player(server: String!, id: Int!): Player
}

View File

@ -10,6 +10,9 @@ type FetchConfig struct {
Server string
Filter *models.PlayerFilter
Count bool
Sort []string
Limit int
Offset int
}
type Repository interface {

View File

@ -8,6 +8,7 @@ import (
"github.com/go-pg/pg/v10"
"github.com/pkg/errors"
"github.com/tribalwarshelp/api/player"
"github.com/tribalwarshelp/api/utils"
"github.com/tribalwarshelp/shared/models"
)
@ -23,33 +24,29 @@ func (repo *pgRepository) Fetch(ctx context.Context, cfg player.FetchConfig) ([]
var err error
data := []*models.Player{}
total := 0
query := repo.WithParam("SERVER", pg.Safe(cfg.Server)).Model(&data).Context(ctx)
query := repo.
WithParam("SERVER", pg.Safe(cfg.Server)).
Model(&data).
Context(ctx).
Order(cfg.Sort...).
Limit(cfg.Limit).
Offset(cfg.Offset)
tribeRequired := utils.FindStringWithPrefix(cfg.Sort, "tribe.") != ""
if cfg.Filter != nil {
query = query.
WhereStruct(cfg.Filter).
Limit(cfg.Filter.Limit).
Offset(cfg.Filter.Offset)
WhereStruct(cfg.Filter)
if cfg.Filter.Exists != nil {
query = query.Where("exists = ?", *cfg.Filter.Exists)
}
order := []string{}
if cfg.Filter.Sort != "" {
order = append(order, cfg.Filter.Sort)
}
if cfg.Filter.TribeFilter != nil {
query = query.Relation("Tribe._").WhereStruct(cfg.Filter.TribeFilter)
if cfg.Filter.TribeFilter.Sort != "" {
order = append(order, fmt.Sprintf("tribe.%s", cfg.Filter.TribeFilter.Sort))
}
tribeRequired = true
query = query.WhereStruct(cfg.Filter.TribeFilter)
}
query = query.Order(order...)
}
if tribeRequired {
query = query.Relation("Tribe._")
}
if cfg.Count {

View File

@ -7,6 +7,6 @@ import (
)
type Usecase interface {
Fetch(ctx context.Context, server string, filter *models.PlayerFilter) ([]*models.Player, int, error)
Fetch(ctx context.Context, cfg FetchConfig) ([]*models.Player, int, error)
GetByID(ctx context.Context, server string, id int) (*models.Player, error)
}

View File

@ -18,31 +18,35 @@ func New(repo player.Repository) player.Usecase {
return &usecase{repo}
}
func (ucase *usecase) Fetch(ctx context.Context, server string, filter *models.PlayerFilter) ([]*models.Player, int, error) {
if filter == nil {
filter = &models.PlayerFilter{}
func (ucase *usecase) Fetch(ctx context.Context, cfg player.FetchConfig) ([]*models.Player, int, error) {
if cfg.Filter == nil {
cfg.Filter = &models.PlayerFilter{}
}
if !middleware.CanExceedLimit(ctx) && (filter.Limit > player.PaginationLimit || filter.Limit <= 0) {
filter.Limit = player.PaginationLimit
if cfg.Filter.Limit > 0 {
cfg.Limit = cfg.Filter.Limit
}
filter.Sort = utils.SanitizeSortExpression(filter.Sort)
if filter.TribeFilter != nil {
filter.TribeFilter.Sort = utils.SanitizeSortExpression(filter.TribeFilter.Sort)
if cfg.Filter.Offset > 0 {
cfg.Offset = cfg.Filter.Offset
}
return ucase.repo.Fetch(ctx, player.FetchConfig{
Server: server,
Filter: filter,
Count: true,
})
if cfg.Filter.Sort != "" {
cfg.Sort = append(cfg.Sort, cfg.Filter.Sort)
}
if !middleware.CanExceedLimit(ctx) && (cfg.Limit > player.PaginationLimit || cfg.Limit <= 0) {
cfg.Limit = player.PaginationLimit
}
cfg.Sort = utils.SanitizeSortExpressions(cfg.Sort)
return ucase.repo.Fetch(ctx, cfg)
}
func (ucase *usecase) GetByID(ctx context.Context, server string, id int) (*models.Player, error) {
players, _, err := ucase.repo.Fetch(ctx, player.FetchConfig{
Server: server,
Filter: &models.PlayerFilter{
ID: []int{id},
Limit: 1,
ID: []int{id},
},
Limit: 1,
Count: false,
})
if err != nil {
return nil, err

View File

@ -20,7 +20,7 @@ func SanitizeSortExpression(expr string) string {
column := splitted[0]
if strings.Contains(splitted[0], ".") {
columnAndTable := strings.Split(splitted[0], ".")
table = columnAndTable[0] + "."
table = Underscore(columnAndTable[0]) + "."
column = columnAndTable[1]
}
keyword := "ASC"