Compare commits
No commits in common. "34d9341e75dbdf36ebbf9258f82e5065fc38909c" and "2025bb451fbde55c50f235117674aaea1ba751ee" have entirely different histories.
34d9341e75
...
2025bb451f
9 changed files with 24 additions and 671 deletions
|
|
@ -1,6 +0,0 @@
|
||||||
package: oapi
|
|
||||||
generate:
|
|
||||||
strict-server: true
|
|
||||||
gin-server: true
|
|
||||||
models: true
|
|
||||||
output: api/api.gen.go
|
|
||||||
|
|
@ -1,436 +0,0 @@
|
||||||
openapi: 3.0.4
|
|
||||||
info:
|
|
||||||
title: 'Titles, Users, Reviews, Tags, and Media API'
|
|
||||||
version: 1.0.0
|
|
||||||
servers:
|
|
||||||
- url: /api/v1
|
|
||||||
paths:
|
|
||||||
/titles:
|
|
||||||
get:
|
|
||||||
summary: Get titles
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/cursor'
|
|
||||||
- $ref: '#/components/parameters/title_sort'
|
|
||||||
- in: query
|
|
||||||
name: sort_forward
|
|
||||||
schema:
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
- in: query
|
|
||||||
name: word
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: query
|
|
||||||
name: status
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/TitleStatus'
|
|
||||||
- in: query
|
|
||||||
name: rating
|
|
||||||
schema:
|
|
||||||
type: number
|
|
||||||
format: double
|
|
||||||
- in: query
|
|
||||||
name: release_year
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
- in: query
|
|
||||||
name: release_season
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/ReleaseSeason'
|
|
||||||
- in: query
|
|
||||||
name: limit
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
default: 10
|
|
||||||
- in: query
|
|
||||||
name: offset
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
default: 0
|
|
||||||
- in: query
|
|
||||||
name: fields
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
default: all
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: List of titles with cursor
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
data:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/Title'
|
|
||||||
description: List of titles
|
|
||||||
cursor:
|
|
||||||
$ref: '#/components/schemas/CursorObj'
|
|
||||||
required:
|
|
||||||
- data
|
|
||||||
- cursor
|
|
||||||
'204':
|
|
||||||
description: No titles found
|
|
||||||
'400':
|
|
||||||
description: Request params are not correct
|
|
||||||
'500':
|
|
||||||
description: Unknown server error
|
|
||||||
'/titles/{title_id}':
|
|
||||||
get:
|
|
||||||
summary: Get title description
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: title_id
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
- in: query
|
|
||||||
name: fields
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
default: all
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Title description
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/Title'
|
|
||||||
'204':
|
|
||||||
description: No title found
|
|
||||||
'400':
|
|
||||||
description: Request params are not correct
|
|
||||||
'404':
|
|
||||||
description: Title not found
|
|
||||||
'500':
|
|
||||||
description: Unknown server error
|
|
||||||
'/users/{user_id}':
|
|
||||||
get:
|
|
||||||
summary: Get user info
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: user_id
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: query
|
|
||||||
name: fields
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
default: all
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: User info
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/User'
|
|
||||||
'400':
|
|
||||||
description: Request params are not correct
|
|
||||||
'404':
|
|
||||||
description: User not found
|
|
||||||
'500':
|
|
||||||
description: Unknown server error
|
|
||||||
'/users/{user_id}/titles/':
|
|
||||||
get:
|
|
||||||
summary: Get user titles
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/cursor'
|
|
||||||
- in: path
|
|
||||||
name: user_id
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: query
|
|
||||||
name: word
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: query
|
|
||||||
name: status
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/TitleStatus'
|
|
||||||
- in: query
|
|
||||||
name: watch_status
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/UserTitleStatus'
|
|
||||||
- in: query
|
|
||||||
name: rating
|
|
||||||
schema:
|
|
||||||
type: number
|
|
||||||
format: double
|
|
||||||
- in: query
|
|
||||||
name: release_year
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
- in: query
|
|
||||||
name: release_season
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/ReleaseSeason'
|
|
||||||
- in: query
|
|
||||||
name: limit
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
default: 10
|
|
||||||
- in: query
|
|
||||||
name: fields
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
default: all
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: List of user titles
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/UserTitle'
|
|
||||||
'204':
|
|
||||||
description: No titles found
|
|
||||||
'400':
|
|
||||||
description: Request params are not correct
|
|
||||||
'500':
|
|
||||||
description: Unknown server error
|
|
||||||
components:
|
|
||||||
parameters:
|
|
||||||
cursor:
|
|
||||||
in: query
|
|
||||||
name: cursor
|
|
||||||
required: false
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
title_sort:
|
|
||||||
in: query
|
|
||||||
name: sort
|
|
||||||
required: false
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/TitleSort'
|
|
||||||
schemas:
|
|
||||||
CursorObj:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- id
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
param:
|
|
||||||
type: string
|
|
||||||
TitleSort:
|
|
||||||
type: string
|
|
||||||
description: Title sort order
|
|
||||||
default: id
|
|
||||||
enum:
|
|
||||||
- id
|
|
||||||
- year
|
|
||||||
- rating
|
|
||||||
- views
|
|
||||||
Image:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
storage_type:
|
|
||||||
type: string
|
|
||||||
image_path:
|
|
||||||
type: string
|
|
||||||
TitleStatus:
|
|
||||||
type: string
|
|
||||||
description: Title status
|
|
||||||
enum:
|
|
||||||
- finished
|
|
||||||
- ongoing
|
|
||||||
- planned
|
|
||||||
ReleaseSeason:
|
|
||||||
type: string
|
|
||||||
description: Title release season
|
|
||||||
enum:
|
|
||||||
- winter
|
|
||||||
- spring
|
|
||||||
- summer
|
|
||||||
- fall
|
|
||||||
UserTitleStatus:
|
|
||||||
type: string
|
|
||||||
description: User's title status
|
|
||||||
enum:
|
|
||||||
- finished
|
|
||||||
- planned
|
|
||||||
- dropped
|
|
||||||
- in-progress
|
|
||||||
Review:
|
|
||||||
type: object
|
|
||||||
additionalProperties: true
|
|
||||||
Tag:
|
|
||||||
type: object
|
|
||||||
description: 'A localized tag: keys are language codes (ISO 639-1), values are tag names'
|
|
||||||
additionalProperties:
|
|
||||||
type: string
|
|
||||||
example:
|
|
||||||
en: Shojo
|
|
||||||
ru: Сёдзё
|
|
||||||
ja: 少女
|
|
||||||
Tags:
|
|
||||||
type: array
|
|
||||||
description: Array of localized tags
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/Tag'
|
|
||||||
example:
|
|
||||||
- en: Shojo
|
|
||||||
ru: Сёдзё
|
|
||||||
ja: 少女
|
|
||||||
- en: Shounen
|
|
||||||
ru: Сёнен
|
|
||||||
ja: 少年
|
|
||||||
Studio:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- id
|
|
||||||
- name
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
poster:
|
|
||||||
$ref: '#/components/schemas/Image'
|
|
||||||
description:
|
|
||||||
type: string
|
|
||||||
Title:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- id
|
|
||||||
- title_names
|
|
||||||
- tags
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
description: Unique title ID (primary key)
|
|
||||||
example: 1
|
|
||||||
title_names:
|
|
||||||
type: object
|
|
||||||
description: 'Localized titles. Key = language (ISO 639-1), value = list of names'
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
example: Attack on Titan
|
|
||||||
minItems: 1
|
|
||||||
example:
|
|
||||||
- Attack on Titan
|
|
||||||
- AoT
|
|
||||||
example:
|
|
||||||
en:
|
|
||||||
- Attack on Titan
|
|
||||||
- AoT
|
|
||||||
ru:
|
|
||||||
- Атака титанов
|
|
||||||
- Титаны
|
|
||||||
ja:
|
|
||||||
- 進撃の巨人
|
|
||||||
studio:
|
|
||||||
$ref: '#/components/schemas/Studio'
|
|
||||||
tags:
|
|
||||||
$ref: '#/components/schemas/Tags'
|
|
||||||
poster:
|
|
||||||
$ref: '#/components/schemas/Image'
|
|
||||||
title_status:
|
|
||||||
$ref: '#/components/schemas/TitleStatus'
|
|
||||||
rating:
|
|
||||||
type: number
|
|
||||||
format: double
|
|
||||||
rating_count:
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
release_year:
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
release_season:
|
|
||||||
$ref: '#/components/schemas/ReleaseSeason'
|
|
||||||
episodes_aired:
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
episodes_all:
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
episodes_len:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: number
|
|
||||||
format: double
|
|
||||||
additionalProperties: true
|
|
||||||
User:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
description: Unique user ID (primary key)
|
|
||||||
example: 1
|
|
||||||
avatar_id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
description: ID of the user avatar (references images table)
|
|
||||||
example: null
|
|
||||||
mail:
|
|
||||||
type: string
|
|
||||||
format: email
|
|
||||||
description: User email
|
|
||||||
example: john.doe@example.com
|
|
||||||
nickname:
|
|
||||||
type: string
|
|
||||||
description: Username (alphanumeric + _ or -)
|
|
||||||
maxLength: 16
|
|
||||||
example: john_doe_42
|
|
||||||
disp_name:
|
|
||||||
type: string
|
|
||||||
description: Display name
|
|
||||||
maxLength: 32
|
|
||||||
example: John Doe
|
|
||||||
user_desc:
|
|
||||||
type: string
|
|
||||||
description: User description
|
|
||||||
maxLength: 512
|
|
||||||
example: Just a regular user.
|
|
||||||
creation_date:
|
|
||||||
type: string
|
|
||||||
format: date-time
|
|
||||||
description: Timestamp when the user was created
|
|
||||||
example: '2025-10-10T23:45:47.908073Z'
|
|
||||||
required:
|
|
||||||
- user_id
|
|
||||||
- nickname
|
|
||||||
UserTitle:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- user_id
|
|
||||||
- title_id
|
|
||||||
- status
|
|
||||||
properties:
|
|
||||||
user_id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
title_id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
status:
|
|
||||||
$ref: '#/components/schemas/UserTitleStatus'
|
|
||||||
rate:
|
|
||||||
type: integer
|
|
||||||
format: int32
|
|
||||||
review_id:
|
|
||||||
type: integer
|
|
||||||
format: int64
|
|
||||||
ctime:
|
|
||||||
type: string
|
|
||||||
format: date-time
|
|
||||||
additionalProperties: true
|
|
||||||
|
|
@ -47,12 +47,6 @@ const (
|
||||||
UserTitleStatusPlanned UserTitleStatus = "planned"
|
UserTitleStatusPlanned UserTitleStatus = "planned"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CursorObj defines model for CursorObj.
|
|
||||||
type CursorObj struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
Param *string `json:"param,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Image defines model for Image.
|
// Image defines model for Image.
|
||||||
type Image struct {
|
type Image struct {
|
||||||
Id *int64 `json:"id,omitempty"`
|
Id *int64 `json:"id,omitempty"`
|
||||||
|
|
@ -114,7 +108,7 @@ type TitleStatus string
|
||||||
// User defines model for User.
|
// User defines model for User.
|
||||||
type User struct {
|
type User struct {
|
||||||
// AvatarId ID of the user avatar (references images table)
|
// AvatarId ID of the user avatar (references images table)
|
||||||
AvatarId *int64 `json:"avatar_id,omitempty"`
|
AvatarId *int64 `json:"avatar_id"`
|
||||||
|
|
||||||
// CreationDate Timestamp when the user was created
|
// CreationDate Timestamp when the user was created
|
||||||
CreationDate *time.Time `json:"creation_date,omitempty"`
|
CreationDate *time.Time `json:"creation_date,omitempty"`
|
||||||
|
|
@ -912,12 +906,7 @@ type GetTitlesResponseObject interface {
|
||||||
VisitGetTitlesResponse(w http.ResponseWriter) error
|
VisitGetTitlesResponse(w http.ResponseWriter) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetTitles200JSONResponse struct {
|
type GetTitles200JSONResponse []Title
|
||||||
Cursor CursorObj `json:"cursor"`
|
|
||||||
|
|
||||||
// Data List of titles
|
|
||||||
Data []Title `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (response GetTitles200JSONResponse) VisitGetTitlesResponse(w http.ResponseWriter) error {
|
func (response GetTitles200JSONResponse) VisitGetTitlesResponse(w http.ResponseWriter) error {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
get:
|
get:
|
||||||
summary: Get titles
|
summary: Get titles
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: "../parameters/cursor.yaml"
|
- $ref: ../parameters/cursor.yaml
|
||||||
- $ref: "../parameters/title_sort.yaml"
|
- $ref: ../parameters/title_sort.yaml
|
||||||
- in: query
|
- in: query
|
||||||
name: sort_forward
|
name: sort_forward
|
||||||
schema:
|
schema:
|
||||||
|
|
|
||||||
|
|
@ -218,9 +218,6 @@ func (s Server) GetTitlesTitleId(ctx context.Context, request oapi.GetTitlesTitl
|
||||||
|
|
||||||
func (s Server) GetTitles(ctx context.Context, request oapi.GetTitlesRequestObject) (oapi.GetTitlesResponseObject, error) {
|
func (s Server) GetTitles(ctx context.Context, request oapi.GetTitlesRequestObject) (oapi.GetTitlesResponseObject, error) {
|
||||||
opai_titles := make([]oapi.Title, 0)
|
opai_titles := make([]oapi.Title, 0)
|
||||||
cursor := oapi.CursorObj{
|
|
||||||
Id: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
word := Word2Sqlc(request.Params.Word)
|
word := Word2Sqlc(request.Params.Word)
|
||||||
status, err := TitleStatus2Sqlc(request.Params.Status)
|
status, err := TitleStatus2Sqlc(request.Params.Status)
|
||||||
|
|
@ -240,6 +237,7 @@ func (s Server) GetTitles(ctx context.Context, request oapi.GetTitlesRequestObje
|
||||||
Rating: request.Params.Rating,
|
Rating: request.Params.Rating,
|
||||||
ReleaseYear: request.Params.ReleaseYear,
|
ReleaseYear: request.Params.ReleaseYear,
|
||||||
ReleaseSeason: season,
|
ReleaseSeason: season,
|
||||||
|
Offset: request.Params.Offset,
|
||||||
Limit: request.Params.Limit,
|
Limit: request.Params.Limit,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -260,5 +258,5 @@ func (s Server) GetTitles(ctx context.Context, request oapi.GetTitlesRequestObje
|
||||||
opai_titles = append(opai_titles, t)
|
opai_titles = append(opai_titles, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
return oapi.GetTitles200JSONResponse{Cursor: cursor, Data: opai_titles}, nil
|
return oapi.GetTitles200JSONResponse(opai_titles), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,67 +107,9 @@ WHERE
|
||||||
AND (sqlc.narg('rating')::float IS NULL OR rating >= sqlc.narg('rating')::float)
|
AND (sqlc.narg('rating')::float IS NULL OR rating >= sqlc.narg('rating')::float)
|
||||||
AND (sqlc.narg('release_year')::int IS NULL OR release_year = sqlc.narg('release_year')::int)
|
AND (sqlc.narg('release_year')::int IS NULL OR release_year = sqlc.narg('release_year')::int)
|
||||||
AND (sqlc.narg('release_season')::release_season_t IS NULL OR release_season = sqlc.narg('release_season')::release_season_t)
|
AND (sqlc.narg('release_season')::release_season_t IS NULL OR release_season = sqlc.narg('release_season')::release_season_t)
|
||||||
ORDER BY
|
|
||||||
-- Основной ключ: выбранное поле
|
|
||||||
CASE
|
|
||||||
WHEN sqlc.arg(forward)::boolean AND sqlc.arg(sort_by)::text = 'id' THEN id
|
|
||||||
WHEN sqlc.arg(forward)::boolean AND sqlc.arg(sort_by)::text = 'name' THEN name
|
|
||||||
WHEN sqlc.arg(forward)::boolean AND sqlc.arg(sort_by)::text = 'year' THEN release_year
|
|
||||||
WHEN sqlc.arg(forward)::boolean AND sqlc.arg(sort_by)::text = 'rating' THEN rating
|
|
||||||
WHEN sqlc.arg(forward)::boolean AND sqlc.arg(sort_by)::text = 'views' THEN views
|
|
||||||
END ASC,
|
|
||||||
CASE
|
|
||||||
WHEN NOT sqlc.arg(forward)::boolean AND sqlc.arg(sort_by)::text = 'id' THEN id
|
|
||||||
WHEN NOT sqlc.arg(forward)::boolean AND sqlc.arg(sort_by)::text = 'name' THEN name
|
|
||||||
WHEN NOT sqlc.arg(forward)::boolean AND sqlc.arg(sort_by)::text = 'year' THEN release_year
|
|
||||||
WHEN NOT sqlc.arg(forward)::boolean AND sqlc.arg(sort_by)::text = 'rating' THEN rating
|
|
||||||
WHEN NOT sqlc.arg(forward)::boolean AND sqlc.arg(sort_by)::text = 'views' THEN views
|
|
||||||
END DESC,
|
|
||||||
|
|
||||||
-- Вторичный ключ: id — только если НЕ сортируем по id
|
LIMIT COALESCE(sqlc.narg('limit')::int, 100) -- 100 is default limit
|
||||||
CASE
|
OFFSET sqlc.narg('offset')::int;
|
||||||
WHEN sqlc.arg(sort_by)::text != 'id' AND sqlc.arg(forward)::boolean THEN id
|
|
||||||
END ASC,
|
|
||||||
CASE
|
|
||||||
WHEN sqlc.arg(sort_by)::text != 'id' AND NOT sqlc.arg(forward)::boolean THEN id
|
|
||||||
END DESC
|
|
||||||
LIMIT COALESCE(sqlc.narg('limit')::int, 100); -- 100 is default limit
|
|
||||||
-- OFFSET sqlc.narg('offset')::int;
|
|
||||||
|
|
||||||
-- name: SearchUserTitles :many
|
|
||||||
SELECT
|
|
||||||
*
|
|
||||||
FROM usertitles as u
|
|
||||||
JOIN titles as t ON (u.title_id = t.id)
|
|
||||||
WHERE
|
|
||||||
CASE
|
|
||||||
WHEN sqlc.narg('word')::text IS NOT NULL THEN
|
|
||||||
(
|
|
||||||
SELECT bool_and(
|
|
||||||
EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM jsonb_each_text(t.title_names) AS t(key, val)
|
|
||||||
WHERE val ILIKE pattern
|
|
||||||
)
|
|
||||||
)
|
|
||||||
FROM unnest(
|
|
||||||
ARRAY(
|
|
||||||
SELECT '%' || trim(w) || '%'
|
|
||||||
FROM unnest(string_to_array(sqlc.narg('word')::text, ' ')) AS w
|
|
||||||
WHERE trim(w) <> ''
|
|
||||||
)
|
|
||||||
) AS pattern
|
|
||||||
)
|
|
||||||
ELSE true
|
|
||||||
END
|
|
||||||
|
|
||||||
AND (sqlc.narg('status')::title_status_t IS NULL OR t.title_status = sqlc.narg('status')::title_status_t)
|
|
||||||
AND (sqlc.narg('rating')::float IS NULL OR t.rating >= sqlc.narg('rating')::float)
|
|
||||||
AND (sqlc.narg('release_year')::int IS NULL OR t.release_year = sqlc.narg('release_year')::int)
|
|
||||||
AND (sqlc.narg('release_season')::release_season_t IS NULL OR t.release_season = sqlc.narg('release_season')::release_season_t)
|
|
||||||
AND (sqlc.narg('usertitle_status')::usertitle_status_t IS NULL OR u.usertitle_status = sqlc.narg('usertitle_status')::usertitle_status_t)
|
|
||||||
|
|
||||||
LIMIT COALESCE(sqlc.narg('limit')::int, 100); -- 100 is default limit
|
|
||||||
|
|
||||||
-- -- name: ListTitles :many
|
-- -- name: ListTitles :many
|
||||||
-- SELECT title_id, title_names, studio_id, poster_id, signal_ids,
|
-- SELECT title_id, title_names, studio_id, poster_id, signal_ids,
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ CREATE TABLE studios (
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE titles (
|
CREATE TABLE titles (
|
||||||
-- // TODO: anime type (film, season etc)
|
// TODO: anime type (film, season etc)
|
||||||
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||||
-- example {"ru": ["Атака титанов", "Атака Титанов"],"en": ["Attack on Titan", "AoT"],"ja": ["進撃の巨人", "しんげきのきょじん"]}
|
-- example {"ru": ["Атака титанов", "Атака Титанов"],"en": ["Attack on Titan", "AoT"],"ja": ["進撃の巨人", "しんげきのきょじん"]}
|
||||||
title_names jsonb NOT NULL,
|
title_names jsonb NOT NULL,
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,7 @@ type Review struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Data string `json:"data"`
|
Data string `json:"data"`
|
||||||
Rating *int32 `json:"rating"`
|
Rating *int32 `json:"rating"`
|
||||||
|
IllustID *int64 `json:"illust_id"`
|
||||||
UserID *int64 `json:"user_id"`
|
UserID *int64 `json:"user_id"`
|
||||||
TitleID *int64 `json:"title_id"`
|
TitleID *int64 `json:"title_id"`
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
|
|
@ -276,10 +277,10 @@ type User struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Usertitle struct {
|
type Usertitle struct {
|
||||||
UserID int64 `json:"user_id"`
|
UserID int64 `json:"user_id"`
|
||||||
TitleID int64 `json:"title_id"`
|
TitleID int64 `json:"title_id"`
|
||||||
Status UsertitleStatusT `json:"status"`
|
Status UsertitleStatusT `json:"status"`
|
||||||
Rate *int32 `json:"rate"`
|
Rate *int32 `json:"rate"`
|
||||||
ReviewID *int64 `json:"review_id"`
|
ReviewText *string `json:"review_text"`
|
||||||
Ctime pgtype.Timestamptz `json:"ctime"`
|
ReviewDate pgtype.Timestamptz `json:"review_date"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ package sqlc
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const createImage = `-- name: CreateImage :one
|
const createImage = `-- name: CreateImage :one
|
||||||
|
|
@ -33,7 +31,7 @@ func (q *Queries) CreateImage(ctx context.Context, arg CreateImageParams) (Image
|
||||||
const getImageByID = `-- name: GetImageByID :one
|
const getImageByID = `-- name: GetImageByID :one
|
||||||
SELECT id, storage_type, image_path
|
SELECT id, storage_type, image_path
|
||||||
FROM images
|
FROM images
|
||||||
WHERE id = $1::bigint
|
WHERE id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetImageByID(ctx context.Context, illustID int64) (Image, error) {
|
func (q *Queries) GetImageByID(ctx context.Context, illustID int64) (Image, error) {
|
||||||
|
|
@ -46,13 +44,11 @@ func (q *Queries) GetImageByID(ctx context.Context, illustID int64) (Image, erro
|
||||||
const getReviewByID = `-- name: GetReviewByID :one
|
const getReviewByID = `-- name: GetReviewByID :one
|
||||||
|
|
||||||
|
|
||||||
|
SELECT id, data, rating, illust_id, user_id, title_id, created_at
|
||||||
SELECT id, data, rating, user_id, title_id, created_at
|
|
||||||
FROM reviews
|
FROM reviews
|
||||||
WHERE review_id = $1::bigint
|
WHERE review_id = $1::bigint
|
||||||
`
|
`
|
||||||
|
|
||||||
// 100 is default limit
|
|
||||||
// -- name: ListTitles :many
|
// -- name: ListTitles :many
|
||||||
// SELECT title_id, title_names, studio_id, poster_id, signal_ids,
|
// SELECT title_id, title_names, studio_id, poster_id, signal_ids,
|
||||||
//
|
//
|
||||||
|
|
@ -86,6 +82,7 @@ func (q *Queries) GetReviewByID(ctx context.Context, reviewID int64) (Review, er
|
||||||
&i.ID,
|
&i.ID,
|
||||||
&i.Data,
|
&i.Data,
|
||||||
&i.Rating,
|
&i.Rating,
|
||||||
|
&i.IllustID,
|
||||||
&i.UserID,
|
&i.UserID,
|
||||||
&i.TitleID,
|
&i.TitleID,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
|
|
@ -315,18 +312,9 @@ WHERE
|
||||||
AND ($3::float IS NULL OR rating >= $3::float)
|
AND ($3::float IS NULL OR rating >= $3::float)
|
||||||
AND ($4::int IS NULL OR release_year = $4::int)
|
AND ($4::int IS NULL OR release_year = $4::int)
|
||||||
AND ($5::release_season_t IS NULL OR release_season = $5::release_season_t)
|
AND ($5::release_season_t IS NULL OR release_season = $5::release_season_t)
|
||||||
ORDER BY CASE
|
|
||||||
WHEN $6::boolean AND $7::text = 'name' THEN name
|
LIMIT COALESCE($7::int, 100) -- 100 is default limit
|
||||||
WHEN $8::boolean AND $7::text = 'id' THEN id
|
OFFSET $6::int
|
||||||
WHEN $8::boolean AND $7::text = 'name' THEN name
|
|
||||||
WHEN $8::boolean AND $7::text = 'id' THEN id
|
|
||||||
END ASC, CASE
|
|
||||||
WHEN NOT $8::boolean AND $7::text = 'name' THEN name
|
|
||||||
WHEN NOT $8::boolean AND $7::text = 'id' THEN id
|
|
||||||
WHEN NOT $8::boolean AND $7::text = 'name' THEN name
|
|
||||||
WHEN NOT $8::boolean AND $7::text = 'id' THEN id
|
|
||||||
END DESC
|
|
||||||
LIMIT COALESCE($9::int, 100)
|
|
||||||
`
|
`
|
||||||
|
|
||||||
type SearchTitlesParams struct {
|
type SearchTitlesParams struct {
|
||||||
|
|
@ -335,9 +323,7 @@ type SearchTitlesParams struct {
|
||||||
Rating *float64 `json:"rating"`
|
Rating *float64 `json:"rating"`
|
||||||
ReleaseYear *int32 `json:"release_year"`
|
ReleaseYear *int32 `json:"release_year"`
|
||||||
ReleaseSeason *ReleaseSeasonT `json:"release_season"`
|
ReleaseSeason *ReleaseSeasonT `json:"release_season"`
|
||||||
Forward bool `json:"forward"`
|
Offset *int32 `json:"offset"`
|
||||||
OrderBy string `json:"order_by"`
|
|
||||||
Reverse bool `json:"reverse"`
|
|
||||||
Limit *int32 `json:"limit"`
|
Limit *int32 `json:"limit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -348,9 +334,7 @@ func (q *Queries) SearchTitles(ctx context.Context, arg SearchTitlesParams) ([]T
|
||||||
arg.Rating,
|
arg.Rating,
|
||||||
arg.ReleaseYear,
|
arg.ReleaseYear,
|
||||||
arg.ReleaseSeason,
|
arg.ReleaseSeason,
|
||||||
arg.Forward,
|
arg.Offset,
|
||||||
arg.OrderBy,
|
|
||||||
arg.Reverse,
|
|
||||||
arg.Limit,
|
arg.Limit,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -384,122 +368,3 @@ func (q *Queries) SearchTitles(ctx context.Context, arg SearchTitlesParams) ([]T
|
||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchUserTitles = `-- name: SearchUserTitles :many
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
user_id, title_id, status, rate, review_id, ctime, id, title_names, studio_id, poster_id, title_status, rating, rating_count, release_year, release_season, season, episodes_aired, episodes_all, episodes_len
|
|
||||||
FROM usertitles as u
|
|
||||||
JOIN titles as t ON (u.title_id = t.id)
|
|
||||||
WHERE
|
|
||||||
CASE
|
|
||||||
WHEN $1::text IS NOT NULL THEN
|
|
||||||
(
|
|
||||||
SELECT bool_and(
|
|
||||||
EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM jsonb_each_text(t.title_names) AS t(key, val)
|
|
||||||
WHERE val ILIKE pattern
|
|
||||||
)
|
|
||||||
)
|
|
||||||
FROM unnest(
|
|
||||||
ARRAY(
|
|
||||||
SELECT '%' || trim(w) || '%'
|
|
||||||
FROM unnest(string_to_array($1::text, ' ')) AS w
|
|
||||||
WHERE trim(w) <> ''
|
|
||||||
)
|
|
||||||
) AS pattern
|
|
||||||
)
|
|
||||||
ELSE true
|
|
||||||
END
|
|
||||||
|
|
||||||
AND ($2::title_status_t IS NULL OR t.title_status = $2::title_status_t)
|
|
||||||
AND ($3::float IS NULL OR t.rating >= $3::float)
|
|
||||||
AND ($4::int IS NULL OR t.release_year = $4::int)
|
|
||||||
AND ($5::release_season_t IS NULL OR t.release_season = $5::release_season_t)
|
|
||||||
AND ($6::usertitle_status_t IS NULL OR u.usertitle_status = $6::usertitle_status_t)
|
|
||||||
|
|
||||||
LIMIT COALESCE($7::int, 100)
|
|
||||||
`
|
|
||||||
|
|
||||||
type SearchUserTitlesParams struct {
|
|
||||||
Word *string `json:"word"`
|
|
||||||
Status *TitleStatusT `json:"status"`
|
|
||||||
Rating *float64 `json:"rating"`
|
|
||||||
ReleaseYear *int32 `json:"release_year"`
|
|
||||||
ReleaseSeason *ReleaseSeasonT `json:"release_season"`
|
|
||||||
UsertitleStatus NullUsertitleStatusT `json:"usertitle_status"`
|
|
||||||
Limit *int32 `json:"limit"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SearchUserTitlesRow struct {
|
|
||||||
UserID int64 `json:"user_id"`
|
|
||||||
TitleID int64 `json:"title_id"`
|
|
||||||
Status UsertitleStatusT `json:"status"`
|
|
||||||
Rate *int32 `json:"rate"`
|
|
||||||
ReviewID *int64 `json:"review_id"`
|
|
||||||
Ctime pgtype.Timestamptz `json:"ctime"`
|
|
||||||
ID int64 `json:"id"`
|
|
||||||
TitleNames []byte `json:"title_names"`
|
|
||||||
StudioID int64 `json:"studio_id"`
|
|
||||||
PosterID *int64 `json:"poster_id"`
|
|
||||||
TitleStatus TitleStatusT `json:"title_status"`
|
|
||||||
Rating *float64 `json:"rating"`
|
|
||||||
RatingCount *int32 `json:"rating_count"`
|
|
||||||
ReleaseYear *int32 `json:"release_year"`
|
|
||||||
ReleaseSeason *ReleaseSeasonT `json:"release_season"`
|
|
||||||
Season *int32 `json:"season"`
|
|
||||||
EpisodesAired *int32 `json:"episodes_aired"`
|
|
||||||
EpisodesAll *int32 `json:"episodes_all"`
|
|
||||||
EpisodesLen []byte `json:"episodes_len"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// 100 is default limit
|
|
||||||
// OFFSET sqlc.narg('offset')::int;
|
|
||||||
func (q *Queries) SearchUserTitles(ctx context.Context, arg SearchUserTitlesParams) ([]SearchUserTitlesRow, error) {
|
|
||||||
rows, err := q.db.Query(ctx, searchUserTitles,
|
|
||||||
arg.Word,
|
|
||||||
arg.Status,
|
|
||||||
arg.Rating,
|
|
||||||
arg.ReleaseYear,
|
|
||||||
arg.ReleaseSeason,
|
|
||||||
arg.UsertitleStatus,
|
|
||||||
arg.Limit,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
var items []SearchUserTitlesRow
|
|
||||||
for rows.Next() {
|
|
||||||
var i SearchUserTitlesRow
|
|
||||||
if err := rows.Scan(
|
|
||||||
&i.UserID,
|
|
||||||
&i.TitleID,
|
|
||||||
&i.Status,
|
|
||||||
&i.Rate,
|
|
||||||
&i.ReviewID,
|
|
||||||
&i.Ctime,
|
|
||||||
&i.ID,
|
|
||||||
&i.TitleNames,
|
|
||||||
&i.StudioID,
|
|
||||||
&i.PosterID,
|
|
||||||
&i.TitleStatus,
|
|
||||||
&i.Rating,
|
|
||||||
&i.RatingCount,
|
|
||||||
&i.ReleaseYear,
|
|
||||||
&i.ReleaseSeason,
|
|
||||||
&i.Season,
|
|
||||||
&i.EpisodesAired,
|
|
||||||
&i.EpisodesAll,
|
|
||||||
&i.EpisodesLen,
|
|
||||||
); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
items = append(items, i)
|
|
||||||
}
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return items, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue