gen: regenerate database code with new search queries
Run sqlc generate to create Go code for new search queries: Added methods to Querier interface: - SearchMediaItemsUnified - Main unified search with fuzzy/exact matching - SearchAuthorValues - Author field autocomplete - SearchGenreValues - Genre field autocomplete - SearchSeriesValues - Series field autocomplete - SearchLanguageValues - Language field autocomplete Generated parameter structs and row types for all new queries. All queries include proper library visibility checks.
This commit is contained in:
@@ -284,9 +284,14 @@ type Querier interface {
|
|||||||
// Revoke OPDS token
|
// Revoke OPDS token
|
||||||
RevokeOpdsToken(ctx context.Context, token string) error
|
RevokeOpdsToken(ctx context.Context, token string) error
|
||||||
RevokeRefreshToken(ctx context.Context, token pgtype.UUID) error
|
RevokeRefreshToken(ctx context.Context, token pgtype.UUID) error
|
||||||
|
SearchAuthorValues(ctx context.Context, arg SearchAuthorValuesParams) ([]SearchAuthorValuesRow, error)
|
||||||
|
SearchGenreValues(ctx context.Context, arg SearchGenreValuesParams) ([]SearchGenreValuesRow, error)
|
||||||
|
SearchLanguageValues(ctx context.Context, arg SearchLanguageValuesParams) ([]SearchLanguageValuesRow, error)
|
||||||
// Search Media Items queries
|
// Search Media Items queries
|
||||||
SearchMediaItems(ctx context.Context, arg SearchMediaItemsParams) ([]SearchMediaItemsRow, error)
|
SearchMediaItems(ctx context.Context, arg SearchMediaItemsParams) ([]SearchMediaItemsRow, error)
|
||||||
SearchMediaItemsFuzzy(ctx context.Context, arg SearchMediaItemsFuzzyParams) ([]SearchMediaItemsFuzzyRow, error)
|
SearchMediaItemsFuzzy(ctx context.Context, arg SearchMediaItemsFuzzyParams) ([]SearchMediaItemsFuzzyRow, error)
|
||||||
|
SearchMediaItemsUnified(ctx context.Context, arg SearchMediaItemsUnifiedParams) ([]SearchMediaItemsUnifiedRow, error)
|
||||||
|
SearchSeriesValues(ctx context.Context, arg SearchSeriesValuesParams) ([]SearchSeriesValuesRow, error)
|
||||||
// Library Visibility queries
|
// Library Visibility queries
|
||||||
SetLibraryVisibility(ctx context.Context, arg SetLibraryVisibilityParams) (LibraryVisibility, error)
|
SetLibraryVisibility(ctx context.Context, arg SetLibraryVisibilityParams) (LibraryVisibility, error)
|
||||||
// Set system config
|
// Set system config
|
||||||
|
|||||||
@@ -7047,6 +7047,180 @@ func (q *Queries) RevokeRefreshToken(ctx context.Context, token pgtype.UUID) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SearchAuthorValues = `-- name: SearchAuthorValues :many
|
||||||
|
SELECT
|
||||||
|
mi.author as value,
|
||||||
|
COUNT(*) as count,
|
||||||
|
word_similarity($1, COALESCE(mi.author, ''))::float8 as score
|
||||||
|
FROM media_items mi
|
||||||
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
||||||
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND mi.library_id = $3
|
||||||
|
AND word_similarity($1, COALESCE(mi.author, '')) > 0.3
|
||||||
|
AND mi.author IS NOT NULL
|
||||||
|
AND mi.author != ''
|
||||||
|
GROUP BY mi.author, word_similarity($1, COALESCE(mi.author, ''))::float8
|
||||||
|
ORDER BY score DESC, count DESC
|
||||||
|
LIMIT $5 OFFSET $4
|
||||||
|
`
|
||||||
|
|
||||||
|
type SearchAuthorValuesParams struct {
|
||||||
|
SearchQuery interface{} `db:"search_query" json:"search_query"`
|
||||||
|
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||||
|
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||||
|
Offset pgtype.Int4 `db:"offset" json:"offset"`
|
||||||
|
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchAuthorValuesRow struct {
|
||||||
|
Value pgtype.Text `db:"value" json:"value"`
|
||||||
|
Count int64 `db:"count" json:"count"`
|
||||||
|
Score float64 `db:"score" json:"score"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) SearchAuthorValues(ctx context.Context, arg SearchAuthorValuesParams) ([]SearchAuthorValuesRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, SearchAuthorValues,
|
||||||
|
arg.SearchQuery,
|
||||||
|
arg.UserID,
|
||||||
|
arg.LibraryID,
|
||||||
|
arg.Offset,
|
||||||
|
arg.Limit,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []SearchAuthorValuesRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i SearchAuthorValuesRow
|
||||||
|
if err := rows.Scan(&i.Value, &i.Count, &i.Score); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const SearchGenreValues = `-- name: SearchGenreValues :many
|
||||||
|
SELECT
|
||||||
|
mi.genre as value,
|
||||||
|
COUNT(*) as count,
|
||||||
|
word_similarity($1, COALESCE(mi.genre, ''))::float8 as score
|
||||||
|
FROM media_items mi
|
||||||
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
||||||
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND mi.library_id = $3
|
||||||
|
AND word_similarity($1, COALESCE(mi.genre, '')) > 0.3
|
||||||
|
AND mi.genre IS NOT NULL
|
||||||
|
AND mi.genre != ''
|
||||||
|
GROUP BY mi.genre, word_similarity($1, COALESCE(mi.genre, ''))::float8
|
||||||
|
ORDER BY score DESC, count DESC
|
||||||
|
LIMIT $5 OFFSET $4
|
||||||
|
`
|
||||||
|
|
||||||
|
type SearchGenreValuesParams struct {
|
||||||
|
SearchQuery interface{} `db:"search_query" json:"search_query"`
|
||||||
|
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||||
|
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||||
|
Offset pgtype.Int4 `db:"offset" json:"offset"`
|
||||||
|
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchGenreValuesRow struct {
|
||||||
|
Value pgtype.Text `db:"value" json:"value"`
|
||||||
|
Count int64 `db:"count" json:"count"`
|
||||||
|
Score float64 `db:"score" json:"score"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) SearchGenreValues(ctx context.Context, arg SearchGenreValuesParams) ([]SearchGenreValuesRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, SearchGenreValues,
|
||||||
|
arg.SearchQuery,
|
||||||
|
arg.UserID,
|
||||||
|
arg.LibraryID,
|
||||||
|
arg.Offset,
|
||||||
|
arg.Limit,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []SearchGenreValuesRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i SearchGenreValuesRow
|
||||||
|
if err := rows.Scan(&i.Value, &i.Count, &i.Score); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const SearchLanguageValues = `-- name: SearchLanguageValues :many
|
||||||
|
SELECT
|
||||||
|
mi.language as value,
|
||||||
|
COUNT(*) as count,
|
||||||
|
word_similarity($1, COALESCE(mi.language, ''))::float8 as score
|
||||||
|
FROM media_items mi
|
||||||
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
||||||
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND mi.library_id = $3
|
||||||
|
AND word_similarity($1, COALESCE(mi.language, '')) > 0.3
|
||||||
|
AND mi.language IS NOT NULL
|
||||||
|
AND mi.language != ''
|
||||||
|
GROUP BY mi.language, word_similarity($1, COALESCE(mi.language, ''))::float8
|
||||||
|
ORDER BY score DESC, count DESC
|
||||||
|
LIMIT $5 OFFSET $4
|
||||||
|
`
|
||||||
|
|
||||||
|
type SearchLanguageValuesParams struct {
|
||||||
|
SearchQuery interface{} `db:"search_query" json:"search_query"`
|
||||||
|
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||||
|
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||||
|
Offset pgtype.Int4 `db:"offset" json:"offset"`
|
||||||
|
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchLanguageValuesRow struct {
|
||||||
|
Value pgtype.Text `db:"value" json:"value"`
|
||||||
|
Count int64 `db:"count" json:"count"`
|
||||||
|
Score float64 `db:"score" json:"score"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) SearchLanguageValues(ctx context.Context, arg SearchLanguageValuesParams) ([]SearchLanguageValuesRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, SearchLanguageValues,
|
||||||
|
arg.SearchQuery,
|
||||||
|
arg.UserID,
|
||||||
|
arg.LibraryID,
|
||||||
|
arg.Offset,
|
||||||
|
arg.Limit,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []SearchLanguageValuesRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i SearchLanguageValuesRow
|
||||||
|
if err := rows.Scan(&i.Value, &i.Count, &i.Score); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const SearchMediaItems = `-- name: SearchMediaItems :many
|
const SearchMediaItems = `-- name: SearchMediaItems :many
|
||||||
SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, l.name as library_name, lt.name as library_type_name
|
SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, l.name as library_name, lt.name as library_type_name
|
||||||
FROM media_items mi
|
FROM media_items mi
|
||||||
@@ -7378,6 +7552,280 @@ func (q *Queries) SearchMediaItemsFuzzy(ctx context.Context, arg SearchMediaItem
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SearchMediaItemsUnified = `-- name: SearchMediaItemsUnified :many
|
||||||
|
SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, l.name as library_name, lt.name as library_type_name
|
||||||
|
FROM media_items mi
|
||||||
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
|
JOIN library_types lt ON l.library_type_id = lt.id
|
||||||
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
|
||||||
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND mi.library_id = $2
|
||||||
|
-- Fuzzy author filter
|
||||||
|
AND ($3 = '' OR word_similarity($3, COALESCE(mi.author, '')) > 0.3)
|
||||||
|
-- Fuzzy series filter
|
||||||
|
AND ($4 = '' OR word_similarity($4, COALESCE(mi.series, '')) > 0.3)
|
||||||
|
-- Fuzzy genre filter
|
||||||
|
AND ($5 = '' OR word_similarity($5, COALESCE(mi.genre, '')) > 0.3)
|
||||||
|
-- Fuzzy language filter
|
||||||
|
AND ($6 = '' OR word_similarity($6, COALESCE(mi.language, '')) > 0.3)
|
||||||
|
-- Year range (exact)
|
||||||
|
AND ($7 = 0 OR mi.copyright_year >= $7)
|
||||||
|
AND ($8 = 0 OR mi.copyright_year <= $8)
|
||||||
|
-- Boolean (exact)
|
||||||
|
AND ($9 = false OR mi.cover_image_path IS NOT NULL)
|
||||||
|
-- Search query (fuzzy or exact based on quotes)
|
||||||
|
AND (
|
||||||
|
$10 = '' OR
|
||||||
|
-- Fuzzy search (default)
|
||||||
|
$11 = false AND (
|
||||||
|
word_similarity($10, mi.title) > 0.3 OR
|
||||||
|
word_similarity($10, COALESCE(mi.author, '')) > 0.3 OR
|
||||||
|
word_similarity($10, COALESCE(mi.series, '')) > 0.3 OR
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1 FROM unnest(mi.tags_search) AS tag
|
||||||
|
WHERE word_similarity($10, tag) > 0.3
|
||||||
|
LIMIT 1
|
||||||
|
) OR
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1 FROM unnest(mi.contributors_search) AS contributor
|
||||||
|
WHERE word_similarity($10, contributor) > 0.3
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
) OR
|
||||||
|
-- Exact search (with quotes)
|
||||||
|
$11 = true AND (
|
||||||
|
mi.title ILIKE $12 OR
|
||||||
|
mi.author ILIKE $12 OR
|
||||||
|
mi.series ILIKE $12 OR
|
||||||
|
$12 = ANY(mi.tags_search) OR
|
||||||
|
$12 = ANY(mi.contributors_search)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN $10 != '' THEN
|
||||||
|
GREATEST(
|
||||||
|
CASE WHEN $11 = false THEN
|
||||||
|
word_similarity($10, mi.title)
|
||||||
|
ELSE 0 END,
|
||||||
|
CASE WHEN $11 = false THEN
|
||||||
|
word_similarity($10, COALESCE(mi.author, ''))
|
||||||
|
ELSE 0 END,
|
||||||
|
word_similarity($3, COALESCE(mi.author, '')),
|
||||||
|
word_similarity($5, COALESCE(mi.genre, ''))
|
||||||
|
)
|
||||||
|
ELSE 0
|
||||||
|
END DESC,
|
||||||
|
mi.title ASC
|
||||||
|
LIMIT $14 OFFSET $13
|
||||||
|
`
|
||||||
|
|
||||||
|
type SearchMediaItemsUnifiedParams struct {
|
||||||
|
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||||
|
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||||
|
AuthorFilter interface{} `db:"author_filter" json:"author_filter"`
|
||||||
|
SeriesFilter interface{} `db:"series_filter" json:"series_filter"`
|
||||||
|
GenreFilter interface{} `db:"genre_filter" json:"genre_filter"`
|
||||||
|
LanguageFilter interface{} `db:"language_filter" json:"language_filter"`
|
||||||
|
YearMin interface{} `db:"year_min" json:"year_min"`
|
||||||
|
YearMax interface{} `db:"year_max" json:"year_max"`
|
||||||
|
HasCover interface{} `db:"has_cover" json:"has_cover"`
|
||||||
|
SearchQuery interface{} `db:"search_query" json:"search_query"`
|
||||||
|
IsExactSearch interface{} `db:"is_exact_search" json:"is_exact_search"`
|
||||||
|
SearchPattern pgtype.Text `db:"search_pattern" json:"search_pattern"`
|
||||||
|
Offset pgtype.Int4 `db:"offset" json:"offset"`
|
||||||
|
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchMediaItemsUnifiedRow struct {
|
||||||
|
ID pgtype.UUID `db:"id" json:"id"`
|
||||||
|
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||||
|
Title string `db:"title" json:"title"`
|
||||||
|
Author pgtype.Text `db:"author" json:"author"`
|
||||||
|
Isbn pgtype.Text `db:"isbn" json:"isbn"`
|
||||||
|
Description pgtype.Text `db:"description" json:"description"`
|
||||||
|
FilePath string `db:"file_path" json:"file_path"`
|
||||||
|
FileSize pgtype.Int8 `db:"file_size" json:"file_size"`
|
||||||
|
MimeType pgtype.Text `db:"mime_type" json:"mime_type"`
|
||||||
|
CoverImagePath pgtype.Text `db:"cover_image_path" json:"cover_image_path"`
|
||||||
|
Series pgtype.Text `db:"series" json:"series"`
|
||||||
|
SeriesNumber pgtype.Int4 `db:"series_number" json:"series_number"`
|
||||||
|
Tags []string `db:"tags" json:"tags"`
|
||||||
|
Asin pgtype.Text `db:"asin" json:"asin"`
|
||||||
|
DatePublished pgtype.Date `db:"date_published" json:"date_published"`
|
||||||
|
Publisher pgtype.Text `db:"publisher" json:"publisher"`
|
||||||
|
Contributors []string `db:"contributors" json:"contributors"`
|
||||||
|
Language pgtype.Text `db:"language" json:"language"`
|
||||||
|
Edition pgtype.Text `db:"edition" json:"edition"`
|
||||||
|
PageCount pgtype.Int4 `db:"page_count" json:"page_count"`
|
||||||
|
Genre pgtype.Text `db:"genre" json:"genre"`
|
||||||
|
CopyrightYear pgtype.Int4 `db:"copyright_year" json:"copyright_year"`
|
||||||
|
GoodreadsID pgtype.Text `db:"goodreads_id" json:"goodreads_id"`
|
||||||
|
OpenlibraryID pgtype.Text `db:"openlibrary_id" json:"openlibrary_id"`
|
||||||
|
GoogleBooksID pgtype.Text `db:"google_books_id" json:"google_books_id"`
|
||||||
|
AddedByAdminID pgtype.UUID `db:"added_by_admin_id" json:"added_by_admin_id"`
|
||||||
|
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
|
||||||
|
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
|
||||||
|
FormatGroup string `db:"format_group" json:"format_group"`
|
||||||
|
FormatMimetype pgtype.Text `db:"format_mimetype" json:"format_mimetype"`
|
||||||
|
IsReflowable pgtype.Bool `db:"is_reflowable" json:"is_reflowable"`
|
||||||
|
HasFixedLayout pgtype.Bool `db:"has_fixed_layout" json:"has_fixed_layout"`
|
||||||
|
TotalCharacters pgtype.Int8 `db:"total_characters" json:"total_characters"`
|
||||||
|
ChapterCount pgtype.Int4 `db:"chapter_count" json:"chapter_count"`
|
||||||
|
EntitlementID pgtype.Text `db:"entitlement_id" json:"entitlement_id"`
|
||||||
|
RevisionNumber pgtype.Int4 `db:"revision_number" json:"revision_number"`
|
||||||
|
KoboContentID pgtype.Text `db:"kobo_content_id" json:"kobo_content_id"`
|
||||||
|
KoboMetadata []byte `db:"kobo_metadata" json:"kobo_metadata"`
|
||||||
|
TagsSearch []string `db:"tags_search" json:"tags_search"`
|
||||||
|
ContributorsSearch []string `db:"contributors_search" json:"contributors_search"`
|
||||||
|
FileSha256 pgtype.Text `db:"file_sha256" json:"file_sha256"`
|
||||||
|
OpfIdentifier pgtype.Text `db:"opf_identifier" json:"opf_identifier"`
|
||||||
|
OpfUuid pgtype.Text `db:"opf_uuid" json:"opf_uuid"`
|
||||||
|
HashConfidence pgtype.Text `db:"hash_confidence" json:"hash_confidence"`
|
||||||
|
LibraryName string `db:"library_name" json:"library_name"`
|
||||||
|
LibraryTypeName string `db:"library_type_name" json:"library_type_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) SearchMediaItemsUnified(ctx context.Context, arg SearchMediaItemsUnifiedParams) ([]SearchMediaItemsUnifiedRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, SearchMediaItemsUnified,
|
||||||
|
arg.UserID,
|
||||||
|
arg.LibraryID,
|
||||||
|
arg.AuthorFilter,
|
||||||
|
arg.SeriesFilter,
|
||||||
|
arg.GenreFilter,
|
||||||
|
arg.LanguageFilter,
|
||||||
|
arg.YearMin,
|
||||||
|
arg.YearMax,
|
||||||
|
arg.HasCover,
|
||||||
|
arg.SearchQuery,
|
||||||
|
arg.IsExactSearch,
|
||||||
|
arg.SearchPattern,
|
||||||
|
arg.Offset,
|
||||||
|
arg.Limit,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []SearchMediaItemsUnifiedRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i SearchMediaItemsUnifiedRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.LibraryID,
|
||||||
|
&i.Title,
|
||||||
|
&i.Author,
|
||||||
|
&i.Isbn,
|
||||||
|
&i.Description,
|
||||||
|
&i.FilePath,
|
||||||
|
&i.FileSize,
|
||||||
|
&i.MimeType,
|
||||||
|
&i.CoverImagePath,
|
||||||
|
&i.Series,
|
||||||
|
&i.SeriesNumber,
|
||||||
|
&i.Tags,
|
||||||
|
&i.Asin,
|
||||||
|
&i.DatePublished,
|
||||||
|
&i.Publisher,
|
||||||
|
&i.Contributors,
|
||||||
|
&i.Language,
|
||||||
|
&i.Edition,
|
||||||
|
&i.PageCount,
|
||||||
|
&i.Genre,
|
||||||
|
&i.CopyrightYear,
|
||||||
|
&i.GoodreadsID,
|
||||||
|
&i.OpenlibraryID,
|
||||||
|
&i.GoogleBooksID,
|
||||||
|
&i.AddedByAdminID,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
&i.FormatGroup,
|
||||||
|
&i.FormatMimetype,
|
||||||
|
&i.IsReflowable,
|
||||||
|
&i.HasFixedLayout,
|
||||||
|
&i.TotalCharacters,
|
||||||
|
&i.ChapterCount,
|
||||||
|
&i.EntitlementID,
|
||||||
|
&i.RevisionNumber,
|
||||||
|
&i.KoboContentID,
|
||||||
|
&i.KoboMetadata,
|
||||||
|
&i.TagsSearch,
|
||||||
|
&i.ContributorsSearch,
|
||||||
|
&i.FileSha256,
|
||||||
|
&i.OpfIdentifier,
|
||||||
|
&i.OpfUuid,
|
||||||
|
&i.HashConfidence,
|
||||||
|
&i.LibraryName,
|
||||||
|
&i.LibraryTypeName,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const SearchSeriesValues = `-- name: SearchSeriesValues :many
|
||||||
|
SELECT
|
||||||
|
mi.series as value,
|
||||||
|
COUNT(*) as count,
|
||||||
|
word_similarity($1, COALESCE(mi.series, ''))::float8 as score
|
||||||
|
FROM media_items mi
|
||||||
|
JOIN libraries l ON mi.library_id = l.id
|
||||||
|
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2
|
||||||
|
WHERE COALESCE(lv.is_visible, true) = true
|
||||||
|
AND mi.library_id = $3
|
||||||
|
AND word_similarity($1, COALESCE(mi.series, '')) > 0.3
|
||||||
|
AND mi.series IS NOT NULL
|
||||||
|
AND mi.series != ''
|
||||||
|
GROUP BY mi.series, word_similarity($1, COALESCE(mi.series, ''))::float8
|
||||||
|
ORDER BY score DESC, count DESC
|
||||||
|
LIMIT $5 OFFSET $4
|
||||||
|
`
|
||||||
|
|
||||||
|
type SearchSeriesValuesParams struct {
|
||||||
|
SearchQuery interface{} `db:"search_query" json:"search_query"`
|
||||||
|
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||||
|
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||||
|
Offset pgtype.Int4 `db:"offset" json:"offset"`
|
||||||
|
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchSeriesValuesRow struct {
|
||||||
|
Value pgtype.Text `db:"value" json:"value"`
|
||||||
|
Count int64 `db:"count" json:"count"`
|
||||||
|
Score float64 `db:"score" json:"score"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) SearchSeriesValues(ctx context.Context, arg SearchSeriesValuesParams) ([]SearchSeriesValuesRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, SearchSeriesValues,
|
||||||
|
arg.SearchQuery,
|
||||||
|
arg.UserID,
|
||||||
|
arg.LibraryID,
|
||||||
|
arg.Offset,
|
||||||
|
arg.Limit,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
items := []SearchSeriesValuesRow{}
|
||||||
|
for rows.Next() {
|
||||||
|
var i SearchSeriesValuesRow
|
||||||
|
if err := rows.Scan(&i.Value, &i.Count, &i.Score); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const SetLibraryVisibility = `-- name: SetLibraryVisibility :one
|
const SetLibraryVisibility = `-- name: SetLibraryVisibility :one
|
||||||
INSERT INTO library_visibility (user_id, library_id, is_visible)
|
INSERT INTO library_visibility (user_id, library_id, is_visible)
|
||||||
VALUES ($1, $2, $3)
|
VALUES ($1, $2, $3)
|
||||||
|
|||||||
Reference in New Issue
Block a user