sarah_bioni
ServiceNow Employee
Options
- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
2 hours ago - edited 2 hours ago
Goal: Return the Knowledge Base category hierarchy in the structure expected by the Content Tree component:
[
{
"id": "p1",
"label": "Tree parent item",
"children": [
{ "id": "c1", "label": "Tree child item 1" },
{ "id": "c2", "label": "Tree child item 2" },
{ "id": "c3", "label": "Tree child item 3" }
]
}
]
``
This article walks you through creating a Transform Data Broker that queries kb_category, builds a nested tree, sorts categories alphabetically, and exposes it for binding to the Content Tree’s Items property.
What we’ll build
- A Transform Data Broker (server script) named Get KB Category Hierarchy.
- A single input property:
kb_id(the sys_id of the Knowledge Base). - A script that:
- Starts from the KB’s top-level categories,
- Performs a batched traversal to collect all children,
- Builds parent/child relationships,
- Sorts siblings by
label, - Returns
items: [...]in the shape the Content Tree requires.
Step 1 — Create the Transform Data Broker in UI Builder
- Open UI Builder for your Workspace page.
- In the Data and Scripts panel, click (+) → Data Resource → Create →choose Transform.
- Name it Get KB Category Hierarchy and (optionally) add a description.
- In Properties, define the broker’s input:
[
{
"name": "kb_id",
"label": "KB Id",
"description": "KB sys_id to get the categories hierarchy",
"readOnly": false,
"fieldType": "string",
"mandatory": true,
"defaultValue": ""
}
]
Step 2 — Paste the Transform Script
It builds a node map, traverses children breadth‑first, wires parent/child relationships, sorts labels, and returns { items: roots }—the Content Tree‑friendly output.
function transform(input) {
var kbId = input.kb_id;
// Node registry and parent mapping
var nodes = {};
var parentMap = {};
var roots = [];
// Pick a good label field (environment differences: 'label' vs 'value')
function getLabelField(grCat) {
if (grCat.isValidField('label')) return 'label';
if (grCat.isValidField('value')) return 'value';
return 'label';
}
// 1) Collect KB top-level categories
var grRoots = new GlideRecord('kb_category');
var pFieldProbe = 'parent_id'; // parent relationship field
grRoots.addQuery(pFieldProbe, kbId); // roots are children of the KB
grRoots.query();
var frontier = [];
while (grRoots.next()) {
var id = grRoots.getUniqueValue();
var label = grRoots.getValue(getLabelField(grRoots));
if (!nodes[id]) nodes[id] = { id: id, label: label, children: [] };
else nodes[id].label = label;
parentMap[id] = kbId;
roots.push(nodes[id]);
frontier.push(id);
}
// 2) BFS traversal collecting descendants in batches
var visitedParents = {};
frontier = frontier.filter(function (pid) { return !!pid; });
while (frontier.length) {
var batchParents = [];
frontier.forEach(function (pid) {
if (!visitedParents[pid]) {
visitedParents[pid] = true;
batchParents.push(pid);
}
});
if (!batchParents.length) break;
var gr = new GlideRecord('kb_category');
// Only active categories if table supports it
gr.addActiveQuery && gr.addActiveQuery();
var pField = 'parent_id';
var qc = gr.addQuery(pField, batchParents[0]);
for (var i = 1; i < batchParents.length; i++) {
qc.addOrCondition(pField, batchParents[i]);
}
gr.query();
var nextFrontier = [];
while (gr.next()) {
var childId = gr.getUniqueValue();
var labelC = gr.getValue(getLabelField(gr));
var parentId = gr.getValue(pField); // parentId is a category or KB ref
if (!nodes[childId]) nodes[childId] = { id: childId, label: labelC, children: [] };
else nodes[childId].label = labelC;
parentMap[childId] = parentId || '';
if (parentId) nextFrontier.push(childId);
}
frontier = nextFrontier;
}
// 3) Wire children to their parents
for (var childIdF in parentMap) {
var parentIdF = parentMap[childIdF];
var childNode = nodes[childIdF];
if (parentIdF && nodes[parentIdF]) {
nodes[parentIdF].children.push(childNode);
}
}
// 4) Sort siblings by label
function sortRec(list) {
list.sort(function (a, b) {
return (a.label || '').localeCompare(b.label || '');
});
list.forEach(function (n) {
if (n.children && n.children.length) sortRec(n.children);
});
}
sortRec(roots);
// 5) Return the array in a named output property for easy binding
return { items: roots };
}
``