feat(analyticsApi): added better api for page counts
Build and Push Docker image / build-and-push (push) Successful in 2m10s Details

This commit is contained in:
Valentin Kolb 2024-11-13 01:05:14 +01:00
parent 23b0e7cf84
commit fe232533c3
1 changed files with 71 additions and 37 deletions

View File

@ -14,30 +14,33 @@ import (
// initPageViewCount // initPageViewCount
// //
// This endpoint is used to retrieve page view counts for each unique path. // This endpoint is used to retrieve paginated page view counts for each unique path.
// It adds the following endpoint to the app: // It adds the following endpoint to the app:
// GET /api/analytics/pageViewCount // GET /api/analytics/pageViewCount
// //
// The endpoint expects the following query parameters: // Query parameters:
// //
// ?startDate=ISO 8601 formatted date (optional) // ?startDate=ISO 8601 formatted date (optional)
// ?page=<page number> // ?page=<page number>
// ?perPage=<results per page> // ?perPage=<results per page>
// ?sort=<ASC|DESC> (default is ASC) // ?sort=<ASC|DESC> (default is ASC)
// //
// - `startDate`: An optional start date in ISO 8601 format (e.g., "2024-11-12T15:04:05Z"). // - `startDate`: Optional start date in ISO 8601 format (e.g., "2024-11-12T15:04:05Z").
// If provided, the global count reflects views from this date onward.
// If not provided, the global count includes all views.
// - `page`: Page number for pagination (default is 1). // - `page`: Page number for pagination (default is 1).
// - `perPage`: Number of results per page for pagination (default is 10). // - `perPage`: Number of results per page (default is 10).
// - `sort`: Sorting order, either `ASC` or `DESC` for ascending or descending order. // - `sort`: Sorting order, either `ASC` or `DESC` (default is ASC).
// //
// Response format: // Response format:
// [
// //
// { // {
// "page": <current page number>,
// "perPage": <results per page>,
// "totalItems": <total number of items matching the filter>,
// "totalPages": <total number of pages>,
// "items": [
// {
// "id": <unique row id>, // "id": <unique row id>,
// "data": "<path>", // "path": "<path>",
// "count": <total view count since startDate>, // "count": <total view count since startDate>,
// "last_30_days_data": [ // "last_30_days_data": [
// { // {
@ -48,10 +51,8 @@ import (
// ] // ]
// }, // },
// ... // ...
//
// ] // ]
// // }
// `last_30_days_data` always provides daily counts for the most recent 30 days regardless of the `startDate` parameter.
func initPageViewCount(app *pocketbase.PocketBase, e *core.ServeEvent) { func initPageViewCount(app *pocketbase.PocketBase, e *core.ServeEvent) {
e.Router.GET("/api/analytics/pageViewCount", func(c echo.Context) error { e.Router.GET("/api/analytics/pageViewCount", func(c echo.Context) error {
@ -72,7 +73,7 @@ func initPageViewCount(app *pocketbase.PocketBase, e *core.ServeEvent) {
} }
} }
// Get pagination and sorting parameters // Pagination and sorting parameters
page, _ := strconv.Atoi(c.QueryParam("page")) page, _ := strconv.Atoi(c.QueryParam("page"))
if page < 1 { if page < 1 {
page = 1 page = 1
@ -90,21 +91,45 @@ func initPageViewCount(app *pocketbase.PocketBase, e *core.ServeEvent) {
offset := (page - 1) * perPage offset := (page - 1) * perPage
type PageViewData struct { // Fetch total items for pagination
ID int `json:"id"` var totalItems int
Data string `json:"data"` err = app.Dao().DB().
Count int `json:"count"` NewQuery(`
Last30DaysData interface{} `json:"last_30_days_data"` SELECT COUNT(DISTINCT path) as total
FROM analyticsPageViews
WHERE created >= {:startDate}
`).
Bind(dbx.Params{
"startDate": startDate,
}).
Row(&totalItems)
if err != nil {
return apis.NewApiError(500, "Failed to count total items", err)
} }
var result []PageViewData totalPages := (totalItems + perPage - 1) / perPage // Calculate total pages
// Execute the query with nested JSON aggregation, pagination, and sorting // Define the structure for items
type Last30DaysData struct {
Date string `json:"date"`
Count int `json:"count"`
}
type Item struct {
ID int `json:"id"`
Path string `json:"path"`
Count int `json:"count"`
Last30DaysData []Last30DaysData `json:"last_30_days_data"`
}
// Query paginated items
var items []Item
err = app.Dao().DB(). err = app.Dao().DB().
NewQuery(` NewQuery(`
SELECT SELECT
(ROW_NUMBER() OVER()) AS id, (ROW_NUMBER() OVER()) AS id,
view.path AS data, view.path AS path,
( (
SELECT COUNT(id) SELECT COUNT(id)
FROM analyticsPageViews FROM analyticsPageViews
@ -133,7 +158,7 @@ func initPageViewCount(app *pocketbase.PocketBase, e *core.ServeEvent) {
GROUP BY GROUP BY
view.path view.path
ORDER BY ORDER BY
data ` + sort + ` path ` + sort + `
LIMIT {:perPage} OFFSET {:offset} LIMIT {:perPage} OFFSET {:offset}
`). `).
Bind(dbx.Params{ Bind(dbx.Params{
@ -141,14 +166,23 @@ func initPageViewCount(app *pocketbase.PocketBase, e *core.ServeEvent) {
"perPage": perPage, "perPage": perPage,
"offset": offset, "offset": offset,
}). }).
All(&result) All(&items)
if err != nil { if err != nil {
return apis.NewApiError(500, "Failed to query page view data", err) return apis.NewApiError(500, "Failed to query page view data", err)
} }
// Final response structure
response := map[string]interface{}{
"page": page,
"perPage": perPage,
"totalItems": totalItems,
"totalPages": totalPages,
"items": items,
}
// Return the final JSON response // Return the final JSON response
return c.JSON(200, result) return c.JSON(200, response)
}, apis.ActivityLogger(app)) }, apis.ActivityLogger(app))
} }