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 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"` } 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) } // https://fulmicoton.com/posts/bayesian_rating/ 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 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 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 } type GetLocationRow struct { 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 sql.NullString `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"` } var getLocationQ = ` 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 ` func (q *Queries) GetLocation(ctx context.Context, location_id int32) (GetLocationRow, error) { row := q.db.QueryRowContext(ctx, getLocationQ, location_id) 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 } 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 { Address string `json:"address"` Name string `json:"name"` SubmittedBy int32 `json:"submitted_by"` LocationType LocationType `json:"location_type"` RegencyID int16 `json:"regency_id"` GoogleMapsLink sql.NullString `json:"google_maps_link"` IsDeleted bool `json:"is_deleted"` ApprovedBy sql.NullInt32 `json:"approved_by"` } func (q *Queries) CreateLocation(ctx context.Context, arg CreateLocationParams) (int32, error) { row := q.db.QueryRowContext(ctx, createLocation, 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 }