feat: 🎉 Rating Orama!
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type postgresDBRepo struct {
|
||||
DB *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewPostgresRepo(conn *pgxpool.Pool) DBRepo {
|
||||
return &postgresDBRepo{
|
||||
DB: conn,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package repository
|
||||
|
||||
import "github.com/zepyrshut/rating-orama/models"
|
||||
|
||||
type DBRepo interface {
|
||||
CheckIfTvShowExists(showID string) bool
|
||||
InsertTvShow(tvShow models.TvShow) error
|
||||
InsertEpisodes(tvShow models.TvShow) error
|
||||
FetchTvShow(showID string) (models.TvShow, error)
|
||||
IncreasePopularity(showID string)
|
||||
FetchEpisodes(showID string) ([]models.Season, error)
|
||||
TvShowAverageRating(show *models.TvShow)
|
||||
SeasonAverageRating(show *models.TvShow)
|
||||
TvShowMedianRating(show *models.TvShow)
|
||||
SeasonMedianRating(show *models.TvShow)
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/zepyrshut/rating-orama/models"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (pg *postgresDBRepo) CheckIfTvShowExists(showID string) bool {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
query := `SELECT show_id FROM tv_show WHERE show_id = $1`
|
||||
|
||||
var showIDFromDB string
|
||||
err := pg.DB.QueryRow(ctx, query, showID).Scan(&showIDFromDB)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (pg *postgresDBRepo) InsertTvShow(tvShow models.TvShow) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
queryTvShow := `INSERT INTO tv_show (show_id, title, runtime) VALUES ($1, $2, $3)`
|
||||
|
||||
_, err := pg.DB.Exec(ctx, queryTvShow, tvShow.ShowID, tvShow.Title, tvShow.Runtime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = pg.InsertEpisodes(tvShow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pg *postgresDBRepo) InsertEpisodes(tvShow models.TvShow) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
query := `INSERT INTO episodes (episode_id, tv_show_id, season_number, title, number, aired, avg_rating, votes) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`
|
||||
|
||||
for k, season := range tvShow.Seasons {
|
||||
for _, episode := range season.Episodes {
|
||||
_, err := pg.DB.Exec(ctx, query, episode.EpisodeID, tvShow.ShowID, k+1, episode.Title, episode.Number, episode.Aired, episode.AvgRating, episode.Votes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pg *postgresDBRepo) FetchTvShow(showID string) (models.TvShow, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
query := `SELECT show_id, title, runtime FROM tv_show WHERE show_id = $1`
|
||||
|
||||
var tvShow models.TvShow
|
||||
var tvShowID int
|
||||
err := pg.DB.QueryRow(ctx, query, showID).Scan(&tvShowID, &tvShow.Title, &tvShow.Runtime)
|
||||
if err != nil {
|
||||
return tvShow, err
|
||||
}
|
||||
|
||||
tvShow.ShowID = fmt.Sprintf("%07d", tvShowID)
|
||||
|
||||
tvShow.Seasons, err = pg.FetchEpisodes(showID)
|
||||
if err != nil {
|
||||
return tvShow, err
|
||||
}
|
||||
|
||||
pg.TvShowAverageRating(&tvShow)
|
||||
pg.SeasonAverageRating(&tvShow)
|
||||
|
||||
pg.TvShowMedianRating(&tvShow)
|
||||
pg.SeasonMedianRating(&tvShow)
|
||||
|
||||
pg.IncreasePopularity(showID)
|
||||
return tvShow, nil
|
||||
}
|
||||
|
||||
func (pg *postgresDBRepo) IncreasePopularity(showID string) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
query := `UPDATE tv_show SET popularity = popularity + 1 WHERE show_id = $1`
|
||||
|
||||
_, err := pg.DB.Exec(ctx, query, showID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (pg *postgresDBRepo) FetchEpisodes(showID string) ([]models.Season, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
query := `SELECT episode_id, season_number, title, number, aired, avg_rating, votes FROM episodes WHERE tv_show_id = $1 ORDER BY season_number, number`
|
||||
|
||||
rows, err := pg.DB.Query(ctx, query, showID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var seasons []models.Season
|
||||
var episodeID int
|
||||
for rows.Next() {
|
||||
var episode models.Episode
|
||||
var seasonNumber int
|
||||
err = rows.Scan(&episodeID, &seasonNumber, &episode.Title, &episode.Number, &episode.Aired, &episode.AvgRating, &episode.Votes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
episode.EpisodeID = fmt.Sprintf("%07d", episodeID)
|
||||
|
||||
if len(seasons) < seasonNumber {
|
||||
seasons = append(seasons, models.Season{})
|
||||
}
|
||||
|
||||
seasons[seasonNumber-1].Number = seasonNumber
|
||||
seasons[seasonNumber-1].Episodes = append(seasons[seasonNumber-1].Episodes, episode)
|
||||
}
|
||||
|
||||
return seasons, nil
|
||||
}
|
||||
|
||||
func (pg *postgresDBRepo) TvShowAverageRating(show *models.TvShow) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
query :=
|
||||
`
|
||||
SELECT
|
||||
AVG(avg_rating), SUM(votes)
|
||||
FROM
|
||||
episodes
|
||||
WHERE
|
||||
tv_show_id = $1
|
||||
AND
|
||||
votes > 0 AND avg_rating > 0
|
||||
`
|
||||
|
||||
var avgRating float64
|
||||
var votes int
|
||||
err := pg.DB.QueryRow(ctx, query, show.ShowID).Scan(&avgRating, &votes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
show.AvgRating = avgRating
|
||||
show.Votes = votes
|
||||
}
|
||||
|
||||
func (pg *postgresDBRepo) SeasonAverageRating(show *models.TvShow) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
query :=
|
||||
`
|
||||
SELECT
|
||||
season_number, AVG(avg_rating), SUM(votes)
|
||||
FROM
|
||||
episodes
|
||||
WHERE
|
||||
tv_show_id = $1
|
||||
AND
|
||||
votes > 0 AND avg_rating > 0
|
||||
GROUP BY
|
||||
season_number
|
||||
ORDER BY
|
||||
season_number;
|
||||
`
|
||||
|
||||
rows, err := pg.DB.Query(ctx, query, show.ShowID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var seasonNumber int
|
||||
var avgRating float64
|
||||
var votes int
|
||||
err = rows.Scan(&seasonNumber, &avgRating, &votes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
show.Seasons[seasonNumber-1].AvgRating = avgRating
|
||||
show.Seasons[seasonNumber-1].Votes = votes
|
||||
}
|
||||
}
|
||||
|
||||
func (pg *postgresDBRepo) TvShowMedianRating(show *models.TvShow) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
query :=
|
||||
`
|
||||
SELECT
|
||||
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY avg_rating) AS median_rating
|
||||
FROM
|
||||
episodes
|
||||
WHERE
|
||||
tv_show_id = $1
|
||||
AND
|
||||
votes > 0 AND avg_rating > 0;
|
||||
`
|
||||
|
||||
var medianRating float64
|
||||
err := pg.DB.QueryRow(ctx, query, show.ShowID).Scan(&medianRating)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
show.MedianRating = medianRating
|
||||
}
|
||||
|
||||
func (pg *postgresDBRepo) SeasonMedianRating(show *models.TvShow) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
query :=
|
||||
`
|
||||
WITH episodes_with_ranks AS (
|
||||
SELECT
|
||||
episode_id,
|
||||
tv_show_id,
|
||||
season_number,
|
||||
avg_rating,
|
||||
votes,
|
||||
ROW_NUMBER() OVER (PARTITION BY season_number ORDER BY avg_rating) AS rank_asc,
|
||||
ROW_NUMBER() OVER (PARTITION BY season_number ORDER BY avg_rating DESC) AS rank_desc,
|
||||
COUNT(*) OVER (PARTITION BY season_number) AS season_episode_count
|
||||
FROM
|
||||
episodes
|
||||
WHERE
|
||||
tv_show_id = $1
|
||||
AND
|
||||
votes > 0 AND avg_rating > 0
|
||||
),
|
||||
episodes_filtered AS (
|
||||
SELECT
|
||||
episode_id,
|
||||
tv_show_id,
|
||||
season_number,
|
||||
avg_rating,
|
||||
votes
|
||||
FROM
|
||||
episodes_with_ranks
|
||||
WHERE
|
||||
rank_asc > season_episode_count * 0.01 AND rank_desc > season_episode_count * 0.01
|
||||
),
|
||||
seasons AS (
|
||||
SELECT DISTINCT
|
||||
season_number
|
||||
FROM
|
||||
episodes_filtered
|
||||
)
|
||||
SELECT
|
||||
seasons.season_number,
|
||||
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY episodes_filtered.avg_rating) AS median_rating
|
||||
FROM
|
||||
seasons
|
||||
JOIN
|
||||
episodes_filtered ON seasons.season_number = episodes_filtered.season_number
|
||||
GROUP BY
|
||||
seasons.season_number
|
||||
ORDER BY
|
||||
seasons.season_number;
|
||||
`
|
||||
|
||||
rows, err := pg.DB.Query(ctx, query, show.ShowID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var seasonNumber int
|
||||
var medianRating float64
|
||||
err = rows.Scan(&seasonNumber, &medianRating)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
show.Seasons[seasonNumber-1].MedianRating = medianRating
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user