Skip to content

Commit

Permalink
[Backend] Refactor the GetMyMarkers and NearbyMarkers functions to im…
Browse files Browse the repository at this point in the history
…plement pagination
  • Loading branch information
Alfex4936 committed Mar 4, 2024
1 parent c89f4cb commit 56e8b65
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 36 deletions.
7 changes: 7 additions & 0 deletions backend/dto/marker_dto.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,16 @@ type QueryParams struct {
Longitude float64 `query:"longitude"`
Distance int `query:"distance"`
N int `query:"n"`
Page int `query:"page"`
}

type MarkerWithDistance struct {
models.Marker
Distance float64 `db:"distance"` // Distance in meters
}

type MarkerWithDislike struct {
models.Marker
Username string `db:"Username"`
DislikeCount int `db:"DislikeCount"`
}
58 changes: 45 additions & 13 deletions backend/handlers/marker_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"chulbong-kr/dto"
"chulbong-kr/services"
"chulbong-kr/utils"
"math"
"strconv"

"github.com/gofiber/fiber/v2"
Expand Down Expand Up @@ -270,21 +271,32 @@ func UndoDislikeHandler(c *fiber.Ctx) error {
}

func GetUserMarkersHandler(c *fiber.Ctx) error {
// Retrieve userID from c.Locals, which is set during authentication
userID, ok := c.Locals("userID").(int)
if !ok {
// If userID is not available, return an error response
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "User not authenticated"})
}

// Call the modified service function with the userID
markersWithPhotos, err := services.GetAllMarkersByUser(userID)
// Parse query parameters for pagination
page, err := strconv.Atoi(c.Query("page", "1"))
if err != nil || page < 1 {
page = 1 // default to first page
}
pageSize := 4

markersWithPhotos, total, err := services.GetAllMarkersByUserWithPagination(userID, page, pageSize)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}

// Return the filtered markers
return c.JSON(markersWithPhotos)
totalPages := int(math.Ceil(float64(total) / float64(pageSize)))

// Return the filtered markers with pagination info
return c.JSON(fiber.Map{
"markers": markersWithPhotos,
"currentPage": page,
"totalPages": totalPages,
"totalMarkers": total,
})
}

// CheckDislikeStatus handler
Expand Down Expand Up @@ -318,8 +330,9 @@ func CheckDislikeStatus(c *fiber.Ctx) error {
// @Param longitude query number true "Longitude of the location (float)"
// @Param distance query int true "Search radius distance (meters)"
// @Param N query int true "Number of markers to return"
// @Param page query int true "Page Index number"
// @Security ApiKeyAuth
// @Success 200 {array} dto.MarkerWithDistance "Markers found successfully (with distance)"
// @Success 200 {object} map[string]interface{} "Markers found successfully (with distance) in pages"
// @Failure 400 {object} map[string]interface{} "Invalid query parameters"
// @Failure 404 {object} map[string]interface{} "No markers found within the specified distance"
// @Failure 500 {object} map[string]interface{} "Internal server error"
Expand All @@ -334,16 +347,35 @@ func FindCloseMarkersHandler(c *fiber.Ctx) error {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Distance cannot be greater than 3,000m (3km)"})
}

// Find nearby markers within the specified distance
markers, err := services.FindClosestNMarkersWithinDistance(params.Latitude, params.Longitude, params.Distance, params.N)
// Set default page to 1 if not specified
if params.Page < 1 {
params.Page = 1
}

pageSize := 4 // Define page size
offset := (params.Page - 1) * pageSize

// Find nearby markers within the specified distance and page
markers, total, err := services.FindClosestNMarkersWithinDistance(params.Latitude, params.Longitude, params.Distance, pageSize, offset)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}

if len(markers) == 0 {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"message": "No markers found within the specified distance"})
// if len(markers) == 0 {
// return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"message": "No markers found within the specified distance"})
// }

// Calculate total pages
totalPages := total / pageSize
if total%pageSize != 0 {
totalPages++
}

// Return the found markers
return c.JSON(markers)
// Return the found markers along with pagination info
return c.JSON(fiber.Map{
"markers": markers,
"currentPage": params.Page,
"totalPages": totalPages,
"totalMarkers": total,
})
}
80 changes: 57 additions & 23 deletions backend/services/marker_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"chulbong-kr/database"
"chulbong-kr/dto"
"chulbong-kr/models"

"github.com/jmoiron/sqlx"
)

func CreateMarkerWithPhotos(markerDto *dto.MarkerRequest, userID int, form *multipart.Form) (*dto.MarkerResponse, error) {
Expand Down Expand Up @@ -119,34 +121,37 @@ GROUP BY Markers.MarkerID, Users.Username, Markers.CreatedAt, Markers.UpdatedAt`
return markersWithPhotos, nil
}

func GetAllMarkersByUser(userID int) ([]models.MarkerWithPhotos, error) {
// Query to select markers created by a specific user
const markerQuery = `
func GetAllMarkersByUserWithPagination(userID, page, pageSize int) ([]models.MarkerWithPhotos, int, error) {
offset := (page - 1) * pageSize

// Query to select markers created by a specific user with LIMIT and OFFSET for pagination
markerQuery := `
SELECT Markers.MarkerID, Markers.UserID, ST_Y(Location) AS Latitude, ST_X(Location) AS Longitude,
Markers.Description, Users.Username, Markers.CreatedAt, Markers.UpdatedAt,
COUNT(MarkerDislikes.DislikeID) AS DislikeCount
FROM Markers
JOIN Users ON Markers.UserID = Users.UserID
LEFT JOIN MarkerDislikes ON Markers.MarkerID = MarkerDislikes.MarkerID
WHERE Markers.UserID = ?
GROUP BY Markers.MarkerID, Users.Username, Markers.CreatedAt, Markers.UpdatedAt`
GROUP BY Markers.MarkerID, Users.Username, Markers.CreatedAt, Markers.UpdatedAt
LIMIT ? OFFSET ?`

var markersWithUsernames []struct {
models.Marker
Username string `db:"Username"`
DislikeCount int `db:"DislikeCount"`
}
err := database.DB.Select(&markersWithUsernames, markerQuery, userID)
var markersWithUsernames []dto.MarkerWithDislike
err := database.DB.Select(&markersWithUsernames, markerQuery, userID, pageSize, offset)
if err != nil {
return nil, err
return nil, 0, err
}

// Fetch all photos at once
const photoQuery = `SELECT * FROM Photos`
photoQuery := `SELECT * FROM Photos WHERE MarkerID IN (?)`
query, args, err := sqlx.In(photoQuery, getMarkerIDs(markersWithUsernames))
if err != nil {
return nil, 0, err
}
var allPhotos []models.Photo
err = database.DB.Select(&allPhotos, photoQuery)
err = database.DB.Select(&allPhotos, database.DB.Rebind(query), args...)
if err != nil {
return nil, err
return nil, 0, err
}

// Map photos to their markers
Expand All @@ -166,7 +171,15 @@ GROUP BY Markers.MarkerID, Users.Username, Markers.CreatedAt, Markers.UpdatedAt`
})
}

return markersWithPhotos, nil
// Query to get the total count of markers for the user
countQuery := `SELECT COUNT(DISTINCT Markers.MarkerID) FROM Markers WHERE Markers.UserID = ?`
var total int
err = database.DB.Get(&total, countQuery, userID)
if err != nil {
return nil, 0, err
}

return markersWithPhotos, total, nil
}

// GetMarker retrieves a single marker and its associated photo by the marker's ID
Expand Down Expand Up @@ -316,22 +329,43 @@ SELECT EXISTS (
}

// FindClosestNMarkersWithinDistance
func FindClosestNMarkersWithinDistance(lat, long float64, distance, n int) ([]dto.MarkerWithDistance, error) {
point := fmt.Sprintf("POINT(%f %f)", long, lat) // Note: MySQL expects longitude first
func FindClosestNMarkersWithinDistance(lat, long float64, distance, pageSize, offset int) ([]dto.MarkerWithDistance, int, error) {
point := fmt.Sprintf("POINT(%f %f)", long, lat)

// Modify the query to also select the distance
// Query to find markers within N meters and calculate total
query := `
SELECT MarkerID, UserID, ST_Y(Location) AS Latitude, ST_X(Location) AS Longitude, Description, CreatedAt, UpdatedAt, ST_Distance_Sphere(Location, ST_GeomFromText(?)) AS distance
SELECT MarkerID, UserID, ST_Y(Location) AS Latitude, ST_X(Location) AS Longitude, Description, CreatedAt, UpdatedAt, ST_Distance_Sphere(Location, ST_GeomFromText(?)) AS distance
FROM Markers
WHERE ST_Distance_Sphere(Location, ST_GeomFromText(?)) <= ?
ORDER BY distance ASC
LIMIT ?`
LIMIT ? OFFSET ?`

var markers []dto.MarkerWithDistance
err := database.DB.Select(&markers, query, point, point, distance, n)
err := database.DB.Select(&markers, query, point, point, distance, pageSize, offset)
if err != nil {
return nil, 0, fmt.Errorf("error checking for nearby markers: %w", err)
}

// Query to get total count of markers within distance
countQuery := `
SELECT COUNT(*)
FROM Markers
WHERE ST_Distance_Sphere(Location, ST_GeomFromText(?)) <= ?`

var total int
err = database.DB.Get(&total, countQuery, point, distance)
if err != nil {
return nil, fmt.Errorf("error checking for nearby markers: %w", err)
return nil, 0, fmt.Errorf("error getting total markers count: %w", err)
}

return markers, nil
return markers, total, nil
}

// Helper function to extract marker IDs
func getMarkerIDs(markers []dto.MarkerWithDislike) []interface{} {
ids := make([]interface{}, len(markers))
for i, marker := range markers {
ids[i] = marker.MarkerID
}
return ids
}

0 comments on commit 56e8b65

Please sign in to comment.