Service Portal - Custom Search Source

We have knowledge bases at my work and as such the default search is limited. I wanted to add to the type ahead a result like "Search all KB's for your term here".

I looked at the search sources and found it has a scripted bit but it's not documented well.

Finished Product

OOB Search Source

So first lets look at the out of box "Service Catalog" search source,
specifically it's data fetch (shows up when you select,
Is scripted source)

(function(query) {
var results = [];
//Here goes the logic. Compute results how you want!
if (!gs.isLoggedIn())
return results;

var sc = new GlideRecord('sc_cat_item');
sc.addQuery('123TEXTQUERY321', query);
sc.addQuery('active',true);
sc.addQuery('no_search', '!=', true);
sc.addQuery('visible_standalone', true);
sc.addQuery('sys_class_name', 'NOT IN', 'sc_cat_item_wizard');
var portalValue = $sp.getValue('sc_catalog');
if (portalValue)
sc.addQuery('sc_catalogs', portalValue);
sc.query();
var catCount = 0;
while (sc.next() && catCount < data.limit) {
if (!$sp.canReadRecord(sc))
continue;

var item = {};
item.type = "sc";
item.page = "sc_cat_item";

if (sc.getRecordClassName() == "sc_cat_item_guide")
item.page = "sc_cat_item_guide";
else if (sc.getRecordClassName() == "sc_cat_item_content") {
var gr = new GlideRecord('sc_cat_item_content');
gr.get(sc.getUniqueValue());
$sp.getRecordValues(item, gr, 'url,content_type,kb_article');
item.type = "sc_content";
}
else
item.type = "sc";

$sp.getRecordDisplayValues(item, sc, 'name,short_description,picture,price,sys_id,sys_class_name');
item.score = parseInt(sc.ir_query_score.getDisplayValue());
item.label = item.name;
item.primary = item.name;

//calculating URL
if (item.type == "sc")
item.url = '?id=' + item.page + '&sys_id=' + item.sys_id;
if (item.type == "sc_content") {
if (item.content_type == "kb")
item.url = '?id=kb_article&sys_id=' + item.kb_article;
else if (item.content_type == "external")
item.target = '_blank';
else
item.url = '?id=sc_cat_item&sys_id=' + item.sys_id;
}
if (item.type == "sc_guide")
item.url = '?id=sc_cat_item_guide&sys_id=' + item.sys_id;

results.push(item);
catCount++;
}

return results;
})(query);

There's a lot going on there, but what I picked out was, it returns an
array of objects where the object has the following properties;

{
score:"-100",
label:resultMsg,
primary:resultMsg,
url: "?id=kb_search&spa=1&query=" + query,
//target: "",
//page:"kb_search"
}

Score

This seems pretty obvious to me, a integer to determine order in the results

Label and Primary

I'm not sure the difference but this seems to be what shows up as text in the result.

URL

This is where the browser will go on click.

Target

I assume this is the a tag's target attribute, so if you want a new tab each time use a value of _blank.

Page

I am not sure if this does anything on it's own, in the out of box widget they use this in the defined url.

Search Source I made

Below is my search source script.

(function(query) {
var resultMsg = "Search KB for " + query;
var results = [{
score:"-100",
label:resultMsg,
primary:resultMsg,
url: "?id=kb_search&spa=1&query=" + query,
//target: "",
//page:"kb_search"
}];
return results;
})(query);

Further Reading

Community Thread