Gallery filter fix (#1147)

* Fix gallery performer and tags filters
* Add unit tests
This commit is contained in:
WithoutPants
2021-03-01 13:30:40 +11:00
committed by GitHub
parent 44ea777019
commit 7cfff46d02
5 changed files with 362 additions and 19 deletions

View File

@@ -233,7 +233,7 @@ func (qb *galleryQueryBuilder) Query(galleryFilter *models.GalleryFilterType, fi
} }
query.body += " LEFT JOIN tags on tags_join.tag_id = tags.id" query.body += " LEFT JOIN tags on tags_join.tag_id = tags.id"
whereClause, havingClause := getMultiCriterionClause("galleries", "tags", "tags_join", "gallery_id", "tag_id", tagsFilter) whereClause, havingClause := getMultiCriterionClause("galleries", "tags", "galleries_tags", "gallery_id", "tag_id", tagsFilter)
query.addWhere(whereClause) query.addWhere(whereClause)
query.addHaving(havingClause) query.addHaving(havingClause)
} }
@@ -244,7 +244,7 @@ func (qb *galleryQueryBuilder) Query(galleryFilter *models.GalleryFilterType, fi
} }
query.body += " LEFT JOIN performers ON performers_join.performer_id = performers.id" query.body += " LEFT JOIN performers ON performers_join.performer_id = performers.id"
whereClause, havingClause := getMultiCriterionClause("galleries", "performers", "performers_join", "gallery_id", "performer_id", performersFilter) whereClause, havingClause := getMultiCriterionClause("galleries", "performers", "performers_galleries", "gallery_id", "performer_id", performersFilter)
query.addWhere(whereClause) query.addWhere(whereClause)
query.addHaving(havingClause) query.addHaving(havingClause)
} }

View File

@@ -3,6 +3,7 @@
package sqlite_test package sqlite_test
import ( import (
"strconv"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -272,6 +273,252 @@ func TestGalleryQueryIsMissingScene(t *testing.T) {
}) })
} }
func queryGallery(t *testing.T, sqb models.GalleryReader, galleryFilter *models.GalleryFilterType, findFilter *models.FindFilterType) []*models.Gallery {
galleries, _, err := sqb.Query(galleryFilter, findFilter)
if err != nil {
t.Errorf("Error querying gallery: %s", err.Error())
}
return galleries
}
func TestGalleryQueryIsMissingStudio(t *testing.T) {
withTxn(func(r models.Repository) error {
sqb := r.Gallery()
isMissing := "studio"
galleryFilter := models.GalleryFilterType{
IsMissing: &isMissing,
}
q := getGalleryStringValue(galleryIdxWithStudio, titleField)
findFilter := models.FindFilterType{
Q: &q,
}
galleries := queryGallery(t, sqb, &galleryFilter, &findFilter)
assert.Len(t, galleries, 0)
findFilter.Q = nil
galleries = queryGallery(t, sqb, &galleryFilter, &findFilter)
// ensure non of the ids equal the one with studio
for _, gallery := range galleries {
assert.NotEqual(t, galleryIDs[galleryIdxWithStudio], gallery.ID)
}
return nil
})
}
func TestGalleryQueryIsMissingPerformers(t *testing.T) {
withTxn(func(r models.Repository) error {
sqb := r.Gallery()
isMissing := "performers"
galleryFilter := models.GalleryFilterType{
IsMissing: &isMissing,
}
q := getGalleryStringValue(galleryIdxWithPerformer, titleField)
findFilter := models.FindFilterType{
Q: &q,
}
galleries := queryGallery(t, sqb, &galleryFilter, &findFilter)
assert.Len(t, galleries, 0)
findFilter.Q = nil
galleries = queryGallery(t, sqb, &galleryFilter, &findFilter)
assert.True(t, len(galleries) > 0)
// ensure non of the ids equal the one with movies
for _, gallery := range galleries {
assert.NotEqual(t, galleryIDs[galleryIdxWithPerformer], gallery.ID)
}
return nil
})
}
func TestGalleryQueryIsMissingTags(t *testing.T) {
withTxn(func(r models.Repository) error {
sqb := r.Gallery()
isMissing := "tags"
galleryFilter := models.GalleryFilterType{
IsMissing: &isMissing,
}
q := getGalleryStringValue(galleryIdxWithTwoTags, titleField)
findFilter := models.FindFilterType{
Q: &q,
}
galleries := queryGallery(t, sqb, &galleryFilter, &findFilter)
assert.Len(t, galleries, 0)
findFilter.Q = nil
galleries = queryGallery(t, sqb, &galleryFilter, &findFilter)
assert.True(t, len(galleries) > 0)
return nil
})
}
func TestGalleryQueryPerformers(t *testing.T) {
withTxn(func(r models.Repository) error {
sqb := r.Gallery()
performerCriterion := models.MultiCriterionInput{
Value: []string{
strconv.Itoa(performerIDs[performerIdxWithGallery]),
strconv.Itoa(performerIDs[performerIdx1WithGallery]),
},
Modifier: models.CriterionModifierIncludes,
}
galleryFilter := models.GalleryFilterType{
Performers: &performerCriterion,
}
galleries := queryGallery(t, sqb, &galleryFilter, nil)
assert.Len(t, galleries, 2)
// ensure ids are correct
for _, gallery := range galleries {
assert.True(t, gallery.ID == galleryIDs[galleryIdxWithPerformer] || gallery.ID == galleryIDs[galleryIdxWithTwoPerformers])
}
performerCriterion = models.MultiCriterionInput{
Value: []string{
strconv.Itoa(performerIDs[performerIdx1WithGallery]),
strconv.Itoa(performerIDs[performerIdx2WithGallery]),
},
Modifier: models.CriterionModifierIncludesAll,
}
galleries = queryGallery(t, sqb, &galleryFilter, nil)
assert.Len(t, galleries, 1)
assert.Equal(t, galleryIDs[galleryIdxWithTwoPerformers], galleries[0].ID)
performerCriterion = models.MultiCriterionInput{
Value: []string{
strconv.Itoa(performerIDs[performerIdx1WithGallery]),
},
Modifier: models.CriterionModifierExcludes,
}
q := getGalleryStringValue(galleryIdxWithTwoPerformers, titleField)
findFilter := models.FindFilterType{
Q: &q,
}
galleries = queryGallery(t, sqb, &galleryFilter, &findFilter)
assert.Len(t, galleries, 0)
return nil
})
}
func TestGalleryQueryTags(t *testing.T) {
withTxn(func(r models.Repository) error {
sqb := r.Gallery()
tagCriterion := models.MultiCriterionInput{
Value: []string{
strconv.Itoa(tagIDs[tagIdxWithGallery]),
strconv.Itoa(tagIDs[tagIdx1WithGallery]),
},
Modifier: models.CriterionModifierIncludes,
}
galleryFilter := models.GalleryFilterType{
Tags: &tagCriterion,
}
galleries := queryGallery(t, sqb, &galleryFilter, nil)
assert.Len(t, galleries, 2)
// ensure ids are correct
for _, gallery := range galleries {
assert.True(t, gallery.ID == galleryIDs[galleryIdxWithTag] || gallery.ID == galleryIDs[galleryIdxWithTwoTags])
}
tagCriterion = models.MultiCriterionInput{
Value: []string{
strconv.Itoa(tagIDs[tagIdx1WithGallery]),
strconv.Itoa(tagIDs[tagIdx2WithGallery]),
},
Modifier: models.CriterionModifierIncludesAll,
}
galleries = queryGallery(t, sqb, &galleryFilter, nil)
assert.Len(t, galleries, 1)
assert.Equal(t, galleryIDs[galleryIdxWithTwoTags], galleries[0].ID)
tagCriterion = models.MultiCriterionInput{
Value: []string{
strconv.Itoa(tagIDs[tagIdx1WithGallery]),
},
Modifier: models.CriterionModifierExcludes,
}
q := getGalleryStringValue(galleryIdxWithTwoTags, titleField)
findFilter := models.FindFilterType{
Q: &q,
}
galleries = queryGallery(t, sqb, &galleryFilter, &findFilter)
assert.Len(t, galleries, 0)
return nil
})
}
func TestGalleryQueryStudio(t *testing.T) {
withTxn(func(r models.Repository) error {
sqb := r.Gallery()
studioCriterion := models.MultiCriterionInput{
Value: []string{
strconv.Itoa(studioIDs[studioIdxWithGallery]),
},
Modifier: models.CriterionModifierIncludes,
}
galleryFilter := models.GalleryFilterType{
Studios: &studioCriterion,
}
galleries := queryGallery(t, sqb, &galleryFilter, nil)
assert.Len(t, galleries, 1)
// ensure id is correct
assert.Equal(t, galleryIDs[galleryIdxWithStudio], galleries[0].ID)
studioCriterion = models.MultiCriterionInput{
Value: []string{
strconv.Itoa(studioIDs[studioIdxWithGallery]),
},
Modifier: models.CriterionModifierExcludes,
}
q := getGalleryStringValue(galleryIdxWithStudio, titleField)
findFilter := models.FindFilterType{
Q: &q,
}
galleries = queryGallery(t, sqb, &galleryFilter, &findFilter)
assert.Len(t, galleries, 0)
return nil
})
}
// TODO Count // TODO Count
// TODO All // TODO All
// TODO Query // TODO Query

View File

@@ -44,6 +44,14 @@ func TestPerformerFindBySceneID(t *testing.T) {
} }
func TestPerformerFindByNames(t *testing.T) { func TestPerformerFindByNames(t *testing.T) {
getNames := func(p []*models.Performer) []string {
var ret []string
for _, pp := range p {
ret = append(ret, pp.Name.String)
}
return ret
}
withTxn(func(r models.Repository) error { withTxn(func(r models.Repository) error {
var names []string var names []string
@@ -72,19 +80,20 @@ func TestPerformerFindByNames(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("Error finding performers: %s", err.Error()) t.Errorf("Error finding performers: %s", err.Error())
} }
assert.Len(t, performers, 2) // performerIdxWithScene and performerIdx1WithScene retNames := getNames(performers)
assert.Equal(t, performerNames[performerIdxWithScene], performers[0].Name.String) assert.Equal(t, names, retNames)
assert.Equal(t, performerNames[performerIdx1WithScene], performers[1].Name.String)
performers, err = pqb.FindByNames(names, true) // find performers by names ( 2 names nocase) performers, err = pqb.FindByNames(names, true) // find performers by names ( 2 names nocase)
if err != nil { if err != nil {
t.Errorf("Error finding performers: %s", err.Error()) t.Errorf("Error finding performers: %s", err.Error())
} }
assert.Len(t, performers, 4) // performerIdxWithScene and performerIdxWithDupName , performerIdx1WithScene and performerIdx1WithDupName retNames = getNames(performers)
assert.Equal(t, performerNames[performerIdxWithScene], performers[0].Name.String) assert.Equal(t, []string{
assert.Equal(t, performerNames[performerIdx1WithScene], performers[1].Name.String) performerNames[performerIdxWithScene],
assert.Equal(t, performerNames[performerIdx1WithDupName], performers[2].Name.String) performerNames[performerIdx1WithScene],
assert.Equal(t, performerNames[performerIdxWithDupName], performers[3].Name.String) performerNames[performerIdx1WithDupName],
performerNames[performerIdxWithDupName],
}, retNames)
return nil return nil
}) })

View File

@@ -22,14 +22,14 @@ import (
const totalScenes = 12 const totalScenes = 12
const totalImages = 6 // TODO - add one for zip file const totalImages = 6 // TODO - add one for zip file
const performersNameCase = 6 const performersNameCase = 9
const performersNameNoCase = 2 const performersNameNoCase = 2
const moviesNameCase = 2 const moviesNameCase = 2
const moviesNameNoCase = 1 const moviesNameNoCase = 1
const totalGalleries = 3 const totalGalleries = 8
const tagsNameNoCase = 2 const tagsNameNoCase = 2
const tagsNameCase = 9 const tagsNameCase = 12
const studiosNameCase = 5 const studiosNameCase = 6
const studiosNameNoCase = 1 const studiosNameNoCase = 1
var sceneIDs []int var sceneIDs []int
@@ -69,10 +69,13 @@ const performerIdx2WithScene = 2
const performerIdxWithImage = 3 const performerIdxWithImage = 3
const performerIdx1WithImage = 4 const performerIdx1WithImage = 4
const performerIdx2WithImage = 5 const performerIdx2WithImage = 5
const performerIdxWithGallery = 6
const performerIdx1WithGallery = 7
const performerIdx2WithGallery = 8
// performers with dup names start from the end // performers with dup names start from the end
const performerIdx1WithDupName = 6 const performerIdx1WithDupName = 9
const performerIdxWithDupName = 7 const performerIdxWithDupName = 10
const movieIdxWithScene = 0 const movieIdxWithScene = 0
const movieIdxWithStudio = 1 const movieIdxWithStudio = 1
@@ -82,6 +85,11 @@ const movieIdxWithDupName = 2
const galleryIdxWithScene = 0 const galleryIdxWithScene = 0
const galleryIdxWithImage = 1 const galleryIdxWithImage = 1
const galleryIdxWithPerformer = 2
const galleryIdxWithTwoPerformers = 3
const galleryIdxWithTag = 4
const galleryIdxWithTwoTags = 5
const galleryIdxWithStudio = 6
const tagIdxWithScene = 0 const tagIdxWithScene = 0
const tagIdx1WithScene = 1 const tagIdx1WithScene = 1
@@ -92,19 +100,23 @@ const tagIdxWithCoverImage = 5
const tagIdxWithImage = 6 const tagIdxWithImage = 6
const tagIdx1WithImage = 7 const tagIdx1WithImage = 7
const tagIdx2WithImage = 8 const tagIdx2WithImage = 8
const tagIdxWithGallery = 9
const tagIdx1WithGallery = 10
const tagIdx2WithGallery = 11
// tags with dup names start from the end // tags with dup names start from the end
const tagIdx1WithDupName = 9 const tagIdx1WithDupName = 12
const tagIdxWithDupName = 10 const tagIdxWithDupName = 13
const studioIdxWithScene = 0 const studioIdxWithScene = 0
const studioIdxWithMovie = 1 const studioIdxWithMovie = 1
const studioIdxWithChildStudio = 2 const studioIdxWithChildStudio = 2
const studioIdxWithParentStudio = 3 const studioIdxWithParentStudio = 3
const studioIdxWithImage = 4 const studioIdxWithImage = 4
const studioIdxWithGallery = 5
// studios with dup names start from the end // studios with dup names start from the end
const studioIdxWithDupName = 5 const studioIdxWithDupName = 6
const markerIdxWithScene = 0 const markerIdxWithScene = 0
@@ -237,6 +249,18 @@ func populateDB() error {
return fmt.Errorf("error linking studio parent: %s", err.Error()) return fmt.Errorf("error linking studio parent: %s", err.Error())
} }
if err := linkGalleryPerformers(r.Gallery()); err != nil {
return fmt.Errorf("error linking gallery performers: %s", err.Error())
}
if err := linkGalleryTags(r.Gallery()); err != nil {
return fmt.Errorf("error linking gallery tags: %s", err.Error())
}
if err := linkGalleryStudio(r.Gallery(), galleryIdxWithStudio, studioIdxWithGallery); err != nil {
return fmt.Errorf("error linking gallery studio: %s", err.Error())
}
if err := createMarker(r.SceneMarker(), sceneIdxWithMarker, tagIdxWithPrimaryMarker, []int{tagIdxWithMarker}); err != nil { if err := createMarker(r.SceneMarker(), sceneIdxWithMarker, tagIdxWithPrimaryMarker, []int{tagIdxWithMarker}); err != nil {
return fmt.Errorf("error creating scene marker: %s", err.Error()) return fmt.Errorf("error creating scene marker: %s", err.Error())
} }
@@ -756,3 +780,65 @@ func linkStudioParent(qb models.StudioWriter, parentIndex, childIndex int) error
func addTagImage(qb models.TagWriter, tagIndex int) error { func addTagImage(qb models.TagWriter, tagIndex int) error {
return qb.UpdateImage(tagIDs[tagIndex], models.DefaultTagImage) return qb.UpdateImage(tagIDs[tagIndex], models.DefaultTagImage)
} }
func linkGalleryTags(iqb models.GalleryReaderWriter) error {
if err := linkGalleryTag(iqb, galleryIdxWithTag, tagIdxWithGallery); err != nil {
return err
}
if err := linkGalleryTag(iqb, galleryIdxWithTwoTags, tagIdx1WithGallery); err != nil {
return err
}
if err := linkGalleryTag(iqb, galleryIdxWithTwoTags, tagIdx2WithGallery); err != nil {
return err
}
return nil
}
func linkGalleryTag(iqb models.GalleryReaderWriter, galleryIndex, tagIndex int) error {
galleryID := galleryIDs[galleryIndex]
tags, err := iqb.GetTagIDs(galleryID)
if err != nil {
return err
}
tags = append(tags, tagIDs[tagIndex])
return iqb.UpdateTags(galleryID, tags)
}
func linkGalleryStudio(qb models.GalleryWriter, galleryIndex, studioIndex int) error {
gallery := models.GalleryPartial{
ID: galleryIDs[galleryIndex],
StudioID: &sql.NullInt64{Int64: int64(studioIDs[studioIndex]), Valid: true},
}
_, err := qb.UpdatePartial(gallery)
return err
}
func linkGalleryPerformers(qb models.GalleryReaderWriter) error {
if err := linkGalleryPerformer(qb, galleryIdxWithPerformer, performerIdxWithGallery); err != nil {
return err
}
if err := linkGalleryPerformer(qb, galleryIdxWithTwoPerformers, performerIdx1WithGallery); err != nil {
return err
}
if err := linkGalleryPerformer(qb, galleryIdxWithTwoPerformers, performerIdx2WithGallery); err != nil {
return err
}
return nil
}
func linkGalleryPerformer(iqb models.GalleryReaderWriter, galleryIndex, performerIndex int) error {
galleryID := galleryIDs[galleryIndex]
performers, err := iqb.GetPerformerIDs(galleryID)
if err != nil {
return err
}
performers = append(performers, performerIDs[performerIndex])
return iqb.UpdatePerformers(galleryID, performers)
}

View File

@@ -8,6 +8,7 @@
* Added Rescan button to scene, image, gallery details overflow button. * Added Rescan button to scene, image, gallery details overflow button.
### 🐛 Bug fixes ### 🐛 Bug fixes
* Fix SQL error when filtering galleries excluding performers or tags.
* Fix version checking for armv7 and arm64. * Fix version checking for armv7 and arm64.
* Change "Is NULL" filter to include empty string values. * Change "Is NULL" filter to include empty string values.
* Prevent scene card previews playing in full-screen on iOS devices. * Prevent scene card previews playing in full-screen on iOS devices.