mirror of https://github.com/stashapp/stash.git
Query bug fixes (#1510)
* Fix joins being dropped * Fix missing scene stash_id criterion * Refactor criterion handlers * Add tag alias filter * Remove handleCriterionFunc
This commit is contained in:
parent
df6e06aaf6
commit
5fdfbaa7f1
|
@ -28,6 +28,10 @@ type criterionHandler interface {
|
|||
|
||||
type criterionHandlerFunc func(f *filterBuilder)
|
||||
|
||||
func (h criterionHandlerFunc) handle(f *filterBuilder) {
|
||||
h(f)
|
||||
}
|
||||
|
||||
type join struct {
|
||||
table string
|
||||
as string
|
||||
|
@ -62,13 +66,17 @@ type joins []join
|
|||
func (j *joins) add(newJoins ...join) {
|
||||
// only add if not already joined
|
||||
for _, newJoin := range newJoins {
|
||||
found := false
|
||||
for _, jj := range *j {
|
||||
if jj.equals(newJoin) {
|
||||
return
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
*j = append(*j, newJoin)
|
||||
if !found {
|
||||
*j = append(*j, newJoin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,15 +292,7 @@ func (f *filterBuilder) getError() error {
|
|||
// handleCriterion calls the handle function on the provided criterionHandler,
|
||||
// providing itself.
|
||||
func (f *filterBuilder) handleCriterion(handler criterionHandler) {
|
||||
f.handleCriterionFunc(func(h *filterBuilder) {
|
||||
handler.handle(h)
|
||||
})
|
||||
}
|
||||
|
||||
// handleCriterionFunc calls the provided criterion handler function providing
|
||||
// itself.
|
||||
func (f *filterBuilder) handleCriterionFunc(handler criterionHandlerFunc) {
|
||||
handler(f)
|
||||
handler.handle(f)
|
||||
}
|
||||
|
||||
func (f *filterBuilder) setError(e error) {
|
||||
|
@ -519,15 +519,9 @@ type stringListCriterionHandlerBuilder struct {
|
|||
func (m *stringListCriterionHandlerBuilder) handler(criterion *models.StringCriterionInput) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if criterion != nil && len(criterion.Value) > 0 {
|
||||
var args []interface{}
|
||||
for _, tagID := range criterion.Value {
|
||||
args = append(args, tagID)
|
||||
}
|
||||
|
||||
m.addJoinTable(f)
|
||||
|
||||
stringCriterionHandler(criterion, m.joinTable+"."+m.stringColumn)(f)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,31 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestJoinsAddJoin(t *testing.T) {
|
||||
var joins joins
|
||||
|
||||
// add a single join
|
||||
joins.add(join{table: "test"})
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
// ensure join was added
|
||||
assert.Len(joins, 1)
|
||||
|
||||
// add the same join and another
|
||||
joins.add([]join{
|
||||
{
|
||||
table: "test",
|
||||
},
|
||||
{
|
||||
table: "foo",
|
||||
},
|
||||
}...)
|
||||
|
||||
// should have added a single join
|
||||
assert.Len(joins, 2)
|
||||
}
|
||||
|
||||
func TestFilterBuilderAnd(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
|
@ -437,7 +462,7 @@ func TestStringCriterionHandlerIncludes(t *testing.T) {
|
|||
const quotedValue = `"two words"`
|
||||
|
||||
f := &filterBuilder{}
|
||||
f.handleCriterionFunc(stringCriterionHandler(&models.StringCriterionInput{
|
||||
f.handleCriterion(stringCriterionHandler(&models.StringCriterionInput{
|
||||
Modifier: models.CriterionModifierIncludes,
|
||||
Value: value1,
|
||||
}, column))
|
||||
|
@ -449,7 +474,7 @@ func TestStringCriterionHandlerIncludes(t *testing.T) {
|
|||
assert.Equal("%words%", f.whereClauses[0].args[1])
|
||||
|
||||
f = &filterBuilder{}
|
||||
f.handleCriterionFunc(stringCriterionHandler(&models.StringCriterionInput{
|
||||
f.handleCriterion(stringCriterionHandler(&models.StringCriterionInput{
|
||||
Modifier: models.CriterionModifierIncludes,
|
||||
Value: quotedValue,
|
||||
}, column))
|
||||
|
@ -468,7 +493,7 @@ func TestStringCriterionHandlerExcludes(t *testing.T) {
|
|||
const quotedValue = `"two words"`
|
||||
|
||||
f := &filterBuilder{}
|
||||
f.handleCriterionFunc(stringCriterionHandler(&models.StringCriterionInput{
|
||||
f.handleCriterion(stringCriterionHandler(&models.StringCriterionInput{
|
||||
Modifier: models.CriterionModifierExcludes,
|
||||
Value: value1,
|
||||
}, column))
|
||||
|
@ -480,7 +505,7 @@ func TestStringCriterionHandlerExcludes(t *testing.T) {
|
|||
assert.Equal("%words%", f.whereClauses[0].args[1])
|
||||
|
||||
f = &filterBuilder{}
|
||||
f.handleCriterionFunc(stringCriterionHandler(&models.StringCriterionInput{
|
||||
f.handleCriterion(stringCriterionHandler(&models.StringCriterionInput{
|
||||
Modifier: models.CriterionModifierExcludes,
|
||||
Value: quotedValue,
|
||||
}, column))
|
||||
|
@ -498,7 +523,7 @@ func TestStringCriterionHandlerEquals(t *testing.T) {
|
|||
const value1 = "two words"
|
||||
|
||||
f := &filterBuilder{}
|
||||
f.handleCriterionFunc(stringCriterionHandler(&models.StringCriterionInput{
|
||||
f.handleCriterion(stringCriterionHandler(&models.StringCriterionInput{
|
||||
Modifier: models.CriterionModifierEquals,
|
||||
Value: value1,
|
||||
}, column))
|
||||
|
@ -516,7 +541,7 @@ func TestStringCriterionHandlerNotEquals(t *testing.T) {
|
|||
const value1 = "two words"
|
||||
|
||||
f := &filterBuilder{}
|
||||
f.handleCriterionFunc(stringCriterionHandler(&models.StringCriterionInput{
|
||||
f.handleCriterion(stringCriterionHandler(&models.StringCriterionInput{
|
||||
Modifier: models.CriterionModifierNotEquals,
|
||||
Value: value1,
|
||||
}, column))
|
||||
|
@ -535,7 +560,7 @@ func TestStringCriterionHandlerMatchesRegex(t *testing.T) {
|
|||
const invalidValue = "*two words"
|
||||
|
||||
f := &filterBuilder{}
|
||||
f.handleCriterionFunc(stringCriterionHandler(&models.StringCriterionInput{
|
||||
f.handleCriterion(stringCriterionHandler(&models.StringCriterionInput{
|
||||
Modifier: models.CriterionModifierMatchesRegex,
|
||||
Value: validValue,
|
||||
}, column))
|
||||
|
@ -547,7 +572,7 @@ func TestStringCriterionHandlerMatchesRegex(t *testing.T) {
|
|||
|
||||
// ensure invalid regex sets error state
|
||||
f = &filterBuilder{}
|
||||
f.handleCriterionFunc(stringCriterionHandler(&models.StringCriterionInput{
|
||||
f.handleCriterion(stringCriterionHandler(&models.StringCriterionInput{
|
||||
Modifier: models.CriterionModifierMatchesRegex,
|
||||
Value: invalidValue,
|
||||
}, column))
|
||||
|
@ -563,7 +588,7 @@ func TestStringCriterionHandlerNotMatchesRegex(t *testing.T) {
|
|||
const invalidValue = "*two words"
|
||||
|
||||
f := &filterBuilder{}
|
||||
f.handleCriterionFunc(stringCriterionHandler(&models.StringCriterionInput{
|
||||
f.handleCriterion(stringCriterionHandler(&models.StringCriterionInput{
|
||||
Modifier: models.CriterionModifierNotMatchesRegex,
|
||||
Value: validValue,
|
||||
}, column))
|
||||
|
@ -575,7 +600,7 @@ func TestStringCriterionHandlerNotMatchesRegex(t *testing.T) {
|
|||
|
||||
// ensure invalid regex sets error state
|
||||
f = &filterBuilder{}
|
||||
f.handleCriterionFunc(stringCriterionHandler(&models.StringCriterionInput{
|
||||
f.handleCriterion(stringCriterionHandler(&models.StringCriterionInput{
|
||||
Modifier: models.CriterionModifierNotMatchesRegex,
|
||||
Value: invalidValue,
|
||||
}, column))
|
||||
|
@ -589,7 +614,7 @@ func TestStringCriterionHandlerIsNull(t *testing.T) {
|
|||
const column = "column"
|
||||
|
||||
f := &filterBuilder{}
|
||||
f.handleCriterionFunc(stringCriterionHandler(&models.StringCriterionInput{
|
||||
f.handleCriterion(stringCriterionHandler(&models.StringCriterionInput{
|
||||
Modifier: models.CriterionModifierIsNull,
|
||||
}, column))
|
||||
|
||||
|
@ -604,7 +629,7 @@ func TestStringCriterionHandlerNotNull(t *testing.T) {
|
|||
const column = "column"
|
||||
|
||||
f := &filterBuilder{}
|
||||
f.handleCriterionFunc(stringCriterionHandler(&models.StringCriterionInput{
|
||||
f.handleCriterion(stringCriterionHandler(&models.StringCriterionInput{
|
||||
Modifier: models.CriterionModifierNotNull,
|
||||
}, column))
|
||||
|
||||
|
|
|
@ -203,20 +203,20 @@ func (qb *galleryQueryBuilder) makeFilter(galleryFilter *models.GalleryFilterTyp
|
|||
query.not(qb.makeFilter(galleryFilter.Not))
|
||||
}
|
||||
|
||||
query.handleCriterionFunc(boolCriterionHandler(galleryFilter.IsZip, "galleries.zip"))
|
||||
query.handleCriterionFunc(stringCriterionHandler(galleryFilter.Path, "galleries.path"))
|
||||
query.handleCriterionFunc(intCriterionHandler(galleryFilter.Rating, "galleries.rating"))
|
||||
query.handleCriterionFunc(stringCriterionHandler(galleryFilter.URL, "galleries.url"))
|
||||
query.handleCriterionFunc(boolCriterionHandler(galleryFilter.Organized, "galleries.organized"))
|
||||
query.handleCriterionFunc(galleryIsMissingCriterionHandler(qb, galleryFilter.IsMissing))
|
||||
query.handleCriterionFunc(galleryTagsCriterionHandler(qb, galleryFilter.Tags))
|
||||
query.handleCriterionFunc(galleryTagCountCriterionHandler(qb, galleryFilter.TagCount))
|
||||
query.handleCriterionFunc(galleryPerformersCriterionHandler(qb, galleryFilter.Performers))
|
||||
query.handleCriterionFunc(galleryPerformerCountCriterionHandler(qb, galleryFilter.PerformerCount))
|
||||
query.handleCriterionFunc(galleryStudioCriterionHandler(qb, galleryFilter.Studios))
|
||||
query.handleCriterionFunc(galleryPerformerTagsCriterionHandler(qb, galleryFilter.PerformerTags))
|
||||
query.handleCriterionFunc(galleryAverageResolutionCriterionHandler(qb, galleryFilter.AverageResolution))
|
||||
query.handleCriterionFunc(galleryImageCountCriterionHandler(qb, galleryFilter.ImageCount))
|
||||
query.handleCriterion(boolCriterionHandler(galleryFilter.IsZip, "galleries.zip"))
|
||||
query.handleCriterion(stringCriterionHandler(galleryFilter.Path, "galleries.path"))
|
||||
query.handleCriterion(intCriterionHandler(galleryFilter.Rating, "galleries.rating"))
|
||||
query.handleCriterion(stringCriterionHandler(galleryFilter.URL, "galleries.url"))
|
||||
query.handleCriterion(boolCriterionHandler(galleryFilter.Organized, "galleries.organized"))
|
||||
query.handleCriterion(galleryIsMissingCriterionHandler(qb, galleryFilter.IsMissing))
|
||||
query.handleCriterion(galleryTagsCriterionHandler(qb, galleryFilter.Tags))
|
||||
query.handleCriterion(galleryTagCountCriterionHandler(qb, galleryFilter.TagCount))
|
||||
query.handleCriterion(galleryPerformersCriterionHandler(qb, galleryFilter.Performers))
|
||||
query.handleCriterion(galleryPerformerCountCriterionHandler(qb, galleryFilter.PerformerCount))
|
||||
query.handleCriterion(galleryStudioCriterionHandler(qb, galleryFilter.Studios))
|
||||
query.handleCriterion(galleryPerformerTagsCriterionHandler(qb, galleryFilter.PerformerTags))
|
||||
query.handleCriterion(galleryAverageResolutionCriterionHandler(qb, galleryFilter.AverageResolution))
|
||||
query.handleCriterion(galleryImageCountCriterionHandler(qb, galleryFilter.ImageCount))
|
||||
|
||||
return query
|
||||
}
|
||||
|
|
|
@ -231,20 +231,20 @@ func (qb *imageQueryBuilder) makeFilter(imageFilter *models.ImageFilterType) *fi
|
|||
query.not(qb.makeFilter(imageFilter.Not))
|
||||
}
|
||||
|
||||
query.handleCriterionFunc(stringCriterionHandler(imageFilter.Path, "images.path"))
|
||||
query.handleCriterionFunc(intCriterionHandler(imageFilter.Rating, "images.rating"))
|
||||
query.handleCriterionFunc(intCriterionHandler(imageFilter.OCounter, "images.o_counter"))
|
||||
query.handleCriterionFunc(boolCriterionHandler(imageFilter.Organized, "images.organized"))
|
||||
query.handleCriterionFunc(resolutionCriterionHandler(imageFilter.Resolution, "images.height", "images.width"))
|
||||
query.handleCriterionFunc(imageIsMissingCriterionHandler(qb, imageFilter.IsMissing))
|
||||
query.handleCriterion(stringCriterionHandler(imageFilter.Path, "images.path"))
|
||||
query.handleCriterion(intCriterionHandler(imageFilter.Rating, "images.rating"))
|
||||
query.handleCriterion(intCriterionHandler(imageFilter.OCounter, "images.o_counter"))
|
||||
query.handleCriterion(boolCriterionHandler(imageFilter.Organized, "images.organized"))
|
||||
query.handleCriterion(resolutionCriterionHandler(imageFilter.Resolution, "images.height", "images.width"))
|
||||
query.handleCriterion(imageIsMissingCriterionHandler(qb, imageFilter.IsMissing))
|
||||
|
||||
query.handleCriterionFunc(imageTagsCriterionHandler(qb, imageFilter.Tags))
|
||||
query.handleCriterionFunc(imageTagCountCriterionHandler(qb, imageFilter.TagCount))
|
||||
query.handleCriterionFunc(imageGalleriesCriterionHandler(qb, imageFilter.Galleries))
|
||||
query.handleCriterionFunc(imagePerformersCriterionHandler(qb, imageFilter.Performers))
|
||||
query.handleCriterionFunc(imagePerformerCountCriterionHandler(qb, imageFilter.PerformerCount))
|
||||
query.handleCriterionFunc(imageStudioCriterionHandler(qb, imageFilter.Studios))
|
||||
query.handleCriterionFunc(imagePerformerTagsCriterionHandler(qb, imageFilter.PerformerTags))
|
||||
query.handleCriterion(imageTagsCriterionHandler(qb, imageFilter.Tags))
|
||||
query.handleCriterion(imageTagCountCriterionHandler(qb, imageFilter.TagCount))
|
||||
query.handleCriterion(imageGalleriesCriterionHandler(qb, imageFilter.Galleries))
|
||||
query.handleCriterion(imagePerformersCriterionHandler(qb, imageFilter.Performers))
|
||||
query.handleCriterion(imagePerformerCountCriterionHandler(qb, imageFilter.PerformerCount))
|
||||
query.handleCriterion(imageStudioCriterionHandler(qb, imageFilter.Studios))
|
||||
query.handleCriterion(imagePerformerTagsCriterionHandler(qb, imageFilter.PerformerTags))
|
||||
|
||||
return query
|
||||
}
|
||||
|
|
|
@ -118,9 +118,9 @@ func (qb *movieQueryBuilder) All() ([]*models.Movie, error) {
|
|||
func (qb *movieQueryBuilder) makeFilter(movieFilter *models.MovieFilterType) *filterBuilder {
|
||||
query := &filterBuilder{}
|
||||
|
||||
query.handleCriterionFunc(movieIsMissingCriterionHandler(qb, movieFilter.IsMissing))
|
||||
query.handleCriterionFunc(stringCriterionHandler(movieFilter.URL, "movies.url"))
|
||||
query.handleCriterionFunc(movieStudioCriterionHandler(qb, movieFilter.Studios))
|
||||
query.handleCriterion(movieIsMissingCriterionHandler(qb, movieFilter.IsMissing))
|
||||
query.handleCriterion(stringCriterionHandler(movieFilter.URL, "movies.url"))
|
||||
query.handleCriterion(movieStudioCriterionHandler(qb, movieFilter.Studios))
|
||||
|
||||
return query
|
||||
}
|
||||
|
|
|
@ -239,51 +239,51 @@ func (qb *performerQueryBuilder) makeFilter(filter *models.PerformerFilterType)
|
|||
}
|
||||
|
||||
const tableName = performerTable
|
||||
query.handleCriterionFunc(boolCriterionHandler(filter.FilterFavorites, tableName+".favorite"))
|
||||
query.handleCriterion(boolCriterionHandler(filter.FilterFavorites, tableName+".favorite"))
|
||||
|
||||
query.handleCriterionFunc(yearFilterCriterionHandler(filter.BirthYear, tableName+".birthdate"))
|
||||
query.handleCriterionFunc(yearFilterCriterionHandler(filter.DeathYear, tableName+".death_date"))
|
||||
query.handleCriterion(yearFilterCriterionHandler(filter.BirthYear, tableName+".birthdate"))
|
||||
query.handleCriterion(yearFilterCriterionHandler(filter.DeathYear, tableName+".death_date"))
|
||||
|
||||
query.handleCriterionFunc(performerAgeFilterCriterionHandler(filter.Age))
|
||||
query.handleCriterion(performerAgeFilterCriterionHandler(filter.Age))
|
||||
|
||||
query.handleCriterionFunc(func(f *filterBuilder) {
|
||||
query.handleCriterion(criterionHandlerFunc(func(f *filterBuilder) {
|
||||
if gender := filter.Gender; gender != nil {
|
||||
f.addWhere(tableName+".gender = ?", gender.Value.String())
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
query.handleCriterionFunc(performerIsMissingCriterionHandler(qb, filter.IsMissing))
|
||||
query.handleCriterionFunc(stringCriterionHandler(filter.Ethnicity, tableName+".ethnicity"))
|
||||
query.handleCriterionFunc(stringCriterionHandler(filter.Country, tableName+".country"))
|
||||
query.handleCriterionFunc(stringCriterionHandler(filter.EyeColor, tableName+".eye_color"))
|
||||
query.handleCriterionFunc(stringCriterionHandler(filter.Height, tableName+".height"))
|
||||
query.handleCriterionFunc(stringCriterionHandler(filter.Measurements, tableName+".measurements"))
|
||||
query.handleCriterionFunc(stringCriterionHandler(filter.FakeTits, tableName+".fake_tits"))
|
||||
query.handleCriterionFunc(stringCriterionHandler(filter.CareerLength, tableName+".career_length"))
|
||||
query.handleCriterionFunc(stringCriterionHandler(filter.Tattoos, tableName+".tattoos"))
|
||||
query.handleCriterionFunc(stringCriterionHandler(filter.Piercings, tableName+".piercings"))
|
||||
query.handleCriterionFunc(intCriterionHandler(filter.Rating, tableName+".rating"))
|
||||
query.handleCriterionFunc(stringCriterionHandler(filter.HairColor, tableName+".hair_color"))
|
||||
query.handleCriterionFunc(stringCriterionHandler(filter.URL, tableName+".url"))
|
||||
query.handleCriterionFunc(intCriterionHandler(filter.Weight, tableName+".weight"))
|
||||
query.handleCriterionFunc(func(f *filterBuilder) {
|
||||
query.handleCriterion(performerIsMissingCriterionHandler(qb, filter.IsMissing))
|
||||
query.handleCriterion(stringCriterionHandler(filter.Ethnicity, tableName+".ethnicity"))
|
||||
query.handleCriterion(stringCriterionHandler(filter.Country, tableName+".country"))
|
||||
query.handleCriterion(stringCriterionHandler(filter.EyeColor, tableName+".eye_color"))
|
||||
query.handleCriterion(stringCriterionHandler(filter.Height, tableName+".height"))
|
||||
query.handleCriterion(stringCriterionHandler(filter.Measurements, tableName+".measurements"))
|
||||
query.handleCriterion(stringCriterionHandler(filter.FakeTits, tableName+".fake_tits"))
|
||||
query.handleCriterion(stringCriterionHandler(filter.CareerLength, tableName+".career_length"))
|
||||
query.handleCriterion(stringCriterionHandler(filter.Tattoos, tableName+".tattoos"))
|
||||
query.handleCriterion(stringCriterionHandler(filter.Piercings, tableName+".piercings"))
|
||||
query.handleCriterion(intCriterionHandler(filter.Rating, tableName+".rating"))
|
||||
query.handleCriterion(stringCriterionHandler(filter.HairColor, tableName+".hair_color"))
|
||||
query.handleCriterion(stringCriterionHandler(filter.URL, tableName+".url"))
|
||||
query.handleCriterion(intCriterionHandler(filter.Weight, tableName+".weight"))
|
||||
query.handleCriterion(criterionHandlerFunc(func(f *filterBuilder) {
|
||||
if filter.StashID != nil {
|
||||
qb.stashIDRepository().join(f, "performer_stash_ids", "performers.id")
|
||||
stringCriterionHandler(filter.StashID, "performer_stash_ids.stash_id")(f)
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
// TODO - need better handling of aliases
|
||||
query.handleCriterionFunc(stringCriterionHandler(filter.Aliases, tableName+".aliases"))
|
||||
query.handleCriterion(stringCriterionHandler(filter.Aliases, tableName+".aliases"))
|
||||
|
||||
query.handleCriterionFunc(performerTagsCriterionHandler(qb, filter.Tags))
|
||||
query.handleCriterion(performerTagsCriterionHandler(qb, filter.Tags))
|
||||
|
||||
query.handleCriterionFunc(performerStudiosCriterionHandler(filter.Studios))
|
||||
query.handleCriterion(performerStudiosCriterionHandler(filter.Studios))
|
||||
|
||||
query.handleCriterionFunc(performerTagCountCriterionHandler(qb, filter.TagCount))
|
||||
query.handleCriterionFunc(performerSceneCountCriterionHandler(qb, filter.SceneCount))
|
||||
query.handleCriterionFunc(performerImageCountCriterionHandler(qb, filter.ImageCount))
|
||||
query.handleCriterionFunc(performerGalleryCountCriterionHandler(qb, filter.GalleryCount))
|
||||
query.handleCriterion(performerTagCountCriterionHandler(qb, filter.TagCount))
|
||||
query.handleCriterion(performerSceneCountCriterionHandler(qb, filter.SceneCount))
|
||||
query.handleCriterion(performerImageCountCriterionHandler(qb, filter.ImageCount))
|
||||
query.handleCriterion(performerGalleryCountCriterionHandler(qb, filter.GalleryCount))
|
||||
|
||||
return query
|
||||
}
|
||||
|
|
|
@ -353,25 +353,32 @@ func (qb *sceneQueryBuilder) makeFilter(sceneFilter *models.SceneFilterType) *fi
|
|||
query.not(qb.makeFilter(sceneFilter.Not))
|
||||
}
|
||||
|
||||
query.handleCriterionFunc(stringCriterionHandler(sceneFilter.Path, "scenes.path"))
|
||||
query.handleCriterionFunc(intCriterionHandler(sceneFilter.Rating, "scenes.rating"))
|
||||
query.handleCriterionFunc(intCriterionHandler(sceneFilter.OCounter, "scenes.o_counter"))
|
||||
query.handleCriterionFunc(boolCriterionHandler(sceneFilter.Organized, "scenes.organized"))
|
||||
query.handleCriterionFunc(durationCriterionHandler(sceneFilter.Duration, "scenes.duration"))
|
||||
query.handleCriterionFunc(resolutionCriterionHandler(sceneFilter.Resolution, "scenes.height", "scenes.width"))
|
||||
query.handleCriterionFunc(hasMarkersCriterionHandler(sceneFilter.HasMarkers))
|
||||
query.handleCriterionFunc(sceneIsMissingCriterionHandler(qb, sceneFilter.IsMissing))
|
||||
query.handleCriterionFunc(stringCriterionHandler(sceneFilter.URL, "scenes.url"))
|
||||
query.handleCriterionFunc(stringCriterionHandler(sceneFilter.StashID, "scene_stash_ids.stash_id"))
|
||||
query.handleCriterionFunc(boolCriterionHandler(sceneFilter.Interactive, "scenes.interactive"))
|
||||
query.handleCriterion(stringCriterionHandler(sceneFilter.Path, "scenes.path"))
|
||||
query.handleCriterion(intCriterionHandler(sceneFilter.Rating, "scenes.rating"))
|
||||
query.handleCriterion(intCriterionHandler(sceneFilter.OCounter, "scenes.o_counter"))
|
||||
query.handleCriterion(boolCriterionHandler(sceneFilter.Organized, "scenes.organized"))
|
||||
query.handleCriterion(durationCriterionHandler(sceneFilter.Duration, "scenes.duration"))
|
||||
query.handleCriterion(resolutionCriterionHandler(sceneFilter.Resolution, "scenes.height", "scenes.width"))
|
||||
query.handleCriterion(hasMarkersCriterionHandler(sceneFilter.HasMarkers))
|
||||
query.handleCriterion(sceneIsMissingCriterionHandler(qb, sceneFilter.IsMissing))
|
||||
query.handleCriterion(stringCriterionHandler(sceneFilter.URL, "scenes.url"))
|
||||
|
||||
query.handleCriterionFunc(sceneTagsCriterionHandler(qb, sceneFilter.Tags))
|
||||
query.handleCriterionFunc(sceneTagCountCriterionHandler(qb, sceneFilter.TagCount))
|
||||
query.handleCriterionFunc(scenePerformersCriterionHandler(qb, sceneFilter.Performers))
|
||||
query.handleCriterionFunc(scenePerformerCountCriterionHandler(qb, sceneFilter.PerformerCount))
|
||||
query.handleCriterionFunc(sceneStudioCriterionHandler(qb, sceneFilter.Studios))
|
||||
query.handleCriterionFunc(sceneMoviesCriterionHandler(qb, sceneFilter.Movies))
|
||||
query.handleCriterionFunc(scenePerformerTagsCriterionHandler(qb, sceneFilter.PerformerTags))
|
||||
query.handleCriterion(criterionHandlerFunc(func(f *filterBuilder) {
|
||||
if sceneFilter.StashID != nil {
|
||||
qb.stashIDRepository().join(f, "scene_stash_ids", "scenes.id")
|
||||
stringCriterionHandler(sceneFilter.StashID, "scene_stash_ids.stash_id")(f)
|
||||
}
|
||||
}))
|
||||
|
||||
query.handleCriterion(boolCriterionHandler(sceneFilter.Interactive, "scenes.interactive"))
|
||||
|
||||
query.handleCriterion(sceneTagsCriterionHandler(qb, sceneFilter.Tags))
|
||||
query.handleCriterion(sceneTagCountCriterionHandler(qb, sceneFilter.TagCount))
|
||||
query.handleCriterion(scenePerformersCriterionHandler(qb, sceneFilter.Performers))
|
||||
query.handleCriterion(scenePerformerCountCriterionHandler(qb, sceneFilter.PerformerCount))
|
||||
query.handleCriterion(sceneStudioCriterionHandler(qb, sceneFilter.Studios))
|
||||
query.handleCriterion(sceneMoviesCriterionHandler(qb, sceneFilter.Movies))
|
||||
query.handleCriterion(scenePerformerTagsCriterionHandler(qb, sceneFilter.PerformerTags))
|
||||
|
||||
return query
|
||||
}
|
||||
|
@ -401,10 +408,6 @@ func (qb *sceneQueryBuilder) Query(sceneFilter *models.SceneFilterType, findFilt
|
|||
}
|
||||
filter := qb.makeFilter(sceneFilter)
|
||||
|
||||
if sceneFilter.StashID != nil {
|
||||
qb.stashIDRepository().join(filter, "scene_stash_ids", "scenes.id")
|
||||
}
|
||||
|
||||
query.addFilter(filter)
|
||||
|
||||
qb.setSceneSort(&query, findFilter)
|
||||
|
@ -427,14 +430,6 @@ func (qb *sceneQueryBuilder) Query(sceneFilter *models.SceneFilterType, findFilt
|
|||
return scenes, countResult, nil
|
||||
}
|
||||
|
||||
func appendClause(clauses []string, clause string) []string {
|
||||
if clause != "" {
|
||||
return append(clauses, clause)
|
||||
}
|
||||
|
||||
return clauses
|
||||
}
|
||||
|
||||
func durationCriterionHandler(durationFilter *models.IntCriterionInput, column string) criterionHandlerFunc {
|
||||
return func(f *filterBuilder) {
|
||||
if durationFilter != nil {
|
||||
|
@ -524,6 +519,9 @@ func sceneIsMissingCriterionHandler(qb *sceneQueryBuilder, isMissing *string) cr
|
|||
case "tags":
|
||||
qb.tagsRepository().join(f, "tags_join", "scenes.id")
|
||||
f.addWhere("tags_join.scene_id IS NULL")
|
||||
case "stash_id":
|
||||
qb.stashIDRepository().join(f, "scene_stash_ids", "scenes.id")
|
||||
f.addWhere("scene_stash_ids.scene_id IS NULL")
|
||||
default:
|
||||
f.addWhere("(scenes." + *isMissing + " IS NULL OR TRIM(scenes." + *isMissing + ") = '')")
|
||||
}
|
||||
|
@ -638,31 +636,6 @@ func scenePerformerTagsCriterionHandler(qb *sceneQueryBuilder, performerTagsFilt
|
|||
}
|
||||
}
|
||||
|
||||
func handleScenePerformerTagsCriterion(query *queryBuilder, performerTagsFilter *models.MultiCriterionInput) {
|
||||
if performerTagsFilter != nil && len(performerTagsFilter.Value) > 0 {
|
||||
for _, tagID := range performerTagsFilter.Value {
|
||||
query.addArg(tagID)
|
||||
}
|
||||
|
||||
query.body += " LEFT JOIN performers_tags AS performer_tags_join on performers_join.performer_id = performer_tags_join.performer_id"
|
||||
|
||||
if performerTagsFilter.Modifier == models.CriterionModifierIncludes {
|
||||
// includes any of the provided ids
|
||||
query.addWhere("performer_tags_join.tag_id IN " + getInBinding(len(performerTagsFilter.Value)))
|
||||
} else if performerTagsFilter.Modifier == models.CriterionModifierIncludesAll {
|
||||
// includes all of the provided ids
|
||||
query.addWhere("performer_tags_join.tag_id IN " + getInBinding(len(performerTagsFilter.Value)))
|
||||
query.addHaving(fmt.Sprintf("count(distinct performer_tags_join.tag_id) IS %d", len(performerTagsFilter.Value)))
|
||||
} else if performerTagsFilter.Modifier == models.CriterionModifierExcludes {
|
||||
query.addWhere(fmt.Sprintf(`not exists
|
||||
(select performers_scenes.performer_id from performers_scenes
|
||||
left join performers_tags on performers_tags.performer_id = performers_scenes.performer_id where
|
||||
performers_scenes.scene_id = scenes.id AND
|
||||
performers_tags.tag_id in %s)`, getInBinding(len(performerTagsFilter.Value))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (qb *sceneQueryBuilder) getDefaultSceneSort() string {
|
||||
return " ORDER BY scenes.path, scenes.date ASC "
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// +build integration
|
||||
|
||||
package sqlite_test
|
||||
|
||||
import (
|
||||
|
|
|
@ -279,14 +279,14 @@ func (qb *tagQueryBuilder) makeFilter(tagFilter *models.TagFilterType) *filterBu
|
|||
// }
|
||||
// }
|
||||
|
||||
query.handleCriterionFunc(stringCriterionHandler(tagFilter.Name, tagTable+".name"))
|
||||
query.handleCriterionFunc(tagAliasCriterionHandler(qb, tagFilter.Aliases))
|
||||
query.handleCriterion(stringCriterionHandler(tagFilter.Name, tagTable+".name"))
|
||||
query.handleCriterion(tagAliasCriterionHandler(qb, tagFilter.Aliases))
|
||||
|
||||
query.handleCriterionFunc(tagIsMissingCriterionHandler(qb, tagFilter.IsMissing))
|
||||
query.handleCriterionFunc(tagSceneCountCriterionHandler(qb, tagFilter.SceneCount))
|
||||
query.handleCriterionFunc(tagImageCountCriterionHandler(qb, tagFilter.ImageCount))
|
||||
query.handleCriterionFunc(tagGalleryCountCriterionHandler(qb, tagFilter.GalleryCount))
|
||||
query.handleCriterionFunc(tagPerformerCountCriterionHandler(qb, tagFilter.PerformerCount))
|
||||
query.handleCriterion(tagIsMissingCriterionHandler(qb, tagFilter.IsMissing))
|
||||
query.handleCriterion(tagSceneCountCriterionHandler(qb, tagFilter.SceneCount))
|
||||
query.handleCriterion(tagImageCountCriterionHandler(qb, tagFilter.ImageCount))
|
||||
query.handleCriterion(tagGalleryCountCriterionHandler(qb, tagFilter.GalleryCount))
|
||||
query.handleCriterion(tagPerformerCountCriterionHandler(qb, tagFilter.PerformerCount))
|
||||
|
||||
return query
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
* Add button to remove studio stash ID. ([#1378](https://github.com/stashapp/stash/pull/1378))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
* Fix query with multiple table joins causing invalid query SQL. ([#1510](https://github.com/stashapp/stash/pull/1510))
|
||||
* Fix file move detection when case of filename is changed on case-insensitive file systems. ([#1426](https://github.com/stashapp/stash/issues/1426))
|
||||
* Fix auto-tagger not tagging scenes with no whitespace in name. ([#1488](https://github.com/stashapp/stash/pull/1488))
|
||||
* Fix click/drag to select scenes. ([#1476](https://github.com/stashapp/stash/pull/1476))
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { createMandatoryNumberCriterionOption } from "./criteria/criterion";
|
||||
import {
|
||||
createMandatoryNumberCriterionOption,
|
||||
createStringCriterionOption,
|
||||
} from "./criteria/criterion";
|
||||
import { TagIsMissingCriterionOption } from "./criteria/is-missing";
|
||||
import { ListFilterOptions } from "./filter-options";
|
||||
import { DisplayMode } from "./types";
|
||||
|
@ -34,6 +37,7 @@ const sortByOptions = [
|
|||
const displayModeOptions = [DisplayMode.Grid, DisplayMode.List];
|
||||
const criterionOptions = [
|
||||
TagIsMissingCriterionOption,
|
||||
createStringCriterionOption("aliases"),
|
||||
createMandatoryNumberCriterionOption("scene_count"),
|
||||
createMandatoryNumberCriterionOption("image_count"),
|
||||
createMandatoryNumberCriterionOption("gallery_count"),
|
||||
|
|
Loading…
Reference in New Issue