feat: index on movie genres, metadata, filters, pagination
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/lib/pq"
|
||||
@@ -18,23 +19,31 @@ type MovieModel struct {
|
||||
// Create a new GetAll() method which returns a slice of movies. Although we're not
|
||||
// using them right now, we've set this up to accept the various filter parameters as
|
||||
// arguments.
|
||||
func (m MovieModel) GetAll(title string, genres []string, filters Filters) ([]*Movie, error) {
|
||||
func (m MovieModel) GetAll(title string, genres []string, filters Filters) ([]*Movie, Metadata, error) {
|
||||
// Use full-text search for the title filter.
|
||||
query := `
|
||||
SELECT id, created_at, title, year, runtime, genres, version
|
||||
query := fmt.Sprintf(`
|
||||
SELECT count(*) OVER(), id, created_at, title, year, runtime, genres, version
|
||||
FROM movies
|
||||
WHERE (to_tsvector('simple', title) @@ plainto_tsquery('simple', $1) OR $1 = '')
|
||||
AND (genres @> $2 OR $2 = '{}')
|
||||
ORDER BY id`
|
||||
ORDER BY %s %s, id ASC
|
||||
LIMIT $3 OFFSET $4`, filters.sortColumn(), filters.sortDirection())
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
args := []any{
|
||||
title,
|
||||
pq.Array(genres),
|
||||
filters.limit(),
|
||||
filters.offset(),
|
||||
}
|
||||
|
||||
// Use QueryContext() to execute the query. This returns a sql.Rows resultset
|
||||
// containing the result.
|
||||
rows, err := m.DB.QueryContext(ctx, query, title, pq.Array(genres))
|
||||
rows, err := m.DB.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, Metadata{}, err
|
||||
}
|
||||
|
||||
// Importantly, defer a call to rows.Close() to ensure that the resultset is closed
|
||||
@@ -42,6 +51,7 @@ func (m MovieModel) GetAll(title string, genres []string, filters Filters) ([]*M
|
||||
defer rows.Close()
|
||||
|
||||
// Initialize an empty slice to hold the movie data.
|
||||
totalRecords := 0
|
||||
movies := []*Movie{}
|
||||
|
||||
// Use rows.Next to iterate through the rows in the resultset.
|
||||
@@ -52,6 +62,7 @@ func (m MovieModel) GetAll(title string, genres []string, filters Filters) ([]*M
|
||||
// Scan the values from the row into the Movie struct. Again, note that we're
|
||||
// using the pq.Array() adapter on the genres field here.
|
||||
err := rows.Scan(
|
||||
&totalRecords,
|
||||
&movie.ID,
|
||||
&movie.CreatedAt,
|
||||
&movie.Title,
|
||||
@@ -61,7 +72,7 @@ func (m MovieModel) GetAll(title string, genres []string, filters Filters) ([]*M
|
||||
&movie.Version,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, Metadata{}, err
|
||||
}
|
||||
|
||||
// Add the Movie struct to the slice.
|
||||
@@ -71,11 +82,15 @@ func (m MovieModel) GetAll(title string, genres []string, filters Filters) ([]*M
|
||||
// When the rows.Next() loop has finished, call rows.Err() to retrieve any error
|
||||
// that was encountered during the iteration.
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
return nil, Metadata{}, err
|
||||
}
|
||||
|
||||
// Generate a Metadata struct, passing in the total record count and pagination
|
||||
// parameters from the client.
|
||||
metadata := calculateMetadata(totalRecords, filters.Page, filters.PageSize)
|
||||
|
||||
// If everything went OK, then return the slice of movies.
|
||||
return movies, nil
|
||||
return movies, metadata, nil
|
||||
}
|
||||
|
||||
// Add a placeholder method for inserting a new record in the movies table.
|
||||
|
||||
Reference in New Issue
Block a user