add top locations query
This commit is contained in:
parent
548c086079
commit
fa86c375c5
@ -110,6 +110,47 @@ func (server *Server) getListLocations(ctx *gin.Context) {
|
|||||||
ctx.JSON(http.StatusOK, locations)
|
ctx.JSON(http.StatusOK, locations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type getTopListLocationsReq struct {
|
||||||
|
Page int32 `form:"page" binding:"required,min=1"`
|
||||||
|
PageSize int32 `form:"page_size" binding:"required,min=5"`
|
||||||
|
OrderBy int16 `form:"order_by" binding:"numeric,min=1,max=3"`
|
||||||
|
RegionType int16 `form:"region_type" binding:"numeric,min=0,max=7"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (server *Server) getTopListLocations(ctx *gin.Context) {
|
||||||
|
var req getTopListLocationsReq
|
||||||
|
var orderby string
|
||||||
|
|
||||||
|
if err := ctx.ShouldBindQuery(&req); err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch req.OrderBy {
|
||||||
|
case 1:
|
||||||
|
orderby = "iq2.avg_bayes"
|
||||||
|
case 2:
|
||||||
|
orderby = "iq2.critic_bayes"
|
||||||
|
case 3:
|
||||||
|
orderby = "iq2.user_bayes"
|
||||||
|
}
|
||||||
|
|
||||||
|
arg := db.GetTopListLocationsParams{
|
||||||
|
Limit: req.PageSize,
|
||||||
|
Offset: (req.Page - 1) * req.PageSize,
|
||||||
|
OrderBy: orderby,
|
||||||
|
RegionType: sql.NullInt16{Valid: req.RegionType > 0, Int16: req.RegionType},
|
||||||
|
}
|
||||||
|
|
||||||
|
locations, err := server.Store.GetTopListLocations(ctx, arg)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, locations)
|
||||||
|
}
|
||||||
|
|
||||||
type getListRecentLocationsWithRatingsReq struct {
|
type getListRecentLocationsWithRatingsReq struct {
|
||||||
Page int32 `form:"page_size" binding:"required,min=1"`
|
Page int32 `form:"page_size" binding:"required,min=1"`
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,8 @@ func (server *Server) getRoutes() {
|
|||||||
|
|
||||||
// LOCATION
|
// LOCATION
|
||||||
router.POST("/locations", server.createLocation)
|
router.POST("/locations", server.createLocation)
|
||||||
router.GET("/recent-locations/ratings", server.getListRecentLocationsWithRatings)
|
router.GET("/locations/recent", server.getListRecentLocationsWithRatings)
|
||||||
|
router.GET("/locations/top-ratings", server.getTopListLocations)
|
||||||
router.GET("/locations", server.getListLocations)
|
router.GET("/locations", server.getListLocations)
|
||||||
router.GET("/location/:location_id", server.getLocation)
|
router.GET("/location/:location_id", server.getLocation)
|
||||||
|
|
||||||
|
@ -109,6 +109,21 @@ func (mr *MockStoreMockRecorder) GetLocation(arg0, arg1 interface{}) *gomock.Cal
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocation", reflect.TypeOf((*MockStore)(nil).GetLocation), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocation", reflect.TypeOf((*MockStore)(nil).GetLocation), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTopListLocations mocks base method.
|
||||||
|
func (m *MockStore) GetTopListLocations(arg0 context.Context, arg1 db.GetTopListLocationsParams) ([]db.GetTopListLocationsRow, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetTopListLocations", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].([]db.GetTopListLocationsRow)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTopListLocations indicates an expected call of GetTopListLocations.
|
||||||
|
func (mr *MockStoreMockRecorder) GetTopListLocations(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTopListLocations", reflect.TypeOf((*MockStore)(nil).GetTopListLocations), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
// UpdatePassword mocks base method.
|
// UpdatePassword mocks base method.
|
||||||
func (m *MockStore) UpdatePassword(arg0 context.Context, arg1 db.UpdatePasswordParams) error {
|
func (m *MockStore) UpdatePassword(arg0 context.Context, arg1 db.UpdatePasswordParams) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
110
db/sqlc/locations.go
Normal file
110
db/sqlc/locations.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetTopListLocationsParams struct {
|
||||||
|
Limit int32
|
||||||
|
Offset int32
|
||||||
|
OrderBy string
|
||||||
|
RegionType sql.NullInt16
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetTopListLocationsRow struct {
|
||||||
|
RowNumber int32 `json:"row_number"`
|
||||||
|
ID int32 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
RegionName string `json:"region_name"`
|
||||||
|
Thumbnail sql.NullString `json:"thumbnail"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
GoogleMapsLink string `json:"google_maps_link"`
|
||||||
|
RegencyName string `json:"regency_name"`
|
||||||
|
CriticScore sql.NullInt32 `json:"critic_score"`
|
||||||
|
CriticCount int16 `json:"critic_count"`
|
||||||
|
UserScore sql.NullInt32 `json:"user_score"`
|
||||||
|
UserCount int16 `json:"user_count"`
|
||||||
|
TotalCount int16 `json:"total_count"`
|
||||||
|
CriticBayes int16 `json:"critic_bayes"`
|
||||||
|
UserBayes int16 `json:"user_bayes"`
|
||||||
|
AvgBayes int16 `json:"avg_bayes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetTopListLocations(ctx context.Context, arg GetTopListLocationsParams) ([]GetTopListLocationsRow, error) {
|
||||||
|
regionType := ""
|
||||||
|
|
||||||
|
if arg.RegionType.Valid {
|
||||||
|
regionType = fmt.Sprintf("AND reg.id = %d", arg.RegionType.Int16)
|
||||||
|
}
|
||||||
|
|
||||||
|
getTopListQ := fmt.Sprintf(`SELECT
|
||||||
|
row_number() over (ORDER BY %s DESC) as row_number,
|
||||||
|
*
|
||||||
|
FROM(
|
||||||
|
SELECT
|
||||||
|
*,
|
||||||
|
(SELECT 5 * 4 + coalesce(critic_score, 0) * coalesce(critic_count, 0) / 5 + coalesce(critic_count, 0)) as critic_bayes,
|
||||||
|
(SELECT 50 + coalesce(user_score, 0) * coalesce(user_count, 0) / 50 + coalesce(user_count, 0)) as user_bayes,
|
||||||
|
((SELECT 50 + coalesce(user_score, 0) * coalesce(user_count, 0) / 50 + coalesce(user_count, 0)) + (SELECT 5 * 4 + coalesce(critic_score, 0) * coalesce(critic_count, 0) / 5 + coalesce(critic_count, 0)) ) / 2 as avg_bayes
|
||||||
|
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
l.id,
|
||||||
|
name,
|
||||||
|
l.address,
|
||||||
|
reg.region_name as region_name,
|
||||||
|
l.google_maps_link,
|
||||||
|
thumbnail,
|
||||||
|
re.regency_name,
|
||||||
|
(SELECT SUM(score) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_score,
|
||||||
|
(SELECT COUNT(id) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_count,
|
||||||
|
(SELECT SUM(score) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_score,
|
||||||
|
(SELECT COUNT(id) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_count
|
||||||
|
FROM locations l
|
||||||
|
JOIN regencies re on re.id = l.regency_id
|
||||||
|
JOIN provinces prov on prov.id = re.province_id
|
||||||
|
JOIN regions reg on reg.id = prov.region_id
|
||||||
|
WHERE approved_by IS NOT NULL %s ) iq1 ) iq2
|
||||||
|
LIMIT $1
|
||||||
|
OFFSET $2;`, arg.OrderBy, regionType)
|
||||||
|
|
||||||
|
rows, err := q.db.QueryContext(ctx, getTopListQ, arg.Limit, arg.Offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []GetTopListLocationsRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetTopListLocationsRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.RowNumber,
|
||||||
|
&i.ID,
|
||||||
|
&i.Name,
|
||||||
|
&i.Address,
|
||||||
|
&i.RegionName,
|
||||||
|
&i.GoogleMapsLink,
|
||||||
|
&i.Thumbnail,
|
||||||
|
&i.RegencyName,
|
||||||
|
&i.CriticScore,
|
||||||
|
&i.CriticCount,
|
||||||
|
&i.UserScore,
|
||||||
|
&i.UserCount,
|
||||||
|
&i.CriticBayes,
|
||||||
|
&i.UserBayes,
|
||||||
|
&i.AvgBayes,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
i.TotalCount = i.UserCount + i.CriticCount
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
@ -143,10 +143,38 @@ func (q *Queries) GetListRecentLocationsWithRatings(ctx context.Context, limit i
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getLocation = `-- name: GetLocation :one
|
const getLocation = `-- name: GetLocation :one
|
||||||
|
|
||||||
|
|
||||||
SELECT id, address, name, google_maps_link, submitted_by, total_visited, thumbnail, regency_id, is_deleted, created_at, updated_at, approved_by, approved_at FROM locations
|
SELECT id, address, name, google_maps_link, submitted_by, total_visited, thumbnail, regency_id, is_deleted, created_at, updated_at, approved_by, approved_at FROM locations
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
|
// https://fulmicoton.com/posts/bayesian_rating/
|
||||||
|
// SELECT
|
||||||
|
//
|
||||||
|
// *,
|
||||||
|
// (SELECT 5 * 4 + coalesce(critic_score, 0) * coalesce(critic_count, 0) / 5 + coalesce(critic_count, 0)) as critic_bayes,
|
||||||
|
// (SELECT 50 + coalesce(user_score, 0) * coalesce(user_count, 0) / 50 + coalesce(user_count, 0)) as user_bayes,
|
||||||
|
// ((SELECT 50 + coalesce(user_score, 0) * coalesce(user_count, 0) / 50 + coalesce(user_count, 0)) + (SELECT 5 * 4 + coalesce(critic_score, 0) * coalesce(critic_count, 0) / 5 + coalesce(critic_count, 0)) ) / 2 as avg_bayes
|
||||||
|
//
|
||||||
|
// FROM (
|
||||||
|
//
|
||||||
|
// SELECT
|
||||||
|
// l.id,
|
||||||
|
// name,
|
||||||
|
// thumbnail,
|
||||||
|
// re.regency_name,
|
||||||
|
// (SELECT SUM(score) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_score,
|
||||||
|
// (SELECT COUNT(id) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_count,
|
||||||
|
// (SELECT SUM(score) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_score,
|
||||||
|
// (SELECT COUNT(id) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_count
|
||||||
|
// FROM locations l
|
||||||
|
// JOIN regencies re on re.id = l.regency_id
|
||||||
|
// WHERE approved_by IS NOT NULL) iq1
|
||||||
|
//
|
||||||
|
// ORDER BY avg_bayes DESC
|
||||||
|
// LIMIT $1
|
||||||
|
// OFFSET $2;
|
||||||
func (q *Queries) GetLocation(ctx context.Context, id int32) (Location, error) {
|
func (q *Queries) GetLocation(ctx context.Context, id int32) (Location, error) {
|
||||||
row := q.db.QueryRowContext(ctx, getLocation, id)
|
row := q.db.QueryRowContext(ctx, getLocation, id)
|
||||||
var i Location
|
var i Location
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
"github.com/yiplee/sqlc"
|
"github.com/yiplee/sqlc"
|
||||||
@ -8,6 +9,7 @@ import (
|
|||||||
|
|
||||||
type Store interface {
|
type Store interface {
|
||||||
Querier
|
Querier
|
||||||
|
GetTopListLocations(ctx context.Context, arg GetTopListLocationsParams) ([]GetTopListLocationsRow, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SQLStore struct {
|
type SQLStore struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user