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:
WithoutPants
2024-06-11 11:34:38 +10:00
committed by GitHub
parent ff23d4e20b
commit e843c890fb
41 changed files with 4562 additions and 3805 deletions

View File

@@ -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)
}