mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
Feature: Tag StashID support (#6255)
This commit is contained in:
@@ -102,6 +102,7 @@ func (db *Anonymiser) deleteStashIDs() error {
|
||||
func() error { return db.truncateTable("scene_stash_ids") },
|
||||
func() error { return db.truncateTable("studio_stash_ids") },
|
||||
func() error { return db.truncateTable("performer_stash_ids") },
|
||||
func() error { return db.truncateTable("tag_stash_ids") },
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ const (
|
||||
cacheSizeEnv = "STASH_SQLITE_CACHE_SIZE"
|
||||
)
|
||||
|
||||
var appSchemaVersion uint = 73
|
||||
var appSchemaVersion uint = 74
|
||||
|
||||
//go:embed migrations/*.sql
|
||||
var migrationsBox embed.FS
|
||||
|
||||
7
pkg/sqlite/migrations/74_tag_stash_ids.up.sql
Normal file
7
pkg/sqlite/migrations/74_tag_stash_ids.up.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE `tag_stash_ids` (
|
||||
`tag_id` integer,
|
||||
`endpoint` varchar(255),
|
||||
`stash_id` varchar(36),
|
||||
`updated_at` datetime not null default '1970-01-01T00:00:00Z',
|
||||
foreign key(`tag_id`) references `tags`(`id`) on delete CASCADE
|
||||
);
|
||||
@@ -27,8 +27,9 @@ func testStashIDReaderWriter(ctx context.Context, t *testing.T, r stashIDReaderW
|
||||
const stashIDStr = "stashID"
|
||||
const endpoint = "endpoint"
|
||||
stashID := models.StashID{
|
||||
StashID: stashIDStr,
|
||||
Endpoint: endpoint,
|
||||
StashID: stashIDStr,
|
||||
Endpoint: endpoint,
|
||||
UpdatedAt: epochTime,
|
||||
}
|
||||
|
||||
// update stash ids and ensure was updated
|
||||
|
||||
@@ -47,6 +47,7 @@ var (
|
||||
|
||||
tagsAliasesJoinTable = goqu.T(tagAliasesTable)
|
||||
tagRelationsJoinTable = goqu.T(tagRelationsTable)
|
||||
tagsStashIDsJoinTable = goqu.T("tag_stash_ids")
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -375,6 +376,13 @@ var (
|
||||
}
|
||||
|
||||
tagsChildTagsTableMgr = *tagsParentTagsTableMgr.invert()
|
||||
|
||||
tagsStashIDsTableMgr = &stashIDTable{
|
||||
table: table{
|
||||
table: tagsStashIDsJoinTable,
|
||||
idColumn: tagsStashIDsJoinTable.Col(tagIDColumn),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -101,7 +101,8 @@ func (r *tagRowRecord) fromPartial(o models.TagPartial) {
|
||||
type tagRepositoryType struct {
|
||||
repository
|
||||
|
||||
aliases stringRepository
|
||||
aliases stringRepository
|
||||
stashIDs stashIDRepository
|
||||
|
||||
scenes joinRepository
|
||||
images joinRepository
|
||||
@@ -121,6 +122,12 @@ var (
|
||||
},
|
||||
stringColumn: tagAliasColumn,
|
||||
},
|
||||
stashIDs: stashIDRepository{
|
||||
repository{
|
||||
tableName: "tag_stash_ids",
|
||||
idColumn: tagIDColumn,
|
||||
},
|
||||
},
|
||||
scenes: joinRepository{
|
||||
repository: repository{
|
||||
tableName: scenesTagsTable,
|
||||
@@ -199,6 +206,12 @@ func (qb *TagStore) Create(ctx context.Context, newObject *models.Tag) error {
|
||||
}
|
||||
}
|
||||
|
||||
if newObject.StashIDs.Loaded() {
|
||||
if err := tagsStashIDsTableMgr.insertJoins(ctx, id, newObject.StashIDs.List()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
updated, err := qb.find(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("finding after create: %w", err)
|
||||
@@ -242,6 +255,12 @@ func (qb *TagStore) UpdatePartial(ctx context.Context, id int, partial models.Ta
|
||||
}
|
||||
}
|
||||
|
||||
if partial.StashIDs != nil {
|
||||
if err := tagsStashIDsTableMgr.modifyJoins(ctx, id, partial.StashIDs.StashIDs, partial.StashIDs.Mode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return qb.find(ctx, id)
|
||||
}
|
||||
|
||||
@@ -271,6 +290,12 @@ func (qb *TagStore) Update(ctx context.Context, updatedObject *models.Tag) error
|
||||
}
|
||||
}
|
||||
|
||||
if updatedObject.StashIDs.Loaded() {
|
||||
if err := tagsStashIDsTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.StashIDs.List()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -509,6 +534,24 @@ func (qb *TagStore) FindByNames(ctx context.Context, names []string, nocase bool
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (qb *TagStore) FindByStashID(ctx context.Context, stashID models.StashID) ([]*models.Tag, error) {
|
||||
sq := dialect.From(tagsStashIDsJoinTable).Select(tagsStashIDsJoinTable.Col(tagIDColumn)).Where(
|
||||
tagsStashIDsJoinTable.Col("stash_id").Eq(stashID.StashID),
|
||||
tagsStashIDsJoinTable.Col("endpoint").Eq(stashID.Endpoint),
|
||||
)
|
||||
|
||||
idsQuery := qb.selectDataset().Where(
|
||||
qb.table().Col(idColumn).In(sq),
|
||||
)
|
||||
|
||||
ret, err := qb.getMany(ctx, idsQuery)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting tags for stash ID %s: %w", stashID.StashID, err)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (qb *TagStore) GetParentIDs(ctx context.Context, relatedID int) ([]int, error) {
|
||||
return tagsParentTagsTableMgr.get(ctx, relatedID)
|
||||
}
|
||||
@@ -779,6 +822,14 @@ func (qb *TagStore) UpdateAliases(ctx context.Context, tagID int, aliases []stri
|
||||
return tagRepository.aliases.replace(ctx, tagID, aliases)
|
||||
}
|
||||
|
||||
func (qb *TagStore) GetStashIDs(ctx context.Context, tagID int) ([]models.StashID, error) {
|
||||
return tagsStashIDsTableMgr.get(ctx, tagID)
|
||||
}
|
||||
|
||||
func (qb *TagStore) UpdateStashIDs(ctx context.Context, tagID int, stashIDs []models.StashID) error {
|
||||
return tagsStashIDsTableMgr.replaceJoins(ctx, tagID, stashIDs)
|
||||
}
|
||||
|
||||
func (qb *TagStore) Merge(ctx context.Context, source []int, destination int) error {
|
||||
if len(source) == 0 {
|
||||
return nil
|
||||
@@ -840,6 +891,19 @@ AND NOT EXISTS(SELECT 1 FROM `+table+` o WHERE o.`+idColumn+` = `+table+`.`+idCo
|
||||
return err
|
||||
}
|
||||
|
||||
// Merge StashIDs - move all source StashIDs to destination (ignoring duplicates)
|
||||
_, err = dbWrapper.Exec(ctx, `UPDATE OR IGNORE `+"tag_stash_ids"+`
|
||||
SET tag_id = ?
|
||||
WHERE tag_id IN `+inBinding, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete remaining source StashIDs that couldn't be moved (duplicates)
|
||||
if _, err := dbWrapper.Exec(ctx, `DELETE FROM tag_stash_ids WHERE tag_id IN `+inBinding, srcArgs...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, id := range source {
|
||||
err = qb.Destroy(ctx, id)
|
||||
if err != nil {
|
||||
|
||||
@@ -900,6 +900,66 @@ func TestTagUpdateAlias(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTagStashIDs(t *testing.T) {
|
||||
if err := withTxn(func(ctx context.Context) error {
|
||||
qb := db.Tag
|
||||
|
||||
// create tag to test against
|
||||
const name = "TestTagStashIDs"
|
||||
tag := models.Tag{
|
||||
Name: name,
|
||||
}
|
||||
err := qb.Create(ctx, &tag)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating tag: %s", err.Error())
|
||||
}
|
||||
|
||||
testStashIDReaderWriter(ctx, t, qb, tag.ID)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTagFindByStashID(t *testing.T) {
|
||||
withTxn(func(ctx context.Context) error {
|
||||
qb := db.Tag
|
||||
|
||||
// create tag to test against
|
||||
const name = "TestTagFindByStashID"
|
||||
const stashID = "stashid"
|
||||
const endpoint = "endpoint"
|
||||
tag := models.Tag{
|
||||
Name: name,
|
||||
StashIDs: models.NewRelatedStashIDs([]models.StashID{{StashID: stashID, Endpoint: endpoint}}),
|
||||
}
|
||||
err := qb.Create(ctx, &tag)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating tag: %s", err.Error())
|
||||
}
|
||||
|
||||
// find by stash ID
|
||||
tags, err := qb.FindByStashID(ctx, models.StashID{StashID: stashID, Endpoint: endpoint})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error finding by stash ID: %s", err.Error())
|
||||
}
|
||||
|
||||
assert.Len(t, tags, 1)
|
||||
assert.Equal(t, tag.ID, tags[0].ID)
|
||||
|
||||
// find by non-existent stash ID
|
||||
tags, err = qb.FindByStashID(ctx, models.StashID{StashID: "nonexistent", Endpoint: endpoint})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error finding by stash ID: %s", err.Error())
|
||||
}
|
||||
|
||||
assert.Len(t, tags, 0)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestTagMerge(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user