diff --git a/internal/api/problem/search.go b/internal/api/problem/search.go index 5a312dd..01bb9ef 100644 --- a/internal/api/problem/search.go +++ b/internal/api/problem/search.go @@ -7,7 +7,8 @@ import ( ) type searchRequest struct { - Search string `form:"search" json:"search"` + Keyword string `form:"keyword" json:"keyword"` + Tag string `form:"tag" json:"tag"` } // Search @@ -16,7 +17,8 @@ type searchRequest struct { // @Tags problem // @Accept application/x-www-form-urlencoded // @Produce json -// @Param search formData string false "keyword" +// @Param keyword formData string false "keyword" +// @Param tag formData string false "tag" // @Response 200 {object} e.Response[[]model.Problem] "problems found" // @Router /v1/problem/search [post] func (h *handler) Search(c *gin.Context) { @@ -27,13 +29,13 @@ func (h *handler) Search(c *gin.Context) { } // TODO: pagination - if req.Search == "" { + if req.Keyword == "" { // TODO: query without LIKE - problems, status := h.problemService.QueryFuzz(req.Search, true, true) + problems, status := h.problemService.QueryFuzz(req.Keyword, req.Tag, true, true) e.Pong(c, status, problems) return } else { - problems, status := h.problemService.QueryFuzz(req.Search, true, true) + problems, status := h.problemService.QueryFuzz(req.Keyword, req.Tag, true, true) e.Pong(c, status, problems) return } diff --git a/internal/api/problem/update.go b/internal/api/problem/update.go index a45e9d4..4cb8f4c 100644 --- a/internal/api/problem/update.go +++ b/internal/api/problem/update.go @@ -6,13 +6,15 @@ import ( "git.0x7f.app/WOJ/woj-server/internal/service/problem" "git.0x7f.app/WOJ/woj-server/pkg/utils" "github.com/gin-gonic/gin" + "github.com/jackc/pgtype" ) type updateRequest struct { - Pid uint `form:"pid" json:"pid"` - Title string `form:"title" json:"title"` - Statement string `form:"statement" json:"statement"` - IsEnabled bool `form:"is_enabled" json:"is_enabled"` + Pid uint `form:"pid" json:"pid"` + Title string `form:"title" json:"title"` + Statement string `form:"statement" json:"statement"` + Tags []string `form:"tags" json:"tags"` + IsEnabled bool `form:"is_enabled" json:"is_enabled"` } // Update @@ -21,10 +23,11 @@ type updateRequest struct { // @Tags problem,admin // @Accept application/x-www-form-urlencoded // @Produce json -// @Param pid formData int false "problem id, 0 for create" -// @Param title formData string false "title" -// @Param statement formData string false "statement" -// @Param is_enabled formData bool false "is enabled" +// @Param pid formData int false "problem id, 0 for create" +// @Param title formData string false "title" +// @Param statement formData string false "statement" +// @Param tags formData []string false "tags" +// @Param is_enabled formData bool false "is enabled" // @Response 200 {object} e.Response[model.Problem] "problem info without provider information" // @Security Authentication // @Router /v1/problem/update [post] @@ -60,6 +63,7 @@ func (h *handler) Update(c *gin.Context) { createData := &problem.CreateData{ Title: req.Title, Statement: req.Statement, + Tags: req.Tags, ProviderID: uid, IsEnabled: false, } @@ -83,6 +87,11 @@ func (h *handler) Update(c *gin.Context) { // update problem p.Title = utils.If(req.Title != "", req.Title, p.Title) p.Statement = utils.If(req.Statement != "", req.Statement, p.Statement) + if len(req.Tags) != 0 { + tags := pgtype.TextArray{} + _ = tags.Set(req.Tags) + p.Tags = tags + } p.IsEnabled = req.IsEnabled p, status = h.problemService.Update(p) diff --git a/internal/model/Problem.go b/internal/model/Problem.go index c72b0b9..6f3d0e1 100644 --- a/internal/model/Problem.go +++ b/internal/model/Problem.go @@ -7,11 +7,12 @@ import ( type Problem struct { gorm.Model `json:"meta"` - Title string `json:"title" gorm:"not null"` - Statement string `json:"statement" gorm:"not null"` - ProviderID uint `json:"-" gorm:"not null;index"` - Provider User `json:"provider" gorm:"foreignKey:ProviderID"` - IsEnabled bool `json:"is_enabled" gorm:"not null;index"` + Title string `json:"title" gorm:"not null;index"` + Statement string `json:"statement" gorm:"not null"` + Tags pgtype.TextArray `json:"tags" gorm:"type:text[];index"` + ProviderID uint `json:"-" gorm:"not null;index"` + Provider User `json:"provider" gorm:"foreignKey:ProviderID"` + IsEnabled bool `json:"is_enabled" gorm:"not null;index"` } type ProblemVersion struct { diff --git a/internal/service/problem/create.go b/internal/service/problem/create.go index 41568e1..27e7c29 100644 --- a/internal/service/problem/create.go +++ b/internal/service/problem/create.go @@ -3,20 +3,25 @@ package problem import ( "git.0x7f.app/WOJ/woj-server/internal/e" "git.0x7f.app/WOJ/woj-server/internal/model" + "github.com/jackc/pgtype" "go.uber.org/zap" ) type CreateData struct { Title string Statement string + Tags []string ProviderID uint IsEnabled bool } func (s *service) Create(data *CreateData) (*model.Problem, e.Status) { + tags := pgtype.TextArray{} + _ = tags.Set(data.Tags) problem := &model.Problem{ Title: data.Title, Statement: data.Statement, + Tags: tags, ProviderID: data.ProviderID, IsEnabled: data.IsEnabled, } diff --git a/internal/service/problem/createVersion.go b/internal/service/problem/create_version.go similarity index 100% rename from internal/service/problem/createVersion.go rename to internal/service/problem/create_version.go diff --git a/internal/service/problem/queryFuzz.go b/internal/service/problem/query_fuzz.go similarity index 56% rename from internal/service/problem/queryFuzz.go rename to internal/service/problem/query_fuzz.go index 20f0149..740b0ca 100644 --- a/internal/service/problem/queryFuzz.go +++ b/internal/service/problem/query_fuzz.go @@ -7,22 +7,28 @@ import ( "gorm.io/gorm/clause" ) -func (s *service) QueryFuzz(search string, associations bool, shouldEnable bool) ([]*model.Problem, e.Status) { +func (s *service) QueryFuzz(keyword string, tag string, associations bool, shouldEnable bool) ([]*model.Problem, e.Status) { problems := make([]*model.Problem, 0) - query := s.db.Get() + if associations { query = query.Preload(clause.Associations) } if shouldEnable { query = query.Where("is_enabled = true") } + query = query. - Where(s.db.Get().Where("title LIKE ?", "%"+search+"%"). - Or("statement LIKE ?", "%"+search+"%")) + Where(s.db.Get().Where("title LIKE ?", "%"+keyword+"%"). + Or("statement LIKE ?", "%"+keyword+"%")) + + if tag != "" { + query = query.Where("tags @> array[?]", tag) + } + err := query.Find(&problems).Error if err != nil { - s.log.Warn("DatabaseError", zap.Error(err), zap.Any("search", search)) + s.log.Warn("DatabaseError", zap.Error(err), zap.Any("keyword", keyword)) return nil, e.DatabaseError } diff --git a/internal/service/problem/queryLatestVersion.go b/internal/service/problem/query_latest_version.go similarity index 100% rename from internal/service/problem/queryLatestVersion.go rename to internal/service/problem/query_latest_version.go diff --git a/internal/service/problem/queryVersion.go b/internal/service/problem/query_version.go similarity index 100% rename from internal/service/problem/queryVersion.go rename to internal/service/problem/query_version.go diff --git a/internal/service/problem/service.go b/internal/service/problem/service.go index f4be009..f3fb43e 100644 --- a/internal/service/problem/service.go +++ b/internal/service/problem/service.go @@ -15,7 +15,7 @@ type Service interface { Create(data *CreateData) (*model.Problem, e.Status) Update(problem *model.Problem) (*model.Problem, e.Status) Query(pid uint, associations bool, shouldEnable bool) (*model.Problem, e.Status) - QueryFuzz(search string, associations bool, shouldEnable bool) ([]*model.Problem, e.Status) + QueryFuzz(keyword string, tag string, associations bool, shouldEnable bool) ([]*model.Problem, e.Status) CreateVersion(data *CreateVersionData) (*model.ProblemVersion, e.Status) UpdateVersion(pvid uint, values interface{}) e.Status diff --git a/internal/service/problem/updateVersion.go b/internal/service/problem/update_version.go similarity index 100% rename from internal/service/problem/updateVersion.go rename to internal/service/problem/update_version.go