perf(web): add pagination

and remove getting total quotes count
This commit is contained in:
Masahiko AMANO 2025-01-07 04:04:06 +03:00
parent 65152526b7
commit 8b417dc623
5 changed files with 51 additions and 60 deletions

View File

@ -162,17 +162,6 @@ func quotesGet(c *gin.Context) {
c.JSON(http.StatusOK, quotes) c.JSON(http.StatusOK, quotes)
} }
func quotesCount(c *gin.Context) {
user_id := c.GetString("user_id")
count, err := db.QuotesCount(context.Background(), user_id)
if err != nil {
status, message := handleDBError(err)
c.JSON(status, gin.H{"error": message})
return
}
c.JSON(http.StatusOK, gin.H{"count": count})
}
func quoteGet(c *gin.Context) { func quoteGet(c *gin.Context) {
user_id := c.GetString("user_id") user_id := c.GetString("user_id")
quote_id := c.Param("id") quote_id := c.Param("id")

View File

@ -26,7 +26,6 @@ func RegisterRoutes(r *gin.Engine) {
api.DELETE("/auth", userLogout) api.DELETE("/auth", userLogout)
api.GET("/quotes", MiddlewareAuth, quotesGet) api.GET("/quotes", MiddlewareAuth, quotesGet)
api.GET("/quotes/count", MiddlewareAuth, quotesCount)
api.POST("/quotes", MiddlewareAuth, quoteAdd) api.POST("/quotes", MiddlewareAuth, quoteAdd)
api.GET("/quotes/:id", MiddlewareAuth, quoteGet) api.GET("/quotes/:id", MiddlewareAuth, quoteGet)
api.PATCH("/quotes/:id", MiddlewareAuth, quoteUpdate) api.PATCH("/quotes/:id", MiddlewareAuth, quoteUpdate)

View File

@ -72,13 +72,13 @@ func UserUpdatePassword(ctx context.Context, user_id string, new_password string
//#region Quotes //#region Quotes
func QuotesGet(ctx context.Context, user_id string, filter string, sort string, limit int, offset int) (quotes []models.Quote, err error) { func QuotesGet(ctx context.Context, user_id string, filter string, sort string, limit int, offset int) (quotes models.Quotes, err error) {
query := "SELECT * FROM quotes_get($1) WHERE position($2 in lower(text))>0 OR position($2 in lower(author))>0" queryGet := "SELECT * FROM quotes_get($1) WHERE position($2 in lower(text))>0 OR position($2 in lower(author))>0"
if sort == "random" { if sort == "random" {
query += " ORDER BY random()" queryGet += " ORDER BY random()"
} else if sort != "" { } else if sort != "" {
sort_options := strings.Split(sort, ",") sort_options := strings.Split(sort, ",")
query += " ORDER BY " queryGet += " ORDER BY "
for i, sort_option := range sort_options { for i, sort_option := range sort_options {
sort_order := sort_option[:1] sort_order := sort_option[:1]
sort_field := sort_option[1:] sort_field := sort_option[1:]
@ -105,23 +105,26 @@ func QuotesGet(ctx context.Context, user_id string, filter string, sort string,
return return
} }
if i > 0 { if i > 0 {
query += ", " queryGet += ", "
} }
query += fmt.Sprintf("%s %s", sort_field, sort_order) queryGet += fmt.Sprintf("%s %s", sort_field, sort_order)
} }
} }
queryCount := queryGet
if limit >= 0 { if limit >= 0 {
query += fmt.Sprintf(" LIMIT %d", limit) queryGet += fmt.Sprintf(" LIMIT %d", limit)
} }
if offset > 0 { if offset > 0 {
query += fmt.Sprintf(" OFFSET %d", offset) queryGet += fmt.Sprintf(" OFFSET %d", offset)
} }
rows, err := ConnPool.Query(ctx, query, user_id, strings.ToLower(filter)) filter = strings.ToLower(filter)
rows, err := ConnPool.Query(ctx, queryGet, user_id, filter)
if err != nil { if err != nil {
err = fmt.Errorf("error while getting quotes: %w", err) err = fmt.Errorf("error while getting quotes: %w", err)
return return
} }
quotes = []models.Quote{} quotes.Quotes = []models.Quote{}
count := 0
for rows.Next() { for rows.Next() {
var quote models.Quote var quote models.Quote
err = rows.Scan(&quote.ID, &quote.Text, &quote.Author, &quote.Datetime, &quote.Creator.ID, &quote.Creator.Name, &quote.Creator.Login, &quote.Creator.Role, &quote.Creator.TelegramID) err = rows.Scan(&quote.ID, &quote.Text, &quote.Author, &quote.Datetime, &quote.Creator.ID, &quote.Creator.Name, &quote.Creator.Login, &quote.Creator.Role, &quote.Creator.TelegramID)
@ -129,15 +132,19 @@ func QuotesGet(ctx context.Context, user_id string, filter string, sort string,
err = fmt.Errorf("error while fetching quotes: %w", err) err = fmt.Errorf("error while fetching quotes: %w", err)
return return
} }
quotes = append(quotes, quote) quotes.Quotes = append(quotes.Quotes, quote)
count++
} }
err = rows.Err() err = rows.Err()
return if err != nil {
} return
}
func QuotesCount(ctx context.Context, user_id string) (count int, err error) { quotes.Pagination.Limit = limit
row := ConnPool.QueryRow(ctx, "SELECT count(*) FROM quotes_get($1)", user_id) quotes.Pagination.Offset = offset
err = row.Scan(&count) quotes.Pagination.Count = count
queryCount = fmt.Sprintf("SELECT count(*) FROM (%s) q", queryCount)
row := ConnPool.QueryRow(ctx, queryCount, user_id, filter)
err = row.Scan(&quotes.Pagination.TotalCount)
return return
} }

View File

@ -27,3 +27,15 @@ type Quote struct {
Datetime time.Time `json:"datetime"` Datetime time.Time `json:"datetime"`
Creator User `json:"creator"` Creator User `json:"creator"`
} }
type Pagination struct {
TotalCount int `json:"totalCount"`
Offset int `json:"offset"`
Limit int `json:"limit"`
Count int `json:"count"`
}
type Quotes struct {
Pagination Pagination `json:"pagination"`
Quotes []Quote `json:"quotes"`
}

View File

@ -45,28 +45,33 @@ function renderBlockQuote(quote) {
function load() { function load() {
var quotesCount; var quotesCount;
failed = false; $("#input-search").val(search);
$("#input-sorting").val(sorting);
container = $("#block-quotes");
$.ajax({ $.ajax({
async: false, async: false,
url: "/api/quotes/count", url: `/api/quotes?filter=${encodeURIComponent(search)}&sort=${encodeURIComponent(sorting)}&limit=${PAGE_SIZE}&offset=${(currPage - 1)*PAGE_SIZE}`,
type: "GET", type: "GET",
dataType: "json", dataType: "json",
success: function (resp) { success: function (resp) {
quotesCount = resp.count; quotesCount = resp.pagination.totalCount
if (resp.pagination.count == 0) {
container.html("<p style='text-align: center;'><i>Чёт нету ничего...</i></p>");
return;
}
resp.quotes.forEach((quote) => {
container.append(renderBlockQuote(quote));
});
}, },
error: function (err) { error: function (err) {
$("#error-message").text(err.responseJSON.error); $("#error-message").text(err.responseJSON.error);
$("#error").removeClass("hidden"); $("#error").removeClass("hidden");
failed = true; },
complete: function () {
$("#block-quotes-loader").addClass("hidden"); $("#block-quotes-loader").addClass("hidden");
}, },
}); });
if (failed) {
return;
}
totalPages = Math.ceil(quotesCount / PAGE_SIZE); totalPages = Math.ceil(quotesCount / PAGE_SIZE);
$("#input-search").val(search);
$("#input-sorting").val(sorting);
$("#btn-page-curr").text(currPage); $("#btn-page-curr").text(currPage);
if (currPage > 1) { if (currPage > 1) {
$("#btn-page-first").removeClass("hidden"); $("#btn-page-first").removeClass("hidden");
@ -89,28 +94,6 @@ function load() {
} }
} }
} }
container = $("#block-quotes");
$.ajax({
url: `/api/quotes?filter=${encodeURIComponent(search)}&sort=${encodeURIComponent(sorting)}&limit=${PAGE_SIZE}&offset=${(currPage - 1)*PAGE_SIZE}`,
type: "GET",
dataType: "json",
success: function (resp) {
if (resp.length == 0) {
container.html("<p style='text-align: center;'><i>Чёт нету ничего...</i></p>");
return;
}
resp.forEach((quote) => {
container.append(renderBlockQuote(quote));
});
},
error: function (err) {
$("#error-message").text(err.responseJSON.error);
$("#error").removeClass("hidden");
},
complete: function () {
$("#block-quotes-loader").addClass("hidden");
},
});
} }
function reload() { function reload() {
@ -122,6 +105,7 @@ function reload() {
$("#btn-page-first").addClass("hidden"); $("#btn-page-first").addClass("hidden");
$("#pages-prev").addClass("hidden"); $("#pages-prev").addClass("hidden");
$("#btn-page-prev").addClass("hidden"); $("#btn-page-prev").addClass("hidden");
$("#btn-page-curr").text(1);
$("#btn-page-next").addClass("hidden"); $("#btn-page-next").addClass("hidden");
$("#pages-next").addClass("hidden"); $("#pages-next").addClass("hidden");
$("#btn-page-last").addClass("hidden"); $("#btn-page-last").addClass("hidden");