Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
This script identifies tickets similar to a given ticket in ServiceNow based on text similarity of short_description and description fields, optionally boosting the score for matching categories or assignment groups. It is intended for background script execution for testing, analysis, or automation purposes.

**Features:**
1) Compares a source ticket against other tickets in the same table (incident by default).
2) Computes Jaccard similarity between tokenized text fields.
3) Applies bonus points for matching category and assignment_group.
4) Returns a sorted list of similar tickets with score, number, caller_id and short_description.
5) Supports top N results and a minimum score filter.


**Usage:**
1) Paste the script in scripts-background.
2) Make changes to the sys_id of your ticket in line no. 3
3) click run to get an output as below

**Output:**
* *** Script: === Similar Tickets to: INC0010005 ===
* *** Script: INC0010006 | Score: 1.08 | undefined | Sai Test INC0008112
* *** Script: INC0010004 | Score: 0.58 | undefined | Sai Test INC0009005
* *** Script: INC0009009 | Score: 0.161 | undefined | Unable to access the shared folder.test
* *** Script: INC0008001 | Score: 0.08 | undefined | ATF:TEST2
* *** Script: INC0000020 | Score: 0.08 | undefined | I need a replacement iPhone, please
* *** Script: INC0000031 | Score: 0.08 | undefined | Need help with Remedy. Can we configure UI?
* *** Script: INC0000040 | Score: 0.08 | undefined | JavaScript error on hiring page of corporate website
* *** Script: INC0010002 | Score: 0.08 | undefined |
* *** Script: INC0000057 | Score: 0.08 | undefined | Performance problems with wifi
* *** Script: INC0010003 | Score: 0.08 | undefined |

**Explanation of the output:**
1) First Line contains the ticket which you have provided as a sys_id.
2) Next lines contains the ticket which contain ticket no. | score | caller_id | short_description.
3) If you keenly observe there are few tickets that do not have similar short description / description with scores as 0.08 but still in output the reason for this is their category and assignment group still matches with the compared ticket.
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
(function() {
var table = 'incident'; //can be used for other tickets as well
var sourceSysId = 'f4755b82c3203210348bbd33e40131cb'; // sys_id of the ticket which is used to find similar tickets
var limit = 10; // top N results
var minScore = 0.05;

function tokensFromText(text) {
if (!text) return [];
text = text.toLowerCase().replace(/[^a-z0-9\s]/g, ' ');
var raw = text.split(/\s+/);
var stop = {
'the':1,'and':1,'for':1,'with':1,'that':1,'this':1,'from':1,'have':1,'has':1,'was':1,'were':1,
'a':1,'an':1,'is':1,'in':1,'on':1,'of':1,'to':1,'it':1,'as':1,'by':1,'be':1,'are':1
};
var map = {};
for (var i=0;i<raw.length;i++) {
var t = raw[i].trim();
if (!t || t.length<3 || stop[t]) continue; // skip very short tokens
t = t.replace(/(ing|ed|s)$/,'');
map[t] = (map[t]||0)+1;
}
return Object.keys(map);
}

function jaccardScore(tokensA, tokensB) {
var setA={}, setB={};
tokensA.forEach(function(t){setA[t]=1;});
tokensB.forEach(function(t){setB[t]=1;});
var inter=0, uni=0;
for (var t in setA){ if(setB[t]) inter++; uni++; }
for (var t2 in setB){ if(!setA[t2]) uni++; }
return uni===0 ? 0 : inter/uni;
}

// Get source record
var src = new GlideRecord(table);
if (!src.get(sourceSysId)) {
gs.error("Source record not found");
return;
}

var sourceText = [src.short_description, src.description].join(" ");
var sourceTokens = tokensFromText(sourceText);
if (sourceTokens.length === 0) {
gs.print("No meaningful text to compare");
return;
}

// Find candidate incidents
var gr = new GlideRecord(table);
gr.addActiveQuery();
gr.addQuery('sys_id','!=',sourceSysId);
gr.setLimit(300);
gr.query();

var results = [];
while (gr.next()) {
var candidateText = [gr.short_description, gr.description].join(" ");
var candidateTokens = tokensFromText(candidateText);
var score = jaccardScore(sourceTokens, candidateTokens);

if (src.category == gr.category) score += 0.05;
if (src.assignment_group == gr.assignment_group) score += 0.03;

if (score >= minScore) {
results.push({
number: gr.number.toString(),
short_description: gr.short_description.toString(),
score: parseFloat(score.toFixed(3))
});
}
}

results.sort(function(a,b){return b.score - a.score;});

// Print top results
gs.print("=== Similar Tickets to: " + src.number + " ===");
results.slice(0, limit).forEach(function(r) {
gs.print(r.number + " | Score: " + r.score + " | " + r.caller_id + " | " + r.short_description);
});

})();
Loading