Returning pagination metadata.
All checks were successful
Deploy Greenlight API / deploy (push) Successful in 2m48s
All checks were successful
Deploy Greenlight API / deploy (push) Successful in 2m48s
This commit is contained in:
@@ -50,3 +50,27 @@ func (f Filters) sortDirection() string {
|
||||
}
|
||||
return "ASC"
|
||||
}
|
||||
|
||||
// Metadata struct for holding the pagination metadata
|
||||
type Metadata struct {
|
||||
CurrentPage int `json:"current_page,omitempty"`
|
||||
PageSize int `json:"page_size,omitempty"`
|
||||
FirstPage int `json:"first_page,omitempty"`
|
||||
LastPage int `json:"last_page,omitempty"`
|
||||
TotalRecords int `json:"total_records,omitempty"`
|
||||
}
|
||||
|
||||
// calculateMetadata : calculates the appropriate pagination metadata values given the total number of records, current page, and page size values. Note that when the last page value is calculated, we are dividing two int values, and when dividing integer types in Go the result will also be an integer type, with the modulus (or reminder) dropped. So, for instance, if there were 12 records in total and a page of 5, the last page value would be (12+5-1)/5 = 3.2, which is then truncated to 3 by Go.
|
||||
func calculateMetadata(totalRecords, page, pageSize int) Metadata {
|
||||
if totalRecords == 0 {
|
||||
return Metadata{}
|
||||
}
|
||||
|
||||
return Metadata{
|
||||
CurrentPage: page,
|
||||
PageSize: pageSize,
|
||||
FirstPage: 1,
|
||||
LastPage: (totalRecords + pageSize - 1) / pageSize,
|
||||
TotalRecords: totalRecords,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,15 +194,16 @@ func (m MovieModel) Delete(id int64) error {
|
||||
}
|
||||
|
||||
// GetAll : 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) {
|
||||
// Construct the SQL query to retrieve all movie records.
|
||||
// to_tsvector('simple', title) transforms 'The Breakfast Club' into 'breakfast' 'club' 'the'. The 'simple' parameter's value is the configuration.
|
||||
// plainto_tsquery('simple', $1) takes a search value and turns it into a formatted query term that PostgreSQL full-text search can understand. As an example : "The Club" would result in the query term 'the' & 'club'
|
||||
// The @@ operator is the matches' operator. To continue the example, the query term 'the' & 'club' will match rows which contain both lexemes 'the' and 'club'.
|
||||
// Add an ORDER BY clause and interpolate the sort column and direction. Importantly, notice that we also include a secondary sort on the movie ID to ensure a consistent ordering.
|
||||
// Update the SQL query to include the LIMIT and OFFSET clauses with placeholder parameter values
|
||||
// Update the SQL query to include the window function which counts the total (filtered) records
|
||||
query := fmt.Sprintf(`
|
||||
SELECT id, created_at, title, year, runtime, genres, version
|
||||
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 = '{}')
|
||||
@@ -220,12 +221,13 @@ func (m MovieModel) GetAll(title string, genres []string, filters Filters) ([]*M
|
||||
// And then pass the args slice to QueryContext() as a variadic parameter
|
||||
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 the resultset is closed before GetAll() returns
|
||||
defer rows.Close()
|
||||
|
||||
totalRecords := 0
|
||||
// Initialize an empty slice to hold the movie data.
|
||||
movies := []*Movie{}
|
||||
|
||||
@@ -236,6 +238,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,
|
||||
@@ -245,7 +248,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.
|
||||
@@ -254,10 +257,14 @@ 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
|
||||
// Include the metadata struct when returning
|
||||
return movies, metadata, nil
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user