Skip to content

Commit 5e55ab1

Browse files
authored
Create findSimilarTickets.js
This script helps the users to find tickets that have similar ticket descriptions and short descriptions and score as per their matched content which can be utilised in reports to see how many tickets are having similarities and further take necessary actions like creating a problem ticket with these details.
1 parent 8859b81 commit 5e55ab1

File tree

1 file changed

+82
-0
lines changed

1 file changed

+82
-0
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
(function() {
2+
var table = 'incident'; //can be used for other tickets as well
3+
var sourceSysId = 'f4755b82c3203210348bbd33e40131cb'; // sys_id of the ticket which is used to find similar tickets
4+
var limit = 10; // top N results
5+
var minScore = 0.05;
6+
7+
function tokensFromText(text) {
8+
if (!text) return [];
9+
text = text.toLowerCase().replace(/[^a-z0-9\s]/g, ' ');
10+
var raw = text.split(/\s+/);
11+
var stop = {
12+
'the':1,'and':1,'for':1,'with':1,'that':1,'this':1,'from':1,'have':1,'has':1,'was':1,'were':1,
13+
'a':1,'an':1,'is':1,'in':1,'on':1,'of':1,'to':1,'it':1,'as':1,'by':1,'be':1,'are':1
14+
};
15+
var map = {};
16+
for (var i=0;i<raw.length;i++) {
17+
var t = raw[i].trim();
18+
if (!t || t.length<3 || stop[t]) continue; // skip very short tokens
19+
t = t.replace(/(ing|ed|s)$/,'');
20+
map[t] = (map[t]||0)+1;
21+
}
22+
return Object.keys(map);
23+
}
24+
25+
function jaccardScore(tokensA, tokensB) {
26+
var setA={}, setB={};
27+
tokensA.forEach(function(t){setA[t]=1;});
28+
tokensB.forEach(function(t){setB[t]=1;});
29+
var inter=0, uni=0;
30+
for (var t in setA){ if(setB[t]) inter++; uni++; }
31+
for (var t2 in setB){ if(!setA[t2]) uni++; }
32+
return uni===0 ? 0 : inter/uni;
33+
}
34+
35+
// Get source record
36+
var src = new GlideRecord(table);
37+
if (!src.get(sourceSysId)) {
38+
gs.error("Source record not found");
39+
return;
40+
}
41+
42+
var sourceText = [src.short_description, src.description].join(" ");
43+
var sourceTokens = tokensFromText(sourceText);
44+
if (sourceTokens.length === 0) {
45+
gs.print("No meaningful text to compare");
46+
return;
47+
}
48+
49+
// Find candidate incidents
50+
var gr = new GlideRecord(table);
51+
gr.addActiveQuery();
52+
gr.addQuery('sys_id','!=',sourceSysId);
53+
gr.setLimit(300);
54+
gr.query();
55+
56+
var results = [];
57+
while (gr.next()) {
58+
var candidateText = [gr.short_description, gr.description].join(" ");
59+
var candidateTokens = tokensFromText(candidateText);
60+
var score = jaccardScore(sourceTokens, candidateTokens);
61+
62+
if (src.category == gr.category) score += 0.05;
63+
if (src.assignment_group == gr.assignment_group) score += 0.03;
64+
65+
if (score >= minScore) {
66+
results.push({
67+
number: gr.number.toString(),
68+
short_description: gr.short_description.toString(),
69+
score: parseFloat(score.toFixed(3))
70+
});
71+
}
72+
}
73+
74+
results.sort(function(a,b){return b.score - a.score;});
75+
76+
// Print top results
77+
gs.print("=== Similar Tickets to: " + src.number + " ===");
78+
results.slice(0, limit).forEach(function(r) {
79+
gs.print(r.number + " | Score: " + r.score + " | " + r.caller_id + " | " + r.short_description);
80+
});
81+
82+
})();

0 commit comments

Comments
 (0)