mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Add AND, OR and NOT support to studio filter (#1726)
This commit is contained in:
@@ -181,6 +181,10 @@ input MovieFilterType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input StudioFilterType {
|
input StudioFilterType {
|
||||||
|
AND: StudioFilterType
|
||||||
|
OR: StudioFilterType
|
||||||
|
NOT: StudioFilterType
|
||||||
|
|
||||||
name: StringCriterionInput
|
name: StringCriterionInput
|
||||||
details: StringCriterionInput
|
details: StringCriterionInput
|
||||||
"""Filter to only include studios with this parent studio"""
|
"""Filter to only include studios with this parent studio"""
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ func TestPerformerQueryEthnicityAndRating(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPerformerQueryPathNotRating(t *testing.T) {
|
func TestPerformerQueryEthnicityNotRating(t *testing.T) {
|
||||||
const performerIdx = 1
|
const performerIdx = 1
|
||||||
|
|
||||||
performerRating := getRating(performerIdx)
|
performerRating := getRating(performerIdx)
|
||||||
|
|||||||
@@ -147,9 +147,50 @@ func (qb *studioQueryBuilder) QueryForAutoTag(words []string) ([]*models.Studio,
|
|||||||
return qb.queryStudios(query+" WHERE "+where, args)
|
return qb.queryStudios(query+" WHERE "+where, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (qb *studioQueryBuilder) 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 *studioQueryBuilder) makeFilter(studioFilter *models.StudioFilterType) *filterBuilder {
|
func (qb *studioQueryBuilder) makeFilter(studioFilter *models.StudioFilterType) *filterBuilder {
|
||||||
query := &filterBuilder{}
|
query := &filterBuilder{}
|
||||||
|
|
||||||
|
if studioFilter.And != nil {
|
||||||
|
query.and(qb.makeFilter(studioFilter.And))
|
||||||
|
}
|
||||||
|
if studioFilter.Or != nil {
|
||||||
|
query.or(qb.makeFilter(studioFilter.Or))
|
||||||
|
}
|
||||||
|
if studioFilter.Not != nil {
|
||||||
|
query.not(qb.makeFilter(studioFilter.Not))
|
||||||
|
}
|
||||||
|
|
||||||
query.handleCriterion(stringCriterionHandler(studioFilter.Name, studioTable+".name"))
|
query.handleCriterion(stringCriterionHandler(studioFilter.Name, studioTable+".name"))
|
||||||
query.handleCriterion(stringCriterionHandler(studioFilter.Details, studioTable+".details"))
|
query.handleCriterion(stringCriterionHandler(studioFilter.Details, studioTable+".details"))
|
||||||
query.handleCriterion(stringCriterionHandler(studioFilter.URL, studioTable+".url"))
|
query.handleCriterion(stringCriterionHandler(studioFilter.URL, studioTable+".url"))
|
||||||
@@ -193,6 +234,9 @@ func (qb *studioQueryBuilder) Query(studioFilter *models.StudioFilterType, findF
|
|||||||
query.addArg(thisArgs...)
|
query.addArg(thisArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := qb.validateFilter(studioFilter); err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
filter := qb.makeFilter(studioFilter)
|
filter := qb.makeFilter(studioFilter)
|
||||||
|
|
||||||
query.addFilter(filter)
|
query.addFilter(filter)
|
||||||
|
|||||||
@@ -46,6 +46,143 @@ func TestStudioFindByName(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStudioQueryNameOr(t *testing.T) {
|
||||||
|
const studio1Idx = 1
|
||||||
|
const studio2Idx = 2
|
||||||
|
|
||||||
|
studio1Name := getStudioStringValue(studio1Idx, "Name")
|
||||||
|
studio2Name := getStudioStringValue(studio2Idx, "Name")
|
||||||
|
|
||||||
|
studioFilter := models.StudioFilterType{
|
||||||
|
Name: &models.StringCriterionInput{
|
||||||
|
Value: studio1Name,
|
||||||
|
Modifier: models.CriterionModifierEquals,
|
||||||
|
},
|
||||||
|
Or: &models.StudioFilterType{
|
||||||
|
Name: &models.StringCriterionInput{
|
||||||
|
Value: studio2Name,
|
||||||
|
Modifier: models.CriterionModifierEquals,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
withTxn(func(r models.Repository) error {
|
||||||
|
sqb := r.Studio()
|
||||||
|
|
||||||
|
studios := queryStudio(t, sqb, &studioFilter, nil)
|
||||||
|
|
||||||
|
assert.Len(t, studios, 2)
|
||||||
|
assert.Equal(t, studio1Name, studios[0].Name.String)
|
||||||
|
assert.Equal(t, studio2Name, studios[1].Name.String)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStudioQueryNameAndUrl(t *testing.T) {
|
||||||
|
const studioIdx = 1
|
||||||
|
studioName := getStudioStringValue(studioIdx, "Name")
|
||||||
|
studioUrl := getStudioNullStringValue(studioIdx, urlField)
|
||||||
|
|
||||||
|
studioFilter := models.StudioFilterType{
|
||||||
|
Name: &models.StringCriterionInput{
|
||||||
|
Value: studioName,
|
||||||
|
Modifier: models.CriterionModifierEquals,
|
||||||
|
},
|
||||||
|
And: &models.StudioFilterType{
|
||||||
|
URL: &models.StringCriterionInput{
|
||||||
|
Value: studioUrl.String,
|
||||||
|
Modifier: models.CriterionModifierEquals,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
withTxn(func(r models.Repository) error {
|
||||||
|
sqb := r.Studio()
|
||||||
|
|
||||||
|
studios := queryStudio(t, sqb, &studioFilter, nil)
|
||||||
|
|
||||||
|
assert.Len(t, studios, 1)
|
||||||
|
assert.Equal(t, studioName, studios[0].Name.String)
|
||||||
|
assert.Equal(t, studioUrl.String, studios[0].URL.String)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStudioQueryNameNotUrl(t *testing.T) {
|
||||||
|
const studioIdx = 1
|
||||||
|
|
||||||
|
studioUrl := getStudioNullStringValue(studioIdx, urlField)
|
||||||
|
|
||||||
|
nameCriterion := models.StringCriterionInput{
|
||||||
|
Value: "studio_.*1_Name",
|
||||||
|
Modifier: models.CriterionModifierMatchesRegex,
|
||||||
|
}
|
||||||
|
|
||||||
|
urlCriterion := models.StringCriterionInput{
|
||||||
|
Value: studioUrl.String,
|
||||||
|
Modifier: models.CriterionModifierEquals,
|
||||||
|
}
|
||||||
|
|
||||||
|
studioFilter := models.StudioFilterType{
|
||||||
|
Name: &nameCriterion,
|
||||||
|
Not: &models.StudioFilterType{
|
||||||
|
URL: &urlCriterion,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
withTxn(func(r models.Repository) error {
|
||||||
|
sqb := r.Studio()
|
||||||
|
|
||||||
|
studios := queryStudio(t, sqb, &studioFilter, nil)
|
||||||
|
|
||||||
|
for _, studio := range studios {
|
||||||
|
verifyString(t, studio.Name.String, nameCriterion)
|
||||||
|
urlCriterion.Modifier = models.CriterionModifierNotEquals
|
||||||
|
verifyNullString(t, studio.URL, urlCriterion)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStudioIllegalQuery(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
const studioIdx = 1
|
||||||
|
subFilter := models.StudioFilterType{
|
||||||
|
Name: &models.StringCriterionInput{
|
||||||
|
Value: getStudioStringValue(studioIdx, "Name"),
|
||||||
|
Modifier: models.CriterionModifierEquals,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
studioFilter := &models.StudioFilterType{
|
||||||
|
And: &subFilter,
|
||||||
|
Or: &subFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
withTxn(func(r models.Repository) error {
|
||||||
|
sqb := r.Studio()
|
||||||
|
|
||||||
|
_, _, err := sqb.Query(studioFilter, nil)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
studioFilter.Or = nil
|
||||||
|
studioFilter.Not = &subFilter
|
||||||
|
_, _, err = sqb.Query(studioFilter, nil)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
studioFilter.And = nil
|
||||||
|
studioFilter.Or = &subFilter
|
||||||
|
_, _, err = sqb.Query(studioFilter, nil)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestStudioQueryForAutoTag(t *testing.T) {
|
func TestStudioQueryForAutoTag(t *testing.T) {
|
||||||
withTxn(func(r models.Repository) error {
|
withTxn(func(r models.Repository) error {
|
||||||
tqb := r.Studio()
|
tqb := r.Studio()
|
||||||
|
|||||||
Reference in New Issue
Block a user