handle merging tag categories
All checks were successful
Build and Push Docker Image / build (push) Successful in 55s
All checks were successful
Build and Push Docker Image / build (push) Successful in 55s
This commit is contained in:
@@ -98,6 +98,62 @@ export function deleteCategoryForce(id: string): void {
|
||||
if (result.changes === 0) throw new Error(`Category not found: ${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge all tags from `sourceId` category into `targetId` category, then
|
||||
* delete the source category. Tags with conflicting names (case-insensitive)
|
||||
* are combined: their media_tags and tag_mappings rows are re-pointed to the
|
||||
* target tag, and the source tag is deleted.
|
||||
*/
|
||||
export function mergeCategories(sourceId: string, targetId: string): void {
|
||||
const db = getDb()
|
||||
|
||||
const source = db.prepare('SELECT id, name FROM tag_categories WHERE id = ?').get(sourceId) as TagCategory | undefined
|
||||
if (!source) throw new Error(`Source category not found: ${sourceId}`)
|
||||
const target = db.prepare('SELECT id, name FROM tag_categories WHERE id = ?').get(targetId) as TagCategory | undefined
|
||||
if (!target) throw new Error(`Target category not found: ${targetId}`)
|
||||
|
||||
const sourceTags = db
|
||||
.prepare('SELECT id, name, category_id as categoryId FROM tags WHERE category_id = ?')
|
||||
.all(sourceId) as Tag[]
|
||||
const targetTags = db
|
||||
.prepare('SELECT id, name, category_id as categoryId FROM tags WHERE category_id = ?')
|
||||
.all(targetId) as Tag[]
|
||||
|
||||
const targetTagsByNameLower = new Map(targetTags.map((t) => [t.name.toLowerCase(), t]))
|
||||
|
||||
const txn = db.transaction(() => {
|
||||
for (const srcTag of sourceTags) {
|
||||
const conflict = targetTagsByNameLower.get(srcTag.name.toLowerCase())
|
||||
if (conflict) {
|
||||
// Re-point media_tags from source tag to target tag (ignore duplicates)
|
||||
db.prepare(
|
||||
`INSERT OR IGNORE INTO media_tags (item_key, tag_id)
|
||||
SELECT item_key, ? FROM media_tags WHERE tag_id = ?`
|
||||
).run(conflict.id, srcTag.id)
|
||||
db.prepare('DELETE FROM media_tags WHERE tag_id = ?').run(srcTag.id)
|
||||
|
||||
// Re-point tag_mappings from source tag to target tag (ignore duplicates)
|
||||
db.prepare(
|
||||
`UPDATE OR IGNORE tag_mappings SET tag_id = ? WHERE tag_id = ?`
|
||||
).run(conflict.id, srcTag.id)
|
||||
// Delete any remaining (were duplicates that couldn't be updated)
|
||||
db.prepare('DELETE FROM tag_mappings WHERE tag_id = ?').run(srcTag.id)
|
||||
|
||||
// Delete the source tag
|
||||
db.prepare('DELETE FROM tags WHERE id = ?').run(srcTag.id)
|
||||
} else {
|
||||
// No conflict — just move the tag to the target category
|
||||
db.prepare('UPDATE tags SET category_id = ? WHERE id = ?').run(targetId, srcTag.id)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the now-empty source category
|
||||
db.prepare('DELETE FROM tag_categories WHERE id = ?').run(sourceId)
|
||||
})
|
||||
|
||||
txn()
|
||||
}
|
||||
|
||||
// ─── Tags ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
export function getTags(categoryId?: string): Tag[] {
|
||||
|
||||
Reference in New Issue
Block a user