mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Add related object filter criteria to various filter types in graphql schema (#4861)
* Move filter criterion handlers into separate file * Add related filters for image filter * Add related filters for scene filter * Add related filters to gallery filter * Add related filters to movie filter * Add related filters to performer filter * Add related filters to studio filter * Add related filters to tag filter * Add scene filter to scene marker filter
This commit is contained in:
@@ -90,8 +90,44 @@ func (r *studioRowRecord) fromPartial(o models.StudioPartial) {
|
||||
r.setBool("ignore_auto_tag", o.IgnoreAutoTag)
|
||||
}
|
||||
|
||||
type StudioStore struct {
|
||||
type studioRepositoryType struct {
|
||||
repository
|
||||
|
||||
stashIDs stashIDRepository
|
||||
|
||||
scenes repository
|
||||
images repository
|
||||
galleries repository
|
||||
}
|
||||
|
||||
var (
|
||||
studioRepository = studioRepositoryType{
|
||||
repository: repository{
|
||||
tableName: studioTable,
|
||||
idColumn: idColumn,
|
||||
},
|
||||
stashIDs: stashIDRepository{
|
||||
repository{
|
||||
tableName: "studio_stash_ids",
|
||||
idColumn: studioIDColumn,
|
||||
},
|
||||
},
|
||||
scenes: repository{
|
||||
tableName: sceneTable,
|
||||
idColumn: studioIDColumn,
|
||||
},
|
||||
images: repository{
|
||||
tableName: imageTable,
|
||||
idColumn: studioIDColumn,
|
||||
},
|
||||
galleries: repository{
|
||||
tableName: galleryTable,
|
||||
idColumn: studioIDColumn,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type StudioStore struct {
|
||||
blobJoinQueryBuilder
|
||||
|
||||
tableMgr *table
|
||||
@@ -99,10 +135,6 @@ type StudioStore struct {
|
||||
|
||||
func NewStudioStore(blobStore *BlobStore) *StudioStore {
|
||||
return &StudioStore{
|
||||
repository: repository{
|
||||
tableName: studioTable,
|
||||
idColumn: idColumn,
|
||||
},
|
||||
blobJoinQueryBuilder: blobJoinQueryBuilder{
|
||||
blobStore: blobStore,
|
||||
joinTable: studioTable,
|
||||
@@ -147,7 +179,7 @@ func (qb *StudioStore) Create(ctx context.Context, newObject *models.Studio) err
|
||||
}
|
||||
}
|
||||
|
||||
updated, _ := qb.find(ctx, id)
|
||||
updated, err := qb.find(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("finding after create: %w", err)
|
||||
}
|
||||
@@ -220,7 +252,7 @@ func (qb *StudioStore) Destroy(ctx context.Context, id int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return qb.destroyExisting(ctx, []int{id})
|
||||
return studioRepository.destroyExisting(ctx, []int{id})
|
||||
}
|
||||
|
||||
// returns nil, nil if not found
|
||||
@@ -452,83 +484,6 @@ func (qb *StudioStore) QueryForAutoTag(ctx context.Context, words []string) ([]*
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (qb *StudioStore) validateFilter(filter *models.StudioFilterType) error {
|
||||
const and = "AND"
|
||||
const or = "OR"
|
||||
const not = "NOT"
|
||||
|
||||
if filter.And != nil {
|
||||
if filter.Or != nil {
|
||||
return illegalFilterCombination(and, or)
|
||||
}
|
||||
if filter.Not != nil {
|
||||
return illegalFilterCombination(and, not)
|
||||
}
|
||||
|
||||
return qb.validateFilter(filter.And)
|
||||
}
|
||||
|
||||
if filter.Or != nil {
|
||||
if filter.Not != nil {
|
||||
return illegalFilterCombination(or, not)
|
||||
}
|
||||
|
||||
return qb.validateFilter(filter.Or)
|
||||
}
|
||||
|
||||
if filter.Not != nil {
|
||||
return qb.validateFilter(filter.Not)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (qb *StudioStore) makeFilter(ctx context.Context, studioFilter *models.StudioFilterType) *filterBuilder {
|
||||
query := &filterBuilder{}
|
||||
|
||||
if studioFilter.And != nil {
|
||||
query.and(qb.makeFilter(ctx, studioFilter.And))
|
||||
}
|
||||
if studioFilter.Or != nil {
|
||||
query.or(qb.makeFilter(ctx, studioFilter.Or))
|
||||
}
|
||||
if studioFilter.Not != nil {
|
||||
query.not(qb.makeFilter(ctx, studioFilter.Not))
|
||||
}
|
||||
|
||||
query.handleCriterion(ctx, stringCriterionHandler(studioFilter.Name, studioTable+".name"))
|
||||
query.handleCriterion(ctx, stringCriterionHandler(studioFilter.Details, studioTable+".details"))
|
||||
query.handleCriterion(ctx, stringCriterionHandler(studioFilter.URL, studioTable+".url"))
|
||||
query.handleCriterion(ctx, intCriterionHandler(studioFilter.Rating100, studioTable+".rating", nil))
|
||||
query.handleCriterion(ctx, boolCriterionHandler(studioFilter.Favorite, studioTable+".favorite", nil))
|
||||
query.handleCriterion(ctx, boolCriterionHandler(studioFilter.IgnoreAutoTag, studioTable+".ignore_auto_tag", nil))
|
||||
|
||||
query.handleCriterion(ctx, criterionHandlerFunc(func(ctx context.Context, f *filterBuilder) {
|
||||
if studioFilter.StashID != nil {
|
||||
qb.stashIDRepository().join(f, "studio_stash_ids", "studios.id")
|
||||
stringCriterionHandler(studioFilter.StashID, "studio_stash_ids.stash_id")(ctx, f)
|
||||
}
|
||||
}))
|
||||
query.handleCriterion(ctx, &stashIDCriterionHandler{
|
||||
c: studioFilter.StashIDEndpoint,
|
||||
stashIDRepository: qb.stashIDRepository(),
|
||||
stashIDTableAs: "studio_stash_ids",
|
||||
parentIDCol: "studios.id",
|
||||
})
|
||||
|
||||
query.handleCriterion(ctx, studioIsMissingCriterionHandler(qb, studioFilter.IsMissing))
|
||||
query.handleCriterion(ctx, studioSceneCountCriterionHandler(qb, studioFilter.SceneCount))
|
||||
query.handleCriterion(ctx, studioImageCountCriterionHandler(qb, studioFilter.ImageCount))
|
||||
query.handleCriterion(ctx, studioGalleryCountCriterionHandler(qb, studioFilter.GalleryCount))
|
||||
query.handleCriterion(ctx, studioParentCriterionHandler(qb, studioFilter.Parents))
|
||||
query.handleCriterion(ctx, studioAliasCriterionHandler(qb, studioFilter.Aliases))
|
||||
query.handleCriterion(ctx, studioChildCountCriterionHandler(qb, studioFilter.ChildCount))
|
||||
query.handleCriterion(ctx, timestampCriterionHandler(studioFilter.CreatedAt, studioTable+".created_at"))
|
||||
query.handleCriterion(ctx, timestampCriterionHandler(studioFilter.UpdatedAt, studioTable+".updated_at"))
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
func (qb *StudioStore) makeQuery(ctx context.Context, studioFilter *models.StudioFilterType, findFilter *models.FindFilterType) (*queryBuilder, error) {
|
||||
if studioFilter == nil {
|
||||
studioFilter = &models.StudioFilterType{}
|
||||
@@ -537,7 +492,7 @@ func (qb *StudioStore) makeQuery(ctx context.Context, studioFilter *models.Studi
|
||||
findFilter = &models.FindFilterType{}
|
||||
}
|
||||
|
||||
query := qb.newQuery()
|
||||
query := studioRepository.newQuery()
|
||||
distinctIDs(&query, studioTable)
|
||||
|
||||
if q := findFilter.Q; q != nil && *q != "" {
|
||||
@@ -546,10 +501,9 @@ func (qb *StudioStore) makeQuery(ctx context.Context, studioFilter *models.Studi
|
||||
query.parseQueryString(searchColumns, *q)
|
||||
}
|
||||
|
||||
if err := qb.validateFilter(studioFilter); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filter := qb.makeFilter(ctx, studioFilter)
|
||||
filter := filterBuilderFromHandler(ctx, &studioFilterHandler{
|
||||
studioFilter: studioFilter,
|
||||
})
|
||||
|
||||
if err := query.addFilter(filter); err != nil {
|
||||
return nil, err
|
||||
@@ -584,93 +538,6 @@ func (qb *StudioStore) Query(ctx context.Context, studioFilter *models.StudioFil
|
||||
return studios, countResult, nil
|
||||
}
|
||||
|
||||
func studioIsMissingCriterionHandler(qb *StudioStore, isMissing *string) criterionHandlerFunc {
|
||||
return func(ctx context.Context, f *filterBuilder) {
|
||||
if isMissing != nil && *isMissing != "" {
|
||||
switch *isMissing {
|
||||
case "image":
|
||||
f.addWhere("studios.image_blob IS NULL")
|
||||
case "stash_id":
|
||||
qb.stashIDRepository().join(f, "studio_stash_ids", "studios.id")
|
||||
f.addWhere("studio_stash_ids.studio_id IS NULL")
|
||||
default:
|
||||
f.addWhere("(studios." + *isMissing + " IS NULL OR TRIM(studios." + *isMissing + ") = '')")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func studioSceneCountCriterionHandler(qb *StudioStore, sceneCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(ctx context.Context, f *filterBuilder) {
|
||||
if sceneCount != nil {
|
||||
f.addLeftJoin("scenes", "", "scenes.studio_id = studios.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct scenes.id)", *sceneCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func studioImageCountCriterionHandler(qb *StudioStore, imageCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(ctx context.Context, f *filterBuilder) {
|
||||
if imageCount != nil {
|
||||
f.addLeftJoin("images", "", "images.studio_id = studios.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct images.id)", *imageCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func studioGalleryCountCriterionHandler(qb *StudioStore, galleryCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(ctx context.Context, f *filterBuilder) {
|
||||
if galleryCount != nil {
|
||||
f.addLeftJoin("galleries", "", "galleries.studio_id = studios.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct galleries.id)", *galleryCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func studioParentCriterionHandler(qb *StudioStore, parents *models.MultiCriterionInput) criterionHandlerFunc {
|
||||
addJoinsFunc := func(f *filterBuilder) {
|
||||
f.addLeftJoin("studios", "parent_studio", "parent_studio.id = studios.parent_id")
|
||||
}
|
||||
h := multiCriterionHandlerBuilder{
|
||||
primaryTable: studioTable,
|
||||
foreignTable: "parent_studio",
|
||||
joinTable: "",
|
||||
primaryFK: studioIDColumn,
|
||||
foreignFK: "parent_id",
|
||||
addJoinsFunc: addJoinsFunc,
|
||||
}
|
||||
return h.handler(parents)
|
||||
}
|
||||
|
||||
func studioAliasCriterionHandler(qb *StudioStore, alias *models.StringCriterionInput) criterionHandlerFunc {
|
||||
h := stringListCriterionHandlerBuilder{
|
||||
joinTable: studioAliasesTable,
|
||||
stringColumn: studioAliasColumn,
|
||||
addJoinTable: func(f *filterBuilder) {
|
||||
studiosAliasesTableMgr.join(f, "", "studios.id")
|
||||
},
|
||||
}
|
||||
|
||||
return h.handler(alias)
|
||||
}
|
||||
|
||||
func studioChildCountCriterionHandler(qb *StudioStore, childCount *models.IntCriterionInput) criterionHandlerFunc {
|
||||
return func(ctx context.Context, f *filterBuilder) {
|
||||
if childCount != nil {
|
||||
f.addLeftJoin("studios", "children_count", "children_count.parent_id = studios.id")
|
||||
clause, args := getIntCriterionWhereClause("count(distinct children_count.id)", *childCount)
|
||||
|
||||
f.addHaving(clause, args...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var studioSortOptions = sortOptions{
|
||||
"child_count",
|
||||
"created_at",
|
||||
@@ -735,16 +602,6 @@ func (qb *StudioStore) destroyImage(ctx context.Context, studioID int) error {
|
||||
return qb.blobJoinQueryBuilder.DestroyImage(ctx, studioID, studioImageBlobColumn)
|
||||
}
|
||||
|
||||
func (qb *StudioStore) stashIDRepository() *stashIDRepository {
|
||||
return &stashIDRepository{
|
||||
repository{
|
||||
tx: qb.tx,
|
||||
tableName: "studio_stash_ids",
|
||||
idColumn: studioIDColumn,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (qb *StudioStore) GetStashIDs(ctx context.Context, studioID int) ([]models.StashID, error) {
|
||||
return studiosStashIDsTableMgr.get(ctx, studioID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user