stash/pkg/models/querybuilder_gallery.go

398 lines
11 KiB
Go
Raw Normal View History

2019-02-09 12:30:49 +00:00
package models
import (
"database/sql"
"fmt"
"path/filepath"
"strconv"
2019-02-09 12:30:49 +00:00
"github.com/jmoiron/sqlx"
2019-02-14 23:42:52 +00:00
"github.com/stashapp/stash/pkg/database"
2019-02-09 12:30:49 +00:00
)
const galleryTable = "galleries"
type GalleryQueryBuilder struct{}
2019-02-09 12:30:49 +00:00
func NewGalleryQueryBuilder() GalleryQueryBuilder {
return GalleryQueryBuilder{}
2019-02-09 12:30:49 +00:00
}
func (qb *GalleryQueryBuilder) Create(newGallery Gallery, tx *sqlx.Tx) (*Gallery, error) {
2019-02-09 12:30:49 +00:00
ensureTx(tx)
result, err := tx.NamedExec(
`INSERT INTO galleries (path, checksum, zip, title, date, details, url, studio_id, rating, organized, scene_id, file_mod_time, created_at, updated_at)
VALUES (:path, :checksum, :zip, :title, :date, :details, :url, :studio_id, :rating, :organized, :scene_id, :file_mod_time, :created_at, :updated_at)
2019-02-09 12:30:49 +00:00
`,
newGallery,
)
if err != nil {
return nil, err
}
galleryID, err := result.LastInsertId()
if err != nil {
return nil, err
}
if err := tx.Get(&newGallery, `SELECT * FROM galleries WHERE id = ? LIMIT 1`, galleryID); err != nil {
return nil, err
}
return &newGallery, nil
}
func (qb *GalleryQueryBuilder) Update(updatedGallery Gallery, tx *sqlx.Tx) (*Gallery, error) {
2019-02-09 12:30:49 +00:00
ensureTx(tx)
_, err := tx.NamedExec(
`UPDATE galleries SET `+SQLGenKeys(updatedGallery)+` WHERE galleries.id = :id`,
2019-02-09 12:30:49 +00:00
updatedGallery,
)
if err != nil {
return nil, err
}
if err := tx.Get(&updatedGallery, `SELECT * FROM galleries WHERE id = ? LIMIT 1`, updatedGallery.ID); err != nil {
return nil, err
}
return &updatedGallery, nil
}
func (qb *GalleryQueryBuilder) UpdatePartial(updatedGallery GalleryPartial, tx *sqlx.Tx) (*Gallery, error) {
ensureTx(tx)
_, err := tx.NamedExec(
`UPDATE galleries SET `+SQLGenKeysPartial(updatedGallery)+` WHERE galleries.id = :id`,
updatedGallery,
)
if err != nil {
return nil, err
}
return qb.Find(updatedGallery.ID, tx)
}
func (qb *GalleryQueryBuilder) UpdateChecksum(id int, checksum string, tx *sqlx.Tx) error {
ensureTx(tx)
_, err := tx.Exec(
`UPDATE galleries SET checksum = ? WHERE galleries.id = ? `,
checksum, id,
)
if err != nil {
return err
}
return nil
}
func (qb *GalleryQueryBuilder) UpdateFileModTime(id int, modTime NullSQLiteTimestamp, tx *sqlx.Tx) error {
ensureTx(tx)
_, err := tx.Exec(
`UPDATE galleries SET file_mod_time = ? WHERE galleries.id = ? `,
modTime, id,
)
if err != nil {
return err
}
return nil
}
func (qb *GalleryQueryBuilder) Destroy(id int, tx *sqlx.Tx) error {
return executeDeleteQuery("galleries", strconv.Itoa(id), tx)
}
type GalleryNullSceneID struct {
SceneID sql.NullInt64
}
func (qb *GalleryQueryBuilder) ClearGalleryId(sceneID int, tx *sqlx.Tx) error {
ensureTx(tx)
_, err := tx.NamedExec(
`UPDATE galleries SET scene_id = null WHERE scene_id = :sceneid`,
GalleryNullSceneID{
SceneID: sql.NullInt64{
Int64: int64(sceneID),
Valid: true,
},
},
)
return err
}
func (qb *GalleryQueryBuilder) Find(id int, tx *sqlx.Tx) (*Gallery, error) {
2019-02-09 12:30:49 +00:00
query := "SELECT * FROM galleries WHERE id = ? LIMIT 1"
args := []interface{}{id}
return qb.queryGallery(query, args, tx)
2019-02-09 12:30:49 +00:00
}
func (qb *GalleryQueryBuilder) FindMany(ids []int) ([]*Gallery, error) {
var galleries []*Gallery
for _, id := range ids {
gallery, err := qb.Find(id, nil)
if err != nil {
return nil, err
}
if gallery == nil {
return nil, fmt.Errorf("gallery with id %d not found", id)
}
galleries = append(galleries, gallery)
}
return galleries, nil
}
func (qb *GalleryQueryBuilder) FindByChecksum(checksum string, tx *sqlx.Tx) (*Gallery, error) {
2019-02-09 12:30:49 +00:00
query := "SELECT * FROM galleries WHERE checksum = ? LIMIT 1"
args := []interface{}{checksum}
return qb.queryGallery(query, args, tx)
}
func (qb *GalleryQueryBuilder) FindByPath(path string) (*Gallery, error) {
2019-02-09 12:30:49 +00:00
query := "SELECT * FROM galleries WHERE path = ? LIMIT 1"
args := []interface{}{path}
return qb.queryGallery(query, args, nil)
}
func (qb *GalleryQueryBuilder) FindBySceneID(sceneID int, tx *sqlx.Tx) (*Gallery, error) {
query := "SELECT galleries.* FROM galleries WHERE galleries.scene_id = ? LIMIT 1"
2019-02-09 12:30:49 +00:00
args := []interface{}{sceneID}
return qb.queryGallery(query, args, tx)
}
2019-05-27 19:34:26 +00:00
func (qb *GalleryQueryBuilder) ValidGalleriesForScenePath(scenePath string) ([]*Gallery, error) {
2019-02-09 12:30:49 +00:00
sceneDirPath := filepath.Dir(scenePath)
query := "SELECT galleries.* FROM galleries WHERE galleries.scene_id IS NULL AND galleries.path LIKE '" + sceneDirPath + "%' ORDER BY path ASC"
return qb.queryGalleries(query, nil, nil)
}
func (qb *GalleryQueryBuilder) FindByImageID(imageID int, tx *sqlx.Tx) ([]*Gallery, error) {
query := selectAll(galleryTable) + `
LEFT JOIN galleries_images as images_join on images_join.gallery_id = galleries.id
WHERE images_join.image_id = ?
GROUP BY galleries.id
`
args := []interface{}{imageID}
return qb.queryGalleries(query, args, tx)
}
func (qb *GalleryQueryBuilder) CountByImageID(imageID int) (int, error) {
query := `SELECT image_id FROM galleries_images
WHERE image_id = ?
GROUP BY gallery_id`
args := []interface{}{imageID}
return runCountQuery(buildCountQuery(query), args)
}
func (qb *GalleryQueryBuilder) Count() (int, error) {
2019-02-11 20:36:10 +00:00
return runCountQuery(buildCountQuery("SELECT galleries.id FROM galleries"), nil)
}
2019-05-27 19:34:26 +00:00
func (qb *GalleryQueryBuilder) All() ([]*Gallery, error) {
return qb.queryGalleries(selectAll("galleries")+qb.getGallerySort(nil), nil, nil)
2019-02-09 12:30:49 +00:00
}
func (qb *GalleryQueryBuilder) Query(galleryFilter *GalleryFilterType, findFilter *FindFilterType) ([]*Gallery, int) {
if galleryFilter == nil {
galleryFilter = &GalleryFilterType{}
}
2019-02-09 12:30:49 +00:00
if findFilter == nil {
findFilter = &FindFilterType{}
}
query := queryBuilder{
tableName: galleryTable,
}
query.body = selectDistinctIDs("galleries")
query.body += `
left join performers_galleries as performers_join on performers_join.gallery_id = galleries.id
left join studios as studio on studio.id = galleries.studio_id
left join galleries_tags as tags_join on tags_join.gallery_id = galleries.id
left join galleries_images as images_join on images_join.gallery_id = galleries.id
left join images on images_join.image_id = images.id
`
2019-02-09 12:30:49 +00:00
if q := findFilter.Q; q != nil && *q != "" {
searchColumns := []string{"galleries.path", "galleries.checksum"}
clause, thisArgs := getSearchBinding(searchColumns, *q, false)
query.addWhere(clause)
query.addArg(thisArgs...)
}
if zipFilter := galleryFilter.IsZip; zipFilter != nil {
var favStr string
if *zipFilter == true {
favStr = "1"
} else {
favStr = "0"
}
query.addWhere("galleries.zip = " + favStr)
}
query.handleStringCriterionInput(galleryFilter.Path, "galleries.path")
query.handleIntCriterionInput(galleryFilter.Rating, "galleries.rating")
qb.handleAverageResolutionFilter(&query, galleryFilter.AverageResolution)
if Organized := galleryFilter.Organized; Organized != nil {
var organized string
if *Organized == true {
organized = "1"
} else {
organized = "0"
}
query.addWhere("galleries.organized = " + organized)
}
if isMissingFilter := galleryFilter.IsMissing; isMissingFilter != nil && *isMissingFilter != "" {
switch *isMissingFilter {
case "scene":
query.addWhere("galleries.scene_id IS NULL")
case "studio":
query.addWhere("galleries.studio_id IS NULL")
case "performers":
query.addWhere("performers_join.gallery_id IS NULL")
case "date":
query.addWhere("galleries.date IS \"\" OR galleries.date IS \"0001-01-01\"")
case "tags":
query.addWhere("tags_join.gallery_id IS NULL")
default:
query.addWhere("galleries." + *isMissingFilter + " IS NULL")
}
}
if tagsFilter := galleryFilter.Tags; tagsFilter != nil && len(tagsFilter.Value) > 0 {
for _, tagID := range tagsFilter.Value {
query.addArg(tagID)
}
query.body += " LEFT JOIN tags on tags_join.tag_id = tags.id"
whereClause, havingClause := getMultiCriterionClause("galleries", "tags", "tags_join", "gallery_id", "tag_id", tagsFilter)
query.addWhere(whereClause)
query.addHaving(havingClause)
}
if performersFilter := galleryFilter.Performers; performersFilter != nil && len(performersFilter.Value) > 0 {
for _, performerID := range performersFilter.Value {
query.addArg(performerID)
}
query.body += " LEFT JOIN performers ON performers_join.performer_id = performers.id"
whereClause, havingClause := getMultiCriterionClause("galleries", "performers", "performers_join", "gallery_id", "performer_id", performersFilter)
query.addWhere(whereClause)
query.addHaving(havingClause)
}
if studiosFilter := galleryFilter.Studios; studiosFilter != nil && len(studiosFilter.Value) > 0 {
for _, studioID := range studiosFilter.Value {
query.addArg(studioID)
}
whereClause, havingClause := getMultiCriterionClause("galleries", "studio", "", "", "studio_id", studiosFilter)
query.addWhere(whereClause)
query.addHaving(havingClause)
2019-02-09 12:30:49 +00:00
}
query.sortAndPagination = qb.getGallerySort(findFilter) + getPagination(findFilter)
idsResult, countResult := query.executeFind()
2019-02-09 12:30:49 +00:00
2019-05-27 19:34:26 +00:00
var galleries []*Gallery
2019-02-09 12:30:49 +00:00
for _, id := range idsResult {
gallery, _ := qb.Find(id, nil)
2019-05-27 19:34:26 +00:00
galleries = append(galleries, gallery)
2019-02-09 12:30:49 +00:00
}
return galleries, countResult
}
func (qb *GalleryQueryBuilder) handleAverageResolutionFilter(query *queryBuilder, resolutionFilter *ResolutionEnum) {
if resolutionFilter == nil {
return
}
if resolution := resolutionFilter.String(); resolutionFilter.IsValid() {
var low int
var high int
switch resolution {
case "LOW":
high = 480
case "STANDARD":
low = 480
high = 720
case "STANDARD_HD":
low = 720
high = 1080
case "FULL_HD":
low = 1080
high = 2160
case "FOUR_K":
low = 2160
}
havingClause := ""
if low != 0 {
havingClause = "avg(images.height) >= " + strconv.Itoa(low)
}
if high != 0 {
if havingClause != "" {
havingClause += " AND "
}
havingClause += "avg(images.height) < " + strconv.Itoa(high)
}
if havingClause != "" {
query.addHaving(havingClause)
}
}
}
func (qb *GalleryQueryBuilder) getGallerySort(findFilter *FindFilterType) string {
2019-02-09 12:30:49 +00:00
var sort string
var direction string
2020-04-03 03:40:37 +00:00
if findFilter == nil {
sort = "path"
direction = "ASC"
} else {
sort = findFilter.GetSort("path")
direction = findFilter.GetDirection()
}
2019-02-09 12:30:49 +00:00
return getSort(sort, direction, "galleries")
}
func (qb *GalleryQueryBuilder) queryGallery(query string, args []interface{}, tx *sqlx.Tx) (*Gallery, error) {
2019-02-09 12:30:49 +00:00
results, err := qb.queryGalleries(query, args, tx)
if err != nil || len(results) < 1 {
return nil, err
}
2019-05-27 19:34:26 +00:00
return results[0], nil
2019-02-09 12:30:49 +00:00
}
2019-05-27 19:34:26 +00:00
func (qb *GalleryQueryBuilder) queryGalleries(query string, args []interface{}, tx *sqlx.Tx) ([]*Gallery, error) {
2019-02-09 12:30:49 +00:00
var rows *sqlx.Rows
var err error
if tx != nil {
rows, err = tx.Queryx(query, args...)
} else {
rows, err = database.DB.Queryx(query, args...)
}
if err != nil && err != sql.ErrNoRows {
return nil, err
}
defer rows.Close()
2019-05-27 19:34:26 +00:00
galleries := make([]*Gallery, 0)
2019-02-09 12:30:49 +00:00
for rows.Next() {
2019-05-27 19:34:26 +00:00
gallery := Gallery{}
2019-02-09 12:30:49 +00:00
if err := rows.StructScan(&gallery); err != nil {
return nil, err
}
2019-05-27 19:34:26 +00:00
galleries = append(galleries, &gallery)
2019-02-09 12:30:49 +00:00
}
if err := rows.Err(); err != nil {
return nil, err
}
return galleries, nil
}