mirror of
https://github.com/stashapp/stash.git
synced 2025-12-16 20:07:05 +03:00
Add tag stash ids filter criterion (#6403)
* Add stash id filter to tag filter * Add tag stash id criterion in UI
This commit is contained in:
@@ -606,6 +606,9 @@ input TagFilterType {
|
|||||||
"Filter by autotag ignore value"
|
"Filter by autotag ignore value"
|
||||||
ignore_auto_tag: Boolean
|
ignore_auto_tag: Boolean
|
||||||
|
|
||||||
|
"Filter by StashID"
|
||||||
|
stash_id_endpoint: StashIDCriterionInput
|
||||||
|
|
||||||
"Filter by related scenes that meet this criteria"
|
"Filter by related scenes that meet this criteria"
|
||||||
scenes_filter: SceneFilterType
|
scenes_filter: SceneFilterType
|
||||||
"Filter by related images that meet this criteria"
|
"Filter by related images that meet this criteria"
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ type TagFilterType struct {
|
|||||||
ChildCount *IntCriterionInput `json:"child_count"`
|
ChildCount *IntCriterionInput `json:"child_count"`
|
||||||
// Filter by autotag ignore value
|
// Filter by autotag ignore value
|
||||||
IgnoreAutoTag *bool `json:"ignore_auto_tag"`
|
IgnoreAutoTag *bool `json:"ignore_auto_tag"`
|
||||||
|
// Filter by StashID Endpoint
|
||||||
|
StashIDEndpoint *StashIDCriterionInput `json:"stash_id_endpoint"`
|
||||||
// Filter by related scenes that meet this criteria
|
// Filter by related scenes that meet this criteria
|
||||||
ScenesFilter *SceneFilterType `json:"scenes_filter"`
|
ScenesFilter *SceneFilterType `json:"scenes_filter"`
|
||||||
// Filter by related images that meet this criteria
|
// Filter by related images that meet this criteria
|
||||||
|
|||||||
@@ -1688,6 +1688,13 @@ func getTagChildCount(id int) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tagStashID(i int) models.StashID {
|
||||||
|
return models.StashID{
|
||||||
|
StashID: getTagStringValue(i, "stashid"),
|
||||||
|
Endpoint: getTagStringValue(i, "endpoint"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// createTags creates n tags with plain Name and o tags with camel cased NaMe included
|
// createTags creates n tags with plain Name and o tags with camel cased NaMe included
|
||||||
func createTags(ctx context.Context, tqb models.TagReaderWriter, n int, o int) error {
|
func createTags(ctx context.Context, tqb models.TagReaderWriter, n int, o int) error {
|
||||||
const namePlain = "Name"
|
const namePlain = "Name"
|
||||||
@@ -1709,6 +1716,12 @@ func createTags(ctx context.Context, tqb models.TagReaderWriter, n int, o int) e
|
|||||||
IgnoreAutoTag: getIgnoreAutoTag(i),
|
IgnoreAutoTag: getIgnoreAutoTag(i),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (index+1)%5 != 0 {
|
||||||
|
tag.StashIDs = models.NewRelatedStashIDs([]models.StashID{
|
||||||
|
tagStashID(i),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
err := tqb.Create(ctx, &tag)
|
err := tqb.Create(ctx, &tag)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -84,6 +84,14 @@ func (qb *tagFilterHandler) criterionHandler() criterionHandler {
|
|||||||
tagHierarchyHandler.ChildrenCriterionHandler(tagFilter.Children),
|
tagHierarchyHandler.ChildrenCriterionHandler(tagFilter.Children),
|
||||||
tagHierarchyHandler.ParentCountCriterionHandler(tagFilter.ParentCount),
|
tagHierarchyHandler.ParentCountCriterionHandler(tagFilter.ParentCount),
|
||||||
tagHierarchyHandler.ChildCountCriterionHandler(tagFilter.ChildCount),
|
tagHierarchyHandler.ChildCountCriterionHandler(tagFilter.ChildCount),
|
||||||
|
|
||||||
|
&stashIDCriterionHandler{
|
||||||
|
c: tagFilter.StashIDEndpoint,
|
||||||
|
stashIDRepository: &tagRepository.stashIDs,
|
||||||
|
stashIDTableAs: "tag_stash_ids",
|
||||||
|
parentIDCol: "tags.id",
|
||||||
|
},
|
||||||
|
|
||||||
×tampCriterionHandler{tagFilter.CreatedAt, "tags.created_at", nil},
|
×tampCriterionHandler{tagFilter.CreatedAt, "tags.created_at", nil},
|
||||||
×tampCriterionHandler{tagFilter.UpdatedAt, "tags.updated_at", nil},
|
×tampCriterionHandler{tagFilter.UpdatedAt, "tags.updated_at", nil},
|
||||||
|
|
||||||
|
|||||||
@@ -343,6 +343,109 @@ func queryTags(ctx context.Context, t *testing.T, qb models.TagReader, tagFilter
|
|||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tagsToIDs(i []*models.Tag) []int {
|
||||||
|
ret := make([]int, len(i))
|
||||||
|
for i, v := range i {
|
||||||
|
ret[i] = v.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTagQuery(t *testing.T) {
|
||||||
|
var (
|
||||||
|
endpoint = tagStashID(tagIdxWithPerformer).Endpoint
|
||||||
|
stashID = tagStashID(tagIdxWithPerformer).StashID
|
||||||
|
)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
findFilter *models.FindFilterType
|
||||||
|
filter *models.TagFilterType
|
||||||
|
includeIdxs []int
|
||||||
|
excludeIdxs []int
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"stash id with endpoint",
|
||||||
|
nil,
|
||||||
|
&models.TagFilterType{
|
||||||
|
StashIDEndpoint: &models.StashIDCriterionInput{
|
||||||
|
Endpoint: &endpoint,
|
||||||
|
StashID: &stashID,
|
||||||
|
Modifier: models.CriterionModifierEquals,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]int{tagIdxWithPerformer},
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"exclude stash id with endpoint",
|
||||||
|
nil,
|
||||||
|
&models.TagFilterType{
|
||||||
|
StashIDEndpoint: &models.StashIDCriterionInput{
|
||||||
|
Endpoint: &endpoint,
|
||||||
|
StashID: &stashID,
|
||||||
|
Modifier: models.CriterionModifierNotEquals,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
[]int{tagIdxWithPerformer},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null stash id with endpoint",
|
||||||
|
nil,
|
||||||
|
&models.TagFilterType{
|
||||||
|
StashIDEndpoint: &models.StashIDCriterionInput{
|
||||||
|
Endpoint: &endpoint,
|
||||||
|
Modifier: models.CriterionModifierIsNull,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
[]int{tagIdxWithPerformer},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"not null stash id with endpoint",
|
||||||
|
nil,
|
||||||
|
&models.TagFilterType{
|
||||||
|
StashIDEndpoint: &models.StashIDCriterionInput{
|
||||||
|
Endpoint: &endpoint,
|
||||||
|
Modifier: models.CriterionModifierNotNull,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]int{tagIdxWithPerformer},
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
runWithRollbackTxn(t, tt.name, func(t *testing.T, ctx context.Context) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
tags, _, err := db.Tag.Query(ctx, tt.filter, tt.findFilter)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("PerformerStore.Query() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ids := tagsToIDs(tags)
|
||||||
|
include := indexesToIDs(tagIDs, tt.includeIdxs)
|
||||||
|
exclude := indexesToIDs(tagIDs, tt.excludeIdxs)
|
||||||
|
|
||||||
|
for _, i := range include {
|
||||||
|
assert.Contains(ids, i)
|
||||||
|
}
|
||||||
|
for _, e := range exclude {
|
||||||
|
assert.NotContains(ids, e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTagQueryIsMissingImage(t *testing.T) {
|
func TestTagQueryIsMissingImage(t *testing.T) {
|
||||||
withTxn(func(ctx context.Context) error {
|
withTxn(func(ctx context.Context) error {
|
||||||
qb := db.Tag
|
qb := db.Tag
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
ParentTagsCriterionOption,
|
ParentTagsCriterionOption,
|
||||||
} from "./criteria/tags";
|
} from "./criteria/tags";
|
||||||
import { FavoriteTagCriterionOption } from "./criteria/favorite";
|
import { FavoriteTagCriterionOption } from "./criteria/favorite";
|
||||||
|
import { StashIDCriterionOption } from "./criteria/stash-ids";
|
||||||
|
|
||||||
const defaultSortBy = "name";
|
const defaultSortBy = "name";
|
||||||
const sortByOptions = ["name", "random", "scenes_duration"]
|
const sortByOptions = ["name", "random", "scenes_duration"]
|
||||||
@@ -58,6 +59,7 @@ const criterionOptions = [
|
|||||||
createStringCriterionOption("aliases"),
|
createStringCriterionOption("aliases"),
|
||||||
createStringCriterionOption("description"),
|
createStringCriterionOption("description"),
|
||||||
createBooleanCriterionOption("ignore_auto_tag"),
|
createBooleanCriterionOption("ignore_auto_tag"),
|
||||||
|
StashIDCriterionOption,
|
||||||
createMandatoryNumberCriterionOption("scene_count"),
|
createMandatoryNumberCriterionOption("scene_count"),
|
||||||
createMandatoryNumberCriterionOption("image_count"),
|
createMandatoryNumberCriterionOption("image_count"),
|
||||||
createMandatoryNumberCriterionOption("gallery_count"),
|
createMandatoryNumberCriterionOption("gallery_count"),
|
||||||
|
|||||||
Reference in New Issue
Block a user