package bundb import ( "context" "database/sql" "errors" "fmt" "gitea.dwysokinski.me/twhelp/core/internal/bundb/internal/model" "gitea.dwysokinski.me/twhelp/core/internal/domain" "github.com/uptrace/bun" ) var ( villageMetaColumns = []string{"id", "name", "x", "y", "continent", "profile_url"} villageOrders = []string{"village.server_key ASC", "village.id ASC"} ) type Village struct { db *bun.DB } func NewVillage(db *bun.DB) *Village { return &Village{db: db} } func (v *Village) CreateOrUpdate(ctx context.Context, params ...domain.CreateVillageParams) error { if len(params) == 0 { return nil } villages := make([]model.Village, 0, len(params)) for _, p := range params { villages = append(villages, model.NewVillage(p)) } if _, err := v.db.NewInsert(). Model(&villages). On("CONFLICT ON CONSTRAINT villages_pkey DO UPDATE"). Set("name = EXCLUDED.name"). Set("points = EXCLUDED.points"). Set("x = EXCLUDED.x"). Set("y = EXCLUDED.y"). Set("continent = EXCLUDED.continent"). Set("bonus = EXCLUDED.bonus"). Set("player_id = EXCLUDED.player_id"). Set("profile_url = EXCLUDED.profile_url"). Returning("NULL"). Exec(ctx); err != nil { return fmt.Errorf("something went wrong while inserting villages into the db: %w", err) } return nil } func (v *Village) List(ctx context.Context, params domain.ListVillagesParams) ([]domain.Village, error) { var villages []model.Village err := v.db.NewSelect(). Model(&villages). Order(villageOrders...). Apply(listVillagesParamsApplier{params}.apply). Scan(ctx) if err != nil && !errors.Is(err, sql.ErrNoRows) { return nil, fmt.Errorf("couldn't select villages from the db: %w", err) } result := make([]domain.Village, 0, len(villages)) for _, village := range villages { result = append(result, village.ToDomain()) } return result, nil } func (v *Village) ListCountWithRelations(ctx context.Context, params domain.ListVillagesParams) ([]domain.VillageWithRelations, int64, error) { var villages []model.Village paramsApplier := listVillagesParamsApplier{params} cntQ := v.db.NewSelect(). Model(&model.Village{}). Apply(paramsApplier.applyFilters) q := v.db.NewSelect(). Model(&villages). Where("(village.id, village.server_key) IN (?)", v.db.NewSelect(). Column("id", "server_key"). Model(&model.Village{}). Order(villageOrders...). Apply(paramsApplier.apply)). Order(villageOrders...). Relation("Player", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Column(playerMetaColumns...) }). Relation("Player.Tribe", func(q *bun.SelectQuery) *bun.SelectQuery { return q.Column(tribeMetaColumns...) }) count, err := scanAndCount(ctx, cntQ, q) if err != nil && !errors.Is(err, sql.ErrNoRows) { return nil, 0, fmt.Errorf("couldn't select villages from the db: %w", err) } result := make([]domain.VillageWithRelations, 0, len(villages)) for _, village := range villages { result = append(result, village.ToDomainWithRelations()) } return result, int64(count), nil } type listVillagesParamsApplier struct { params domain.ListVillagesParams } func (l listVillagesParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery { return q.Apply(l.applyPagination).Apply(l.applyFilters) } func (l listVillagesParamsApplier) applyFilters(q *bun.SelectQuery) *bun.SelectQuery { if len(l.params.IDs) > 0 { q = q.Where("village.id IN (?)", bun.In(l.params.IDs)) } if len(l.params.ServerKeys) > 0 { q = q.Where("village.server_key IN (?)", bun.In(l.params.ServerKeys)) } return q } func (l listVillagesParamsApplier) applyPagination(q *bun.SelectQuery) *bun.SelectQuery { return (paginationApplier{pagination: l.params.Pagination}).apply(q) }