Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 hours ago
Hi everyone,
I wanted to share a challenge we recently faced regarding the Employee Center taxonomy and how we solved it. If you have run into issues where your published knowledge articles dont automatically show up under your topics despite having the correct category mappings, this might help you.
The Problem
If you are using the Employee Center taxonomy and have connected your knowledge categories to topics via the Connected Categories related list on a topic, you would expect new knowledge articles to automatically appear as connected content under that topic. In practice, this doesnt always work reliably.
If you are using the Employee Center taxonomy and have connected your knowledge categories to topics via the Connected Categories related list on a topic, you would expect new knowledge articles to automatically appear as connected content under that topic. In practice, this doesnt always work reliably.
The Out of the Box flow works as follows:
kb knowledge article published goes to unconnected category content queue goes to m2m connected content visible in portal
kb knowledge article published goes to unconnected category content queue goes to m2m connected content visible in portal
There are two breaking points in this flow:
- Historical articles are missed. Articles that existed before the category topic mapping was established are never picked up by TopicCategoryContentSurfacingUtil surfaceNewContent. They never enter the queue, so they never get linked.
- The queue requires manual action. The OOB scheduled job Surface New Unconnected Content of Categories only populates the queue. It does not automatically move articles into m2m connected content. That step requires manual action via the UI Add Content to Topic, which simply does not scale for large organizations.
This is a widely reported issue with no delivered OOB fix as of the Australia release.
I will post our workaround which bypasses the broken intermediate steps entirely and writes directly to the M2M table via a scheduled job in the answers below. Hopefully, it helps anyone struggling with the same issue.
Solved! Go to Solution.
1 ACCEPTED SOLUTION
Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 hours ago
The Solution: Direct M2M Sync via Scheduled Job
To solve this, we bypass the intermediate queue steps and write directly to m2m connected content using a Scheduled Script Execution. This achieves the exact same end result as the OOB flow, but fully automated and without the queue bottlenecks.
Key features of this job:
Configurable: It reads which knowledge bases to sync from a system property comma separated sys ids, so no code changes are needed when adding new KBs.
Safe and Idempotent: It looks up the topic via the m2m connected category table and skips articles already linked to that exact topic, making it safe to run repeatedly.
Preserves manual work: It leaves manually assigned topics on the same article untouched.
Defensive: It skips articles without a category or whose category has no topic mapping yet.
Configurable: It reads which knowledge bases to sync from a system property comma separated sys ids, so no code changes are needed when adding new KBs.
Safe and Idempotent: It looks up the topic via the m2m connected category table and skips articles already linked to that exact topic, making it safe to run repeatedly.
Preserves manual work: It leaves manually assigned topics on the same article untouched.
Defensive: It skips articles without a category or whose category has no topic mapping yet.
Step 1 Create a System Property
Name: taxonomy_sync.kb_sys_ids
Type: string
Value: comma separated sys ids of your knowledge bases
Name: taxonomy_sync.kb_sys_ids
Type: string
Value: comma separated sys ids of your knowledge bases
Step 2 Create the Scheduled Job
Go to System Definition, then Scheduled Jobs, then New. Select Automatically run a script of your choosing, and paste the script below. Set it to run daily or at a cadence that fits your publication volume during low traffic hours.
Go to System Definition, then Scheduled Jobs, then New. Select Automatically run a script of your choosing, and paste the script below. Set it to run daily or at a cadence that fits your publication volume during low traffic hours.
javascript
// ============================================================================= // WHY THIS SCHEDULED JOB EXISTS // ============================================================================= // The OOB Employee Center taxonomy sync relies on TopicCategoryContentSurfacingUtil // (surfaceNewContent) to detect new knowledge articles and place them in the // unconnected_category_content queue. A second OOB scheduled job // "Surface New Unconnected Content of Categories" then processes that queue. // // In practice, this OOB flow has two known issues: // // 1. Articles that existed BEFORE the kb_category → topic mapping was established // in m2m_connected_category are never picked up by surfaceNewContent(). // They never appear in the unconnected_category_content queue and therefore // never get linked to a topic automatically. // // 2. The OOB scheduled job only populates the queue, it does not automatically // move articles from the queue into m2m_connected_content. That step requires // manual action via the UI ("Add Content to Topic"), which is not scalable. // // This is a widely reported issue in the ServiceNow community with no OOB fix. // // This job bypasses both problems by querying knowledge articles directly and // writing the article-topic connection straight to m2m_connected_content, // which is the same end result as the OOB flow — just without the broken // intermediate steps. // // Knowledge bases to sync are configured via a system property so that no code // changes are needed when additional knowledge bases are added: // Property: taxonomy_sync.kb_sys_ids (comma-separated sys_ids) // ============================================================================= var CONTENT_TYPE_KB = '4c32a92153622010069addeeff7b12a3'; // Content type: Knowledge (kb_knowledge) var kbSysIds = gs.getProperty('taxonomy_sync.kb_sys_ids', ''); if (!kbSysIds) { gs.warn('[Taxonomy Sync] No knowledge bases configured. Set system property: taxonomy_sync.kb_sys_ids'); } else { var kbArr = kbSysIds.split(','); var totalCount = 0; kbArr.forEach(function(kbSysId) { kbSysId = kbSysId.trim(); if (!kbSysId) return; var kbGr = new GlideRecord('kb_knowledge_base'); var kbName = kbGr.get(kbSysId) ? kbGr.getValue('title') : kbSysId; var count = 0; var errorCount = 0; var articleGr = new GlideRecord('kb_knowledge'); articleGr.addQuery('kb_knowledge_base', kbSysId); articleGr.addQuery('workflow_state', '!=', 'retired'); articleGr.query(); while (articleGr.next()) { var articleSysId = articleGr.getUniqueValue(); var categoryId = articleGr.getValue('kb_category'); if (!categoryId) continue; var topicGr = new GlideRecord('m2m_connected_category'); topicGr.addQuery('kb_category', categoryId); topicGr.setLimit(1); topicGr.query(); if (!topicGr.next()) continue; var topicSysId = topicGr.getValue('topic'); var checkGr = new GlideRecord('m2m_connected_content'); checkGr.addQuery('knowledge', articleSysId); checkGr.addQuery('topic', topicSysId); checkGr.query(); if (checkGr.next()) continue; try { var newGr = new GlideRecord('m2m_connected_content'); newGr.setValue('knowledge', articleSysId); newGr.setValue('topic', topicSysId); newGr.setValue('content_type', CONTENT_TYPE_KB); newGr.setValue('source', 'auto'); var result = newGr.insert(); if (result) { count++; } else { errorCount++; gs.warn('[Taxonomy Sync] Failed to link: ' + articleGr.getValue('number') + ' in KB: ' + kbName); } } catch (ex) { errorCount++; gs.error('[Taxonomy Sync] Error linking: ' + articleGr.getValue('number') + ' in KB: ' + kbName + ' — ' + ex.message); } } totalCount += count; if (count > 0 || errorCount > 0) gs.info('[Taxonomy Sync] KB: ' + kbName + ' — linked: ' + count + ', errors: ' + errorCount); else gs.info('[Taxonomy Sync] KB: ' + kbName + ' — no new articles to link.'); }); gs.info('[Taxonomy Sync] Done — total linked: ' + totalCount); }
1 REPLY 1
Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
2 hours ago
The Solution: Direct M2M Sync via Scheduled Job
To solve this, we bypass the intermediate queue steps and write directly to m2m connected content using a Scheduled Script Execution. This achieves the exact same end result as the OOB flow, but fully automated and without the queue bottlenecks.
Key features of this job:
Configurable: It reads which knowledge bases to sync from a system property comma separated sys ids, so no code changes are needed when adding new KBs.
Safe and Idempotent: It looks up the topic via the m2m connected category table and skips articles already linked to that exact topic, making it safe to run repeatedly.
Preserves manual work: It leaves manually assigned topics on the same article untouched.
Defensive: It skips articles without a category or whose category has no topic mapping yet.
Configurable: It reads which knowledge bases to sync from a system property comma separated sys ids, so no code changes are needed when adding new KBs.
Safe and Idempotent: It looks up the topic via the m2m connected category table and skips articles already linked to that exact topic, making it safe to run repeatedly.
Preserves manual work: It leaves manually assigned topics on the same article untouched.
Defensive: It skips articles without a category or whose category has no topic mapping yet.
Step 1 Create a System Property
Name: taxonomy_sync.kb_sys_ids
Type: string
Value: comma separated sys ids of your knowledge bases
Name: taxonomy_sync.kb_sys_ids
Type: string
Value: comma separated sys ids of your knowledge bases
Step 2 Create the Scheduled Job
Go to System Definition, then Scheduled Jobs, then New. Select Automatically run a script of your choosing, and paste the script below. Set it to run daily or at a cadence that fits your publication volume during low traffic hours.
Go to System Definition, then Scheduled Jobs, then New. Select Automatically run a script of your choosing, and paste the script below. Set it to run daily or at a cadence that fits your publication volume during low traffic hours.
javascript
// ============================================================================= // WHY THIS SCHEDULED JOB EXISTS // ============================================================================= // The OOB Employee Center taxonomy sync relies on TopicCategoryContentSurfacingUtil // (surfaceNewContent) to detect new knowledge articles and place them in the // unconnected_category_content queue. A second OOB scheduled job // "Surface New Unconnected Content of Categories" then processes that queue. // // In practice, this OOB flow has two known issues: // // 1. Articles that existed BEFORE the kb_category → topic mapping was established // in m2m_connected_category are never picked up by surfaceNewContent(). // They never appear in the unconnected_category_content queue and therefore // never get linked to a topic automatically. // // 2. The OOB scheduled job only populates the queue, it does not automatically // move articles from the queue into m2m_connected_content. That step requires // manual action via the UI ("Add Content to Topic"), which is not scalable. // // This is a widely reported issue in the ServiceNow community with no OOB fix. // // This job bypasses both problems by querying knowledge articles directly and // writing the article-topic connection straight to m2m_connected_content, // which is the same end result as the OOB flow — just without the broken // intermediate steps. // // Knowledge bases to sync are configured via a system property so that no code // changes are needed when additional knowledge bases are added: // Property: taxonomy_sync.kb_sys_ids (comma-separated sys_ids) // ============================================================================= var CONTENT_TYPE_KB = '4c32a92153622010069addeeff7b12a3'; // Content type: Knowledge (kb_knowledge) var kbSysIds = gs.getProperty('taxonomy_sync.kb_sys_ids', ''); if (!kbSysIds) { gs.warn('[Taxonomy Sync] No knowledge bases configured. Set system property: taxonomy_sync.kb_sys_ids'); } else { var kbArr = kbSysIds.split(','); var totalCount = 0; kbArr.forEach(function(kbSysId) { kbSysId = kbSysId.trim(); if (!kbSysId) return; var kbGr = new GlideRecord('kb_knowledge_base'); var kbName = kbGr.get(kbSysId) ? kbGr.getValue('title') : kbSysId; var count = 0; var errorCount = 0; var articleGr = new GlideRecord('kb_knowledge'); articleGr.addQuery('kb_knowledge_base', kbSysId); articleGr.addQuery('workflow_state', '!=', 'retired'); articleGr.query(); while (articleGr.next()) { var articleSysId = articleGr.getUniqueValue(); var categoryId = articleGr.getValue('kb_category'); if (!categoryId) continue; var topicGr = new GlideRecord('m2m_connected_category'); topicGr.addQuery('kb_category', categoryId); topicGr.setLimit(1); topicGr.query(); if (!topicGr.next()) continue; var topicSysId = topicGr.getValue('topic'); var checkGr = new GlideRecord('m2m_connected_content'); checkGr.addQuery('knowledge', articleSysId); checkGr.addQuery('topic', topicSysId); checkGr.query(); if (checkGr.next()) continue; try { var newGr = new GlideRecord('m2m_connected_content'); newGr.setValue('knowledge', articleSysId); newGr.setValue('topic', topicSysId); newGr.setValue('content_type', CONTENT_TYPE_KB); newGr.setValue('source', 'auto'); var result = newGr.insert(); if (result) { count++; } else { errorCount++; gs.warn('[Taxonomy Sync] Failed to link: ' + articleGr.getValue('number') + ' in KB: ' + kbName); } } catch (ex) { errorCount++; gs.error('[Taxonomy Sync] Error linking: ' + articleGr.getValue('number') + ' in KB: ' + kbName + ' — ' + ex.message); } } totalCount += count; if (count > 0 || errorCount > 0) gs.info('[Taxonomy Sync] KB: ' + kbName + ' — linked: ' + count + ', errors: ' + errorCount); else gs.info('[Taxonomy Sync] KB: ' + kbName + ' — no new articles to link.'); }); gs.info('[Taxonomy Sync] Done — total linked: ' + totalCount); }
