Add studio *_count filters and sort options (#1307)

This commit is contained in:
peolic
2021-04-20 09:48:36 +03:00
committed by GitHub
parent 1759a99f65
commit 9200f167bf
6 changed files with 237 additions and 22 deletions

View File

@@ -149,6 +149,12 @@ input StudioFilterType {
stash_id: String
"""Filter to only include studios missing this property"""
is_missing: String
"""Filter by scene count"""
scene_count: IntCriterionInput
"""Filter by image count"""
image_count: IntCriterionInput
"""Filter by gallery count"""
gallery_count: IntCriterionInput
"""Filter by url"""
url: StringCriterionInput
}

View File

@@ -126,7 +126,7 @@ func TestImageQueryPath(t *testing.T) {
verifyImagePath(t, pathCriterion, totalImages-1)
pathCriterion.Modifier = models.CriterionModifierMatchesRegex
pathCriterion.Value = "image_.*1_Path"
pathCriterion.Value = "image_.*01_Path"
verifyImagePath(t, pathCriterion, 1) // TODO - 2 if zip path is included
pathCriterion.Modifier = models.CriterionModifierNotMatchesRegex

View File

@@ -34,6 +34,8 @@ const (
sceneIdxWithTag
sceneIdxWithTwoTags
sceneIdxWithStudio
sceneIdx1WithStudio
sceneIdx2WithStudio
sceneIdxWithMarker
sceneIdxWithPerformerTag
sceneIdxWithPerformerTwoTags
@@ -53,6 +55,8 @@ const (
imageIdxWithTag
imageIdxWithTwoTags
imageIdxWithStudio
imageIdx1WithStudio
imageIdx2WithStudio
imageIdxInZip // TODO - not implemented
imageIdxWithPerformerTag
imageIdxWithPerformerTwoTags
@@ -105,6 +109,8 @@ const (
galleryIdxWithTag
galleryIdxWithTwoTags
galleryIdxWithStudio
galleryIdx1WithStudio
galleryIdx2WithStudio
galleryIdxWithPerformerTag
galleryIdxWithPerformerTwoTags
// new indexes above
@@ -140,11 +146,14 @@ const (
const (
studioIdxWithScene = iota
studioIdxWithTwoScenes
studioIdxWithMovie
studioIdxWithChildStudio
studioIdxWithParentStudio
studioIdxWithImage
studioIdxWithTwoImages
studioIdxWithGallery
studioIdxWithTwoGalleries
// new indexes above
// studios with dup names start from the end
studioIdxWithDupName
@@ -213,6 +222,8 @@ var (
sceneStudioLinks = [][2]int{
{sceneIdxWithStudio, studioIdxWithScene},
{sceneIdx1WithStudio, studioIdxWithTwoScenes},
{sceneIdx2WithStudio, studioIdxWithTwoScenes},
}
)
@@ -222,6 +233,8 @@ var (
}
imageStudioLinks = [][2]int{
{imageIdxWithStudio, studioIdxWithImage},
{imageIdx1WithStudio, studioIdxWithTwoImages},
{imageIdx2WithStudio, studioIdxWithTwoImages},
}
imageTagLinks = [][2]int{
{imageIdxWithTag, tagIdxWithImage},
@@ -250,6 +263,12 @@ var (
{galleryIdx2WithPerformer, performerIdxWithTwoGalleries},
}
galleryStudioLinks = [][2]int{
{galleryIdxWithStudio, studioIdxWithGallery},
{galleryIdx1WithStudio, studioIdxWithTwoGalleries},
{galleryIdx2WithStudio, studioIdxWithTwoGalleries},
}
galleryTagLinks = [][2]int{
{galleryIdxWithTag, tagIdxWithGallery},
{galleryIdxWithTwoTags, tagIdx1WithGallery},
@@ -413,8 +432,8 @@ func populateDB() error {
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 := linkGalleryStudios(r.Gallery()); err != nil {
return fmt.Errorf("error linking gallery studios: %s", err.Error())
}
if err := createMarker(r.SceneMarker(), sceneIdxWithMarker, tagIdxWithPrimaryMarker, []int{tagIdxWithMarker}); err != nil {
@@ -1017,7 +1036,7 @@ func linkImagePerformers(qb models.ImageReaderWriter) error {
func linkGalleryPerformers(qb models.GalleryReaderWriter) error {
return doLinks(galleryPerformerLinks, func(galleryIndex, performerIndex int) error {
galleryID := imageIDs[galleryIndex]
galleryID := galleryIDs[galleryIndex]
performers, err := qb.GetPerformerIDs(galleryID)
if err != nil {
return err
@@ -1029,17 +1048,29 @@ func linkGalleryPerformers(qb models.GalleryReaderWriter) error {
})
}
func linkGalleryTags(iqb models.GalleryReaderWriter) error {
func linkGalleryStudios(qb models.GalleryReaderWriter) error {
return doLinks(galleryStudioLinks, func(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 linkGalleryTags(qb models.GalleryReaderWriter) error {
return doLinks(galleryTagLinks, func(galleryIndex, tagIndex int) error {
galleryID := imageIDs[galleryIndex]
tags, err := iqb.GetTagIDs(galleryID)
galleryID := galleryIDs[galleryIndex]
tags, err := qb.GetTagIDs(galleryID)
if err != nil {
return err
}
tags = append(tags, tagIDs[tagIndex])
return iqb.UpdateTags(galleryID, tags)
return qb.UpdateTags(galleryID, tags)
})
}
@@ -1070,13 +1101,3 @@ func linkStudiosParent(qb models.StudioWriter) error {
func addTagImage(qb models.TagWriter, tagIndex int) error {
return qb.UpdateImage(tagIDs[tagIndex], models.DefaultTagImage)
}
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
}

View File

@@ -165,6 +165,10 @@ func (qb *studioQueryBuilder) Query(studioFilter *models.StudioFilterType, findF
query.addArg(stashIDFilter)
}
query.handleCountCriterion(studioFilter.SceneCount, studioTable, sceneTable, studioIDColumn)
query.handleCountCriterion(studioFilter.ImageCount, studioTable, imageTable, studioIDColumn)
query.handleCountCriterion(studioFilter.GalleryCount, studioTable, galleryTable, studioIDColumn)
query.handleStringCriterionInput(studioFilter.URL, "studios.url")
if isMissingFilter := studioFilter.IsMissing; isMissingFilter != nil && *isMissingFilter != "" {
@@ -209,7 +213,15 @@ func (qb *studioQueryBuilder) getStudioSort(findFilter *models.FindFilterType) s
sort = findFilter.GetSort("name")
direction = findFilter.GetDirection()
}
return getSort(sort, direction, "studios")
switch sort {
case "images_count":
return getCountSort(studioTable, imageTable, studioIDColumn, direction)
case "galleries_count":
return getCountSort(studioTable, galleryTable, studioIDColumn, direction)
default:
return getSort(sort, direction, "studios")
}
}
func (qb *studioQueryBuilder) queryStudio(query string, args []interface{}) (*models.Studio, error) {

View File

@@ -265,6 +265,147 @@ func TestStudioDestroyStudioImage(t *testing.T) {
}
}
func TestStudioQuerySceneCount(t *testing.T) {
const sceneCount = 1
sceneCountCriterion := models.IntCriterionInput{
Value: sceneCount,
Modifier: models.CriterionModifierEquals,
}
verifyStudiosSceneCount(t, sceneCountCriterion)
sceneCountCriterion.Modifier = models.CriterionModifierNotEquals
verifyStudiosSceneCount(t, sceneCountCriterion)
sceneCountCriterion.Modifier = models.CriterionModifierGreaterThan
verifyStudiosSceneCount(t, sceneCountCriterion)
sceneCountCriterion.Modifier = models.CriterionModifierLessThan
verifyStudiosSceneCount(t, sceneCountCriterion)
}
func verifyStudiosSceneCount(t *testing.T, sceneCountCriterion models.IntCriterionInput) {
withTxn(func(r models.Repository) error {
sqb := r.Studio()
studioFilter := models.StudioFilterType{
SceneCount: &sceneCountCriterion,
}
studios := queryStudio(t, sqb, &studioFilter, nil)
assert.Greater(t, len(studios), 0)
for _, studio := range studios {
sceneCount, err := r.Scene().CountByStudioID(studio.ID)
if err != nil {
return err
}
verifyInt(t, sceneCount, sceneCountCriterion)
}
return nil
})
}
func TestStudioQueryImageCount(t *testing.T) {
const imageCount = 1
imageCountCriterion := models.IntCriterionInput{
Value: imageCount,
Modifier: models.CriterionModifierEquals,
}
verifyStudiosImageCount(t, imageCountCriterion)
imageCountCriterion.Modifier = models.CriterionModifierNotEquals
verifyStudiosImageCount(t, imageCountCriterion)
imageCountCriterion.Modifier = models.CriterionModifierGreaterThan
verifyStudiosImageCount(t, imageCountCriterion)
imageCountCriterion.Modifier = models.CriterionModifierLessThan
verifyStudiosImageCount(t, imageCountCriterion)
}
func verifyStudiosImageCount(t *testing.T, imageCountCriterion models.IntCriterionInput) {
withTxn(func(r models.Repository) error {
sqb := r.Studio()
studioFilter := models.StudioFilterType{
ImageCount: &imageCountCriterion,
}
studios := queryStudio(t, sqb, &studioFilter, nil)
assert.Greater(t, len(studios), 0)
for _, studio := range studios {
pp := 0
_, count, err := r.Image().Query(&models.ImageFilterType{
Studios: &models.MultiCriterionInput{
Value: []string{strconv.Itoa(studio.ID)},
Modifier: models.CriterionModifierIncludes,
},
}, &models.FindFilterType{
PerPage: &pp,
})
if err != nil {
return err
}
verifyInt(t, count, imageCountCriterion)
}
return nil
})
}
func TestStudioQueryGalleryCount(t *testing.T) {
const galleryCount = 1
galleryCountCriterion := models.IntCriterionInput{
Value: galleryCount,
Modifier: models.CriterionModifierEquals,
}
verifyStudiosGalleryCount(t, galleryCountCriterion)
galleryCountCriterion.Modifier = models.CriterionModifierNotEquals
verifyStudiosGalleryCount(t, galleryCountCriterion)
galleryCountCriterion.Modifier = models.CriterionModifierGreaterThan
verifyStudiosGalleryCount(t, galleryCountCriterion)
galleryCountCriterion.Modifier = models.CriterionModifierLessThan
verifyStudiosGalleryCount(t, galleryCountCriterion)
}
func verifyStudiosGalleryCount(t *testing.T, galleryCountCriterion models.IntCriterionInput) {
withTxn(func(r models.Repository) error {
sqb := r.Studio()
studioFilter := models.StudioFilterType{
GalleryCount: &galleryCountCriterion,
}
studios := queryStudio(t, sqb, &studioFilter, nil)
assert.Greater(t, len(studios), 0)
for _, studio := range studios {
pp := 0
_, count, err := r.Gallery().Query(&models.GalleryFilterType{
Studios: &models.MultiCriterionInput{
Value: []string{strconv.Itoa(studio.ID)},
Modifier: models.CriterionModifierIncludes,
},
}, &models.FindFilterType{
PerPage: &pp,
})
if err != nil {
return err
}
verifyInt(t, count, galleryCountCriterion)
}
return nil
})
}
func TestStudioStashIDs(t *testing.T) {
if err := withTxn(func(r models.Repository) error {
qb := r.Studio()

View File

@@ -240,12 +240,21 @@ export class ListFilterModel {
}
case FilterMode.Studios:
this.sortBy = "name";
this.sortByOptions = ["name", "scenes_count", "random"];
this.sortByOptions = [
"name",
"scenes_count",
"images_count",
"galleries_count",
"random",
];
this.displayModeOptions = [DisplayMode.Grid];
this.criterionOptions = [
new NoneCriterionOption(),
new ParentStudiosCriterionOption(),
new StudioIsMissingCriterionOption(),
ListFilterModel.createCriterionOption("scene_count"),
ListFilterModel.createCriterionOption("image_count"),
ListFilterModel.createCriterionOption("gallery_count"),
ListFilterModel.createCriterionOption("url"),
];
break;
@@ -1034,8 +1043,34 @@ export class ListFilterModel {
};
break;
}
case "studioIsMissing":
case "studioIsMissing": {
result.is_missing = (criterion as IsMissingCriterion).value;
break;
}
case "scene_count": {
const countCrit = criterion as NumberCriterion;
result.scene_count = {
value: countCrit.value,
modifier: countCrit.modifier,
};
break;
}
case "image_count": {
const countCrit = criterion as NumberCriterion;
result.image_count = {
value: countCrit.value,
modifier: countCrit.modifier,
};
break;
}
case "gallery_count": {
const countCrit = criterion as NumberCriterion;
result.gallery_count = {
value: countCrit.value,
modifier: countCrit.modifier,
};
break;
}
// no default
}
});