2023-09-17 16:32:48 +07:00
|
|
|
package db
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2024-03-16 19:23:15 +07:00
|
|
|
"strings"
|
2024-02-06 11:55:25 +07:00
|
|
|
|
2024-03-16 19:23:15 +07:00
|
|
|
"github.com/henvic/pgq"
|
2024-02-06 11:55:25 +07:00
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
2023-09-17 16:32:48 +07:00
|
|
|
)
|
|
|
|
|
|
|
|
type GetTopListLocationsParams struct {
|
|
|
|
Limit int32
|
|
|
|
Offset int32
|
|
|
|
OrderBy string
|
2024-03-04 16:09:55 +07:00
|
|
|
RegionType pgtype.Int4
|
2023-09-17 16:32:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
type GetTopListLocationsRow struct {
|
2024-03-04 16:09:55 +07:00
|
|
|
RowNumber int32 `json:"row_number"`
|
|
|
|
ID int32 `json:"id"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
RegionName string `json:"region_name"`
|
|
|
|
Thumbnail pgtype.Text `json:"thumbnail"`
|
|
|
|
Address string `json:"address"`
|
|
|
|
GoogleMapsLink string `json:"google_maps_link"`
|
|
|
|
RegencyName string `json:"regency_name"`
|
|
|
|
CriticScore int16 `json:"critic_score"`
|
|
|
|
CriticCount int16 `json:"critic_count"`
|
|
|
|
UserScore int16 `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"`
|
2023-09-17 16:32:48 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (q *Queries) GetTopListLocations(ctx context.Context, arg GetTopListLocationsParams) ([]GetTopListLocationsRow, error) {
|
|
|
|
regionType := ""
|
|
|
|
|
|
|
|
if arg.RegionType.Valid {
|
2024-03-04 16:09:55 +07:00
|
|
|
regionType = fmt.Sprintf("AND reg.id = %d", arg.RegionType.Int32)
|
2023-09-17 16:32:48 +07:00
|
|
|
}
|
|
|
|
|
2023-09-19 21:50:33 +07:00
|
|
|
// https://fulmicoton.com/posts/bayesian_rating/
|
2023-09-17 16:32:48 +07:00
|
|
|
getTopListQ := fmt.Sprintf(`SELECT
|
|
|
|
row_number() over (ORDER BY %s DESC) as row_number,
|
|
|
|
*
|
|
|
|
FROM(
|
|
|
|
SELECT
|
|
|
|
*,
|
2023-10-11 16:31:52 +07:00
|
|
|
(SELECT 5 * 5 + COALESCE(critic_score, 0) * COALESCE(critic_count, 0) / 5 + COALESCE(critic_count, 0)) as critic_bayes,
|
|
|
|
(SELECT 5 * 5 + COALESCE(user_score, 0) * COALESCE(user_count, 0) / 5 + COALESCE(user_count, 0)) as user_bayes,
|
|
|
|
((SELECT 5 * 5 + COALESCE(user_score, 0) * COALESCE(user_count, 0) / 50 + COALESCE(user_count, 0)) + (SELECT 5 * 5 + COALESCE(critic_score, 0) * COALESCE(critic_count, 0) / 5 + COALESCE(critic_count, 0)) ) / 2 as avg_bayes
|
2023-09-17 16:32:48 +07:00
|
|
|
|
|
|
|
FROM (
|
|
|
|
SELECT
|
|
|
|
l.id,
|
|
|
|
name,
|
|
|
|
l.address,
|
|
|
|
reg.region_name as region_name,
|
|
|
|
l.google_maps_link,
|
|
|
|
thumbnail,
|
|
|
|
re.regency_name,
|
2023-09-20 11:20:06 +07:00
|
|
|
(SELECT COALESCE(SUM(score), 0) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_score,
|
2023-09-17 16:32:48 +07:00
|
|
|
(SELECT COUNT(id) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_count,
|
2023-09-20 11:20:06 +07:00
|
|
|
(SELECT COALESCE(SUM(score), 0) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_score,
|
2023-09-17 16:32:48 +07:00
|
|
|
(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)
|
|
|
|
|
2024-02-06 11:55:25 +07:00
|
|
|
rows, err := q.db.Query(ctx, getTopListQ, arg.Limit, arg.Offset)
|
2023-09-17 16:32:48 +07:00
|
|
|
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)
|
|
|
|
}
|
2024-02-06 11:55:25 +07:00
|
|
|
rows.Close()
|
2023-09-17 16:32:48 +07:00
|
|
|
if err := rows.Err(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return items, nil
|
|
|
|
}
|
2023-09-27 21:58:14 +07:00
|
|
|
|
2024-03-16 19:23:15 +07:00
|
|
|
type GetListRecentLocationsWithRatingsRow struct {
|
|
|
|
ID int32 `json:"id"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Thumbnail pgtype.Text `json:"thumbnail"`
|
|
|
|
LocationType pgtype.Text `json:"location_type"`
|
|
|
|
RegencyName string `json:"regency_name"`
|
|
|
|
ProvinceName string `json:"province_name"`
|
|
|
|
CriticScore interface{} `json:"critic_score"`
|
|
|
|
CriticCount int64 `json:"critic_count"`
|
|
|
|
UserScore interface{} `json:"user_score"`
|
|
|
|
UserCount int64 `json:"user_count"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type GetListRecentLocationsParams struct {
|
|
|
|
Limit int32 `json:"limit" default:"12"`
|
|
|
|
Regions string `json:"regions" default:""`
|
|
|
|
LocationTypes string `json:"location_type" default:""`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (q *Queries) GetListRecentLocationsWithRatings(ctx context.Context, arg GetListRecentLocationsParams) ([]GetListRecentLocationsWithRatingsRow, error) {
|
|
|
|
sqlBuilder := pgq.Select(
|
|
|
|
"l.id",
|
|
|
|
"name",
|
|
|
|
"thumbnail",
|
|
|
|
"l.location_type",
|
|
|
|
"COALESCE(re.regency_name, '') as regency_name",
|
|
|
|
"COALESCE(pr.province_name, '') as province_name",
|
|
|
|
"(SELECT COALESCE(SUM(score), 0) 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 COALESCE(SUM(score), 0) 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 pr on re.province_id = pr.id").
|
|
|
|
Limit(uint64(arg.Limit))
|
|
|
|
|
|
|
|
if arg.Regions != "" {
|
|
|
|
region := strings.Split(arg.Regions, ",")
|
|
|
|
sqlBuilder = sqlBuilder.Where(pgq.Eq{"re.id": region})
|
|
|
|
}
|
|
|
|
|
|
|
|
if arg.LocationTypes != "" {
|
|
|
|
locType := strings.Split(arg.LocationTypes, ",")
|
|
|
|
sqlBuilder = sqlBuilder.Where(pgq.Eq{"l.location_type": locType})
|
|
|
|
}
|
|
|
|
|
|
|
|
query, arguments, err := sqlBuilder.Where(pgq.NotEq{"approved_by": nil}).SQL()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
rows, err := q.db.Query(ctx, query, arguments...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
items := []GetListRecentLocationsWithRatingsRow{}
|
|
|
|
for rows.Next() {
|
|
|
|
var i GetListRecentLocationsWithRatingsRow
|
|
|
|
if err := rows.Scan(
|
|
|
|
&i.ID,
|
|
|
|
&i.Name,
|
|
|
|
&i.Thumbnail,
|
|
|
|
&i.LocationType,
|
|
|
|
&i.RegencyName,
|
|
|
|
&i.ProvinceName,
|
|
|
|
&i.CriticScore,
|
|
|
|
&i.CriticCount,
|
|
|
|
&i.UserScore,
|
|
|
|
&i.UserCount,
|
|
|
|
); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
items = append(items, i)
|
|
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return items, nil
|
|
|
|
}
|
|
|
|
|
2023-09-27 21:58:14 +07:00
|
|
|
type GetLocationRow struct {
|
2024-03-04 16:09:55 +07:00
|
|
|
ID int32 `json:"id"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Address string `json:"address"`
|
|
|
|
RegencyName string `json:"regency_name"`
|
|
|
|
ProvinceName string `json:"province_name"`
|
|
|
|
RegionName string `json:"region_name"`
|
|
|
|
GoogleMapsLink string `json:"google_maps_link"`
|
|
|
|
Thumbnail pgtype.Text `json:"thumbnail"`
|
|
|
|
SubmittedBy string `json:"submitted_by"`
|
|
|
|
CriticScore int32 `json:"critic_score"`
|
|
|
|
CriticCount int32 `json:"critic_count"`
|
|
|
|
UserScore int32 `json:"user_score"`
|
|
|
|
UserCount int32 `json:"user_count"`
|
2023-09-27 21:58:14 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
var getLocationQ = `
|
2024-03-16 19:23:15 +07:00
|
|
|
SELECT
|
|
|
|
l.id,
|
|
|
|
name,
|
|
|
|
l.address,
|
|
|
|
COALESCE(re.regency_name, '') as regency_name,
|
|
|
|
COALESCE(prov.province_name, '') as province_name,
|
|
|
|
COALESCE(reg.region_name, '') as region_name,
|
|
|
|
COALESCE(l.google_maps_link, '') as google_maps_link,
|
|
|
|
thumbnail,
|
|
|
|
u.username as submitted_by,
|
|
|
|
(SELECT COALESCE(SUM(score), 0) 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 COALESCE(SUM(score), 0) 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
|
|
|
|
JOIN users u on u.id = l.submitted_by
|
|
|
|
WHERE l.id = $1
|
2023-09-27 21:58:14 +07:00
|
|
|
`
|
|
|
|
|
|
|
|
func (q *Queries) GetLocation(ctx context.Context, location_id int32) (GetLocationRow, error) {
|
2024-02-06 11:55:25 +07:00
|
|
|
row := q.db.QueryRow(ctx, getLocationQ, location_id)
|
2023-09-27 21:58:14 +07:00
|
|
|
var i GetLocationRow
|
|
|
|
|
|
|
|
err := row.Scan(
|
|
|
|
&i.ID,
|
|
|
|
&i.Name,
|
|
|
|
&i.Address,
|
|
|
|
&i.RegencyName,
|
|
|
|
&i.ProvinceName,
|
|
|
|
&i.RegionName,
|
|
|
|
&i.GoogleMapsLink,
|
|
|
|
&i.Thumbnail,
|
|
|
|
&i.SubmittedBy,
|
|
|
|
&i.CriticScore,
|
|
|
|
&i.CriticCount,
|
|
|
|
&i.UserScore,
|
|
|
|
&i.UserCount,
|
|
|
|
)
|
|
|
|
|
|
|
|
return i, err
|
|
|
|
}
|
2023-10-03 14:56:57 +07:00
|
|
|
|
|
|
|
const createLocation = `-- name: CreateLocation :exec
|
|
|
|
INSERT INTO locations(
|
|
|
|
address,
|
|
|
|
name,
|
|
|
|
submitted_by,
|
|
|
|
location_type,
|
|
|
|
regency_id,
|
|
|
|
google_maps_link,
|
|
|
|
approved_by,
|
|
|
|
is_deleted
|
|
|
|
) values (
|
|
|
|
$1, $2, $3, $4, $5, $6, $7, $8
|
|
|
|
)
|
|
|
|
RETURNING id
|
|
|
|
`
|
|
|
|
|
|
|
|
type CreateLocationParams struct {
|
2024-03-04 16:09:55 +07:00
|
|
|
Address string `json:"address"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
SubmittedBy int32 `json:"submitted_by"`
|
|
|
|
LocationType LocationType `json:"location_type"`
|
|
|
|
RegencyID int16 `json:"regency_id"`
|
|
|
|
GoogleMapsLink pgtype.Text `json:"google_maps_link"`
|
|
|
|
IsDeleted bool `json:"is_deleted"`
|
|
|
|
ApprovedBy pgtype.Int4 `json:"approved_by"`
|
2023-10-03 14:56:57 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (q *Queries) CreateLocation(ctx context.Context, arg CreateLocationParams) (int32, error) {
|
2024-02-06 11:55:25 +07:00
|
|
|
row := q.db.QueryRow(ctx, createLocation,
|
2023-10-03 14:56:57 +07:00
|
|
|
arg.Address,
|
|
|
|
arg.Name,
|
|
|
|
arg.SubmittedBy,
|
|
|
|
arg.LocationType,
|
|
|
|
arg.RegencyID,
|
|
|
|
arg.GoogleMapsLink,
|
|
|
|
arg.ApprovedBy,
|
|
|
|
arg.IsDeleted,
|
|
|
|
)
|
|
|
|
|
|
|
|
var i int32
|
|
|
|
|
|
|
|
err := row.Scan(
|
|
|
|
&i,
|
|
|
|
)
|
|
|
|
|
|
|
|
fmt.Println(i)
|
|
|
|
|
|
|
|
return i, err
|
|
|
|
}
|