From 0a94cecb0d7a2188b1ad0881ae18c4428fc345f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 11 Mar 2026 17:21:06 -0400 Subject: [PATCH] fix: #14084, fix tags not getting properly removed from topics this was happening because the same topic was passed to setObject bulk multiple times with different tag data. the fix is to find the uniq tids first and then only call setObjectBulk once per topic --- src/topics/tags.js | 21 ++++++++++++++------- test/topics.js | 21 ++++++++++++--------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/topics/tags.js b/src/topics/tags.js index 7f60096cd1..48bbb44e15 100644 --- a/src/topics/tags.js +++ b/src/topics/tags.js @@ -256,14 +256,21 @@ module.exports = function (Topics) { }; async function removeTagsFromTopics(tags) { - await async.eachLimit(tags, 50, async (tag) => { - const tids = await db.getSortedSetRange(`tag:${tag}:topics`, 0, -1); - if (!tids.length) { - return; - } + const uniqTids = new Set(); + const tagsToRemove = new Set(tags); + + await batch.processArray(tags, async (tags) => { + await Promise.all(tags.map(async (tag) => { + await batch.processSortedSet(`tag:${tag}:topics`, async (tids) => { + tids.forEach(tid => uniqTids.add(tid)); + }, { batch: 500 }); + })); + }, { batch: 50 }); + + await batch.processArray(Array.from(uniqTids), async (tids) => { let topicsTags = await Topics.getTopicsTags(tids); topicsTags = topicsTags.map( - topicTags => topicTags.filter(topicTag => topicTag && topicTag !== tag) + topicTags => topicTags.filter(tag => tag && !tagsToRemove.has(tag)) ); await db.setObjectBulk( @@ -271,7 +278,7 @@ module.exports = function (Topics) { `topic:${tid}`, { tags: topicsTags[index].join(',') }, ])) ); - }); + }, { batch: 500 }); } async function removeTagsFromUsers(tags) { diff --git a/test/topics.js b/test/topics.js index 21ecffd245..f70bd301fb 100644 --- a/test/topics.js +++ b/test/topics.js @@ -1632,15 +1632,18 @@ describe('Topic\'s', () => { assert.deepStrictEqual(tags, ['deleteme1', 'deleteme3']); }); - it('should delete tag', (done) => { - topics.deleteTag('javascript', (err) => { - assert.ifError(err); - db.getObject('tag:javascript', (err, data) => { - assert.ifError(err); - assert(!data); - done(); - }); - }); + it('should delete tag', async () => { + await topics.deleteTag('javascript'); + const data = await db.getObject('tag:javascript'); + assert(!data); + }); + + it('should properly remove tags from topic hash when removing all tags of a topic', async () => { + const result1 = await topics.post({ uid: adminUid, tags: ['tag1', 'tag2', 'tag3', 'tag4', 'tag5'], title: 'many tags much wow', content: 'topic 1 content', cid: topic.categoryId }); + await topics.deleteTags(['tag1', 'tag2', 'tag3', 'tag4', 'tag5']); + const topicData = await topics.getTopicData(result1.topicData.tid); + const tags = topicData.tags.map(t => t.value); + assert.deepStrictEqual(tags, []); }); it('should delete category tag as well', async () => {