From 07e3feb436769e8039d743e7a6c9ce2687e1e363 Mon Sep 17 00:00:00 2001 From: iamjooon2 Date: Sun, 2 Mar 2025 22:46:59 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20tags=20query=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/http/handler/postinghandler.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/http/handler/postinghandler.go b/internal/http/handler/postinghandler.go index eb77c41..b934923 100644 --- a/internal/http/handler/postinghandler.go +++ b/internal/http/handler/postinghandler.go @@ -31,6 +31,7 @@ type PostingSearchResponses struct { func GetPostings(client *ent.Client) gin.HandlerFunc { return func(c *gin.Context) { titleSearchParam := c.DefaultQuery("title", "") + tagsSearchParam := c.DefaultQuery("tags", "") paging := common.GenerateTechPaging(c.Query("cursor"), c.Query("size")) totalCount, err := countTotalPostings(client, titleSearchParam, c) From b3e52ab2fe027bd5afd4ff6f69d9eaa00993b37f Mon Sep 17 00:00:00 2001 From: iamjooon2 Date: Mon, 3 Mar 2025 00:13:29 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=ED=83=9C=EA=B7=B8=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/http/handler/postinghandler.go | 87 +++++++++++++++++++------ 1 file changed, 68 insertions(+), 19 deletions(-) diff --git a/internal/http/handler/postinghandler.go b/internal/http/handler/postinghandler.go index b934923..0d8a711 100644 --- a/internal/http/handler/postinghandler.go +++ b/internal/http/handler/postinghandler.go @@ -2,8 +2,11 @@ package handler import ( "net/http" + "strings" "time" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqljson" "github.com/gin-gonic/gin" "github.com/techbloghub/server/ent" "github.com/techbloghub/server/ent/posting" @@ -32,32 +35,73 @@ func GetPostings(client *ent.Client) gin.HandlerFunc { return func(c *gin.Context) { titleSearchParam := c.DefaultQuery("title", "") tagsSearchParam := c.DefaultQuery("tags", "") + paging := common.GenerateTechPaging(c.Query("cursor"), c.Query("size")) - totalCount, err := countTotalPostings(client, titleSearchParam, c) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } + if titleSearchParam == "" && tagsSearchParam == "" { + posts, err := fetchPostings(client, "", paging, c) + totalCount, err := countTotalPostings(client, "", c) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } - postings, err := fetchPostings(client, titleSearchParam, paging, c) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + c.JSON(http.StatusOK, PostingSearchResponses{ + Count: totalCount, + Postings: convertToTitleSearchResponse(posts), + HasNextPage: paging.HasNextPage(totalCount), + }) return } - c.JSON(http.StatusOK, PostingSearchResponses{ - Count: totalCount, - Postings: convertToTitleSearchResponse(postings), - HasNextPage: paging.HasNextPage(totalCount), - }) + if titleSearchParam != "" { + counts, err := countTotalPostings(client, titleSearchParam, c) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + postings, err := fetchPostings(client, titleSearchParam, paging, c) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, PostingSearchResponses{ + Count: counts, + Postings: convertToTitleSearchResponse(postings), + HasNextPage: paging.HasNextPage(counts), + }) + } else { + totalCount, err := countTotalPostings(client, tagsSearchParam, c) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + postings, err := fetchPostings(client, tagsSearchParam, paging, c) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, PostingSearchResponses{ + Count: totalCount, + Postings: convertToTitleSearchResponse(postings), + HasNextPage: paging.HasNextPage(totalCount), + }) + } } } -func fetchPostings(client *ent.Client, title string, paging common.TechbloghubPaging, c *gin.Context) ([]*ent.Posting, error) { +func fetchPostings(client *ent.Client, param string, paging common.TechbloghubPaging, c *gin.Context) ([]*ent.Posting, error) { query := client.Posting.Query().WithCompany() - if title != "" { - query = query.Where(posting.TitleContainsFold(title)) + if strings.Contains(param, ",") { + arr := strings.Split(param, ",") + query = query.Where(func(s *sql.Selector) { + s.Where(sqljson.ValueContains("tags", arr)) + }) + } else if param != "" { + query = query.Where(posting.TitleContainsFold(param)) } if paging.Cursor > 0 { query = query.Where(posting.IDLT(paging.Cursor)) @@ -69,10 +113,15 @@ func fetchPostings(client *ent.Client, title string, paging common.TechbloghubPa ).Limit(paging.Size).All(c) } -func countTotalPostings(client *ent.Client, title string, c *gin.Context) (int, error) { +func countTotalPostings(client *ent.Client, param string, c *gin.Context) (int, error) { query := client.Posting.Query() - if title != "" { - query = query.Where(posting.TitleContainsFold(title)) + if param != "" { + query = query.Where(posting.TitleContainsFold(param)) + } else if strings.Contains(param, ",") { + arr := strings.Split(param, ",") + query = query.Where(func(s *sql.Selector) { + s.Where(sqljson.ValueContains("tags", arr)) + }) } return query.Count(c) } From 107b6282e1ac453668867c0b98984810ccb80eaf Mon Sep 17 00:00:00 2001 From: iamjooon2 Date: Mon, 3 Mar 2025 00:15:33 +0900 Subject: [PATCH 3/7] =?UTF-8?q?refactor:=20=EA=B5=AC=EC=A1=B0=EC=B2=B4=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/http/handler/postinghandler.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/http/handler/postinghandler.go b/internal/http/handler/postinghandler.go index 0d8a711..3e74708 100644 --- a/internal/http/handler/postinghandler.go +++ b/internal/http/handler/postinghandler.go @@ -13,7 +13,7 @@ import ( "github.com/techbloghub/server/internal/common" ) -type TitleSearchResponse struct { +type PostingSearchResponse struct { ID int `json:"posting_id"` Title string `json:"title"` Url string `json:"url"` @@ -26,9 +26,9 @@ type TitleSearchResponse struct { } type PostingSearchResponses struct { - Count int `json:"count"` - Postings []TitleSearchResponse `json:"postings"` - HasNextPage bool `json:"has_next_page"` + Count int `json:"count"` + Postings []PostingSearchResponse `json:"postings"` + HasNextPage bool `json:"has_next_page"` } func GetPostings(client *ent.Client) gin.HandlerFunc { @@ -126,10 +126,10 @@ func countTotalPostings(client *ent.Client, param string, c *gin.Context) (int, return query.Count(c) } -func convertToTitleSearchResponse(postings []*ent.Posting) []TitleSearchResponse { - responses := make([]TitleSearchResponse, len(postings)) +func convertToTitleSearchResponse(postings []*ent.Posting) []PostingSearchResponse { + responses := make([]PostingSearchResponse, len(postings)) for i, posting := range postings { - responses[i] = TitleSearchResponse{ + responses[i] = PostingSearchResponse{ ID: posting.ID, Title: posting.Title, Url: posting.URL.String(), From 7dd79dab045783d147c0d71817cb65f9248a9945 Mon Sep 17 00:00:00 2001 From: iamjooon2 Date: Mon, 3 Mar 2025 23:36:12 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20=ED=83=9C=EA=B7=B8=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/http/handler/postinghandler.go | 160 ++++++++++++------------ 1 file changed, 82 insertions(+), 78 deletions(-) diff --git a/internal/http/handler/postinghandler.go b/internal/http/handler/postinghandler.go index 3e74708..e84b247 100644 --- a/internal/http/handler/postinghandler.go +++ b/internal/http/handler/postinghandler.go @@ -1,14 +1,15 @@ package handler import ( + "fmt" "net/http" "strings" "time" "entgo.io/ent/dialect/sql" - "entgo.io/ent/dialect/sql/sqljson" "github.com/gin-gonic/gin" "github.com/techbloghub/server/ent" + "github.com/techbloghub/server/ent/company" "github.com/techbloghub/server/ent/posting" "github.com/techbloghub/server/internal/common" ) @@ -31,6 +32,7 @@ type PostingSearchResponses struct { HasNextPage bool `json:"has_next_page"` } +// ✅ GetPostings을 더 깔끔하게 정리 func GetPostings(client *ent.Client) gin.HandlerFunc { return func(c *gin.Context) { titleSearchParam := c.DefaultQuery("title", "") @@ -38,108 +40,110 @@ func GetPostings(client *ent.Client) gin.HandlerFunc { paging := common.GenerateTechPaging(c.Query("cursor"), c.Query("size")) - if titleSearchParam == "" && tagsSearchParam == "" { - posts, err := fetchPostings(client, "", paging, c) - totalCount, err := countTotalPostings(client, "", c) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - c.JSON(http.StatusOK, PostingSearchResponses{ - Count: totalCount, - Postings: convertToTitleSearchResponse(posts), - HasNextPage: paging.HasNextPage(totalCount), - }) - return - } - - if titleSearchParam != "" { - counts, err := countTotalPostings(client, titleSearchParam, c) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - postings, err := fetchPostings(client, titleSearchParam, paging, c) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - c.JSON(http.StatusOK, PostingSearchResponses{ - Count: counts, - Postings: convertToTitleSearchResponse(postings), - HasNextPage: paging.HasNextPage(counts), - }) - } else { - totalCount, err := countTotalPostings(client, tagsSearchParam, c) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - postings, err := fetchPostings(client, tagsSearchParam, paging, c) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - c.JSON(http.StatusOK, PostingSearchResponses{ - Count: totalCount, - Postings: convertToTitleSearchResponse(postings), - HasNextPage: paging.HasNextPage(totalCount), - }) - } + handlePostingsQuery(client, titleSearchParam, tagsSearchParam, paging, c) } } +func handlePostingsQuery(client *ent.Client, title, tags string, paging common.TechbloghubPaging, c *gin.Context) { + searchParam := title + if searchParam == "" { + searchParam = tags + } + + postings, err := fetchPostings(client, searchParam, paging, c) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + totalCount, err := countPostings(client, searchParam, c) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, PostingSearchResponses{ + Count: totalCount, + Postings: convertToTitleSearchResponse(postings), + HasNextPage: paging.HasNextPage(totalCount), + }) +} + func fetchPostings(client *ent.Client, param string, paging common.TechbloghubPaging, c *gin.Context) ([]*ent.Posting, error) { - query := client.Posting.Query().WithCompany() - if strings.Contains(param, ",") { - arr := strings.Split(param, ",") + query := client.Posting.Query() + + if param != "" { + query = applySearchFilter(query, param) + } + + if paging.Cursor > common.CURSOR_DEFAULT { + query = query.Where(posting.IDLT(paging.Cursor)) + } + return query.Where(posting.HasCompany()). + WithCompany(func(q *ent.CompanyQuery) { + q.Select( + company.FieldLogoURL, + company.FieldName, + ) + }). + Order( + ent.Desc(posting.FieldID), + ent.Desc(posting.FieldID), + ). + Limit(paging.Size). + All(c.Request.Context()) +} + +func applySearchFilter(query *ent.PostingQuery, param string) *ent.PostingQuery { + // before: [react,java] + // after: ARRAY[react,java] + if strings.HasPrefix(param, "[") && strings.HasSuffix(param, "]") { + arrayQuery := "ARRAY" + param + query = query.Where(func(s *sql.Selector) { - s.Where(sqljson.ValueContains("tags", arr)) + s.Where(sql.ExprP(fmt.Sprintf("%s @> %s", s.C("tags"), arrayQuery))) }) + // 단일 제목으로 검색하는 케이스는 제목 검색 } else if param != "" { query = query.Where(posting.TitleContainsFold(param)) } - if paging.Cursor > 0 { - query = query.Where(posting.IDLT(paging.Cursor)) - } - - return query.Order( - ent.Desc(posting.FieldPublishedAt), - ent.Desc(posting.FieldID), - ).Limit(paging.Size).All(c) + return query } -func countTotalPostings(client *ent.Client, param string, c *gin.Context) (int, error) { +func countPostings(client *ent.Client, param string, c *gin.Context) (int, error) { query := client.Posting.Query() if param != "" { - query = query.Where(posting.TitleContainsFold(param)) - } else if strings.Contains(param, ",") { - arr := strings.Split(param, ",") - query = query.Where(func(s *sql.Selector) { - s.Where(sqljson.ValueContains("tags", arr)) - }) + query = applySearchFilter(query, param) } return query.Count(c) } +// 대체 왜 못갖고오냐 +func getCompanyInfo(posting *ent.Posting) (string, string) { + if posting.Edges.Company != nil { + return posting.Edges.Company.Name, posting.Edges.Company.LogoURL.String() + } + return "Unknown", "" +} + func convertToTitleSearchResponse(postings []*ent.Posting) []PostingSearchResponse { - responses := make([]PostingSearchResponse, len(postings)) - for i, posting := range postings { - responses[i] = PostingSearchResponse{ + var responses []PostingSearchResponse + + for _, posting := range postings { + companyName, logoURL := getCompanyInfo(posting) + + responses = append(responses, PostingSearchResponse{ ID: posting.ID, Title: posting.Title, Url: posting.URL.String(), - Company: posting.Edges.Company.Name, - Logo: posting.Edges.Company.LogoURL.String(), + Company: companyName, + Logo: logoURL, Tags: posting.Tags.ToStringSlice(), CreateTime: posting.CreateTime, UpdateTime: posting.UpdateTime, PublishedTime: posting.PublishedAt, - } + }) } + return responses } From 1553391d55f956988f14b0d9ec3cca699a697bee Mon Sep 17 00:00:00 2001 From: iamjooon2 Date: Mon, 3 Mar 2025 23:48:46 +0900 Subject: [PATCH 5/7] =?UTF-8?q?refactor:=20=EC=98=81=ED=98=BC=EC=9D=98=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/http/handler/postinghandler.go | 89 +++++++++++-------------- 1 file changed, 39 insertions(+), 50 deletions(-) diff --git a/internal/http/handler/postinghandler.go b/internal/http/handler/postinghandler.go index e84b247..a1d70da 100644 --- a/internal/http/handler/postinghandler.go +++ b/internal/http/handler/postinghandler.go @@ -32,53 +32,60 @@ type PostingSearchResponses struct { HasNextPage bool `json:"has_next_page"` } -// ✅ GetPostings을 더 깔끔하게 정리 func GetPostings(client *ent.Client) gin.HandlerFunc { return func(c *gin.Context) { titleSearchParam := c.DefaultQuery("title", "") tagsSearchParam := c.DefaultQuery("tags", "") - paging := common.GenerateTechPaging(c.Query("cursor"), c.Query("size")) - handlePostingsQuery(client, titleSearchParam, tagsSearchParam, paging, c) + postings, err := fetchPostings(client, titleSearchParam, tagsSearchParam, paging, c) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + totalCount, err := countPostings(client, titleSearchParam, tagsSearchParam, c) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, PostingSearchResponses{ + Count: totalCount, + Postings: convertToTitleSearchResponse(postings), + HasNextPage: paging.HasNextPage(totalCount), + }) } } -func handlePostingsQuery(client *ent.Client, title, tags string, paging common.TechbloghubPaging, c *gin.Context) { - searchParam := title - if searchParam == "" { - searchParam = tags - } - - postings, err := fetchPostings(client, searchParam, paging, c) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return +func applySearchFilters(query *ent.PostingQuery, title, tags string) *ent.PostingQuery { + if title != "" { + query = query.Where(posting.TitleContainsFold(title)) } + // 태그 조회 케이스 -> postgresql ARRAY 검색 형식에 맞춰야함 + // tags: [react,java] + // query : where tags @> ARRAY['react','java'] + if tags != "" { + trimmed := strings.Trim(tags, "[]") + arr := strings.Split(trimmed, ",") + arrayQuery := "ARRAY" + fmt.Sprintf("['%s']", strings.Join(arr, ",")) - totalCount, err := countPostings(client, searchParam, c) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return + query = query.Where(func(s *sql.Selector) { + s.Where(sql.ExprP(fmt.Sprintf("%s @> %s", s.C("tags"), arrayQuery))) + }) } - c.JSON(http.StatusOK, PostingSearchResponses{ - Count: totalCount, - Postings: convertToTitleSearchResponse(postings), - HasNextPage: paging.HasNextPage(totalCount), - }) + return query } -func fetchPostings(client *ent.Client, param string, paging common.TechbloghubPaging, c *gin.Context) ([]*ent.Posting, error) { +func fetchPostings(client *ent.Client, title, tags string, paging common.TechbloghubPaging, c *gin.Context) ([]*ent.Posting, error) { query := client.Posting.Query() - - if param != "" { - query = applySearchFilter(query, param) - } + query = applySearchFilters(query, title, tags) if paging.Cursor > common.CURSOR_DEFAULT { query = query.Where(posting.IDLT(paging.Cursor)) } + return query.Where(posting.HasCompany()). WithCompany(func(q *ent.CompanyQuery) { q.Select( @@ -87,38 +94,20 @@ func fetchPostings(client *ent.Client, param string, paging common.TechbloghubPa ) }). Order( - ent.Desc(posting.FieldID), + ent.Desc(posting.FieldPublishedAt), ent.Desc(posting.FieldID), ). Limit(paging.Size). All(c.Request.Context()) } -func applySearchFilter(query *ent.PostingQuery, param string) *ent.PostingQuery { - // before: [react,java] - // after: ARRAY[react,java] - if strings.HasPrefix(param, "[") && strings.HasSuffix(param, "]") { - arrayQuery := "ARRAY" + param - - query = query.Where(func(s *sql.Selector) { - s.Where(sql.ExprP(fmt.Sprintf("%s @> %s", s.C("tags"), arrayQuery))) - }) - // 단일 제목으로 검색하는 케이스는 제목 검색 - } else if param != "" { - query = query.Where(posting.TitleContainsFold(param)) - } - return query -} - -func countPostings(client *ent.Client, param string, c *gin.Context) (int, error) { +func countPostings(client *ent.Client, title, tags string, c *gin.Context) (int, error) { query := client.Posting.Query() - if param != "" { - query = applySearchFilter(query, param) - } + query = applySearchFilters(query, title, tags) return query.Count(c) } -// 대체 왜 못갖고오냐 +// 대체 왜 못갖고오냐!!!! func getCompanyInfo(posting *ent.Posting) (string, string) { if posting.Edges.Company != nil { return posting.Edges.Company.Name, posting.Edges.Company.LogoURL.String() @@ -127,7 +116,7 @@ func getCompanyInfo(posting *ent.Posting) (string, string) { } func convertToTitleSearchResponse(postings []*ent.Posting) []PostingSearchResponse { - var responses []PostingSearchResponse + responses := make([]PostingSearchResponse, len(postings)) for _, posting := range postings { companyName, logoURL := getCompanyInfo(posting) From d2c35c3f5299111140a8b3227d77da6a01dc1484 Mon Sep 17 00:00:00 2001 From: iamjooon2 Date: Tue, 4 Mar 2025 10:44:04 +0900 Subject: [PATCH 6/7] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/http/handler/postinghandler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/http/handler/postinghandler.go b/internal/http/handler/postinghandler.go index a1d70da..c7a7280 100644 --- a/internal/http/handler/postinghandler.go +++ b/internal/http/handler/postinghandler.go @@ -52,7 +52,7 @@ func GetPostings(client *ent.Client) gin.HandlerFunc { c.JSON(http.StatusOK, PostingSearchResponses{ Count: totalCount, - Postings: convertToTitleSearchResponse(postings), + Postings: toPostingSearchResponses(postings), HasNextPage: paging.HasNextPage(totalCount), }) } @@ -115,7 +115,7 @@ func getCompanyInfo(posting *ent.Posting) (string, string) { return "Unknown", "" } -func convertToTitleSearchResponse(postings []*ent.Posting) []PostingSearchResponse { +func toPostingSearchResponses(postings []*ent.Posting) []PostingSearchResponse { responses := make([]PostingSearchResponse, len(postings)) for _, posting := range postings { From b9ee25ca1ba7542609b2329919c5c36f41137ee7 Mon Sep 17 00:00:00 2001 From: iamjooon2 Date: Sat, 8 Mar 2025 20:18:29 +0900 Subject: [PATCH 7/7] =?UTF-8?q?fix:=20api=20=EB=AA=85=EC=84=B8=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/http/handler/postinghandler.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/http/handler/postinghandler.go b/internal/http/handler/postinghandler.go index c7a7280..32eae79 100644 --- a/internal/http/handler/postinghandler.go +++ b/internal/http/handler/postinghandler.go @@ -66,8 +66,7 @@ func applySearchFilters(query *ent.PostingQuery, title, tags string) *ent.Postin // tags: [react,java] // query : where tags @> ARRAY['react','java'] if tags != "" { - trimmed := strings.Trim(tags, "[]") - arr := strings.Split(trimmed, ",") + arr := strings.Split(tags, ",") arrayQuery := "ARRAY" + fmt.Sprintf("['%s']", strings.Join(arr, ",")) query = query.Where(func(s *sql.Selector) {