From 88dc5ec95d26dbad3f7b6d6ee9e8a0e9ace6228f Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 25 Oct 2025 07:06:27 -0700 Subject: [PATCH] new feature for change data cal --- .../Change Table/README.md | 32 ++++++ .../similarity_change_calculator.js | 97 +++++++++++++++++++ .../{ => Incident Table}/README.md | 0 .../similarity_incident_calculator.js} | 0 4 files changed, 129 insertions(+) create mode 100644 Specialized Areas/Data Quality/Similarity Calculator/Change Table/README.md create mode 100644 Specialized Areas/Data Quality/Similarity Calculator/Change Table/similarity_change_calculator.js rename Specialized Areas/Data Quality/Similarity Calculator/{ => Incident Table}/README.md (100%) rename Specialized Areas/Data Quality/Similarity Calculator/{similarity_calculator.js => Incident Table/similarity_incident_calculator.js} (100%) diff --git a/Specialized Areas/Data Quality/Similarity Calculator/Change Table/README.md b/Specialized Areas/Data Quality/Similarity Calculator/Change Table/README.md new file mode 100644 index 0000000000..b13059261c --- /dev/null +++ b/Specialized Areas/Data Quality/Similarity Calculator/Change Table/README.md @@ -0,0 +1,32 @@ +# Similarity Calculator for ServiceNow Change Requests + +## Overview +This utility provides manual similarity scoring between ServiceNow change request records using text analysis, without requiring machine learning. It helps developers and admins find similar changes by comparing descriptions and calculating similarity scores programmatically. + +## How It Works +1. Extracts keywords from change request descriptions +2. Compares keyword overlap between change requests +3. Calculates a similarity score (0-100%) +4. Finds and ranks similar change requests based on score + +## Features +- Compare change request descriptions using keyword matching +- Calculate similarity scores between change requests +- Find and rank similar changes programmatically +- No ML or Predictive Intelligence required + +## Use Cases +- Manual clustering of change requests +- Identifying duplicate or related changes +- Data quality analysis before ML model training +- Change impact analysis and triage + +## Setup Requirements +- ServiceNow instance with access to the `change_request` table +- Script execution permissions (Background Script or Script Include) +- No external dependencies + +## Customization +- Adjust keyword extraction logic for your environment +- Change scoring algorithm to use TF-IDF, cosine similarity, etc. +- Filter by assignment group, category, or other fields diff --git a/Specialized Areas/Data Quality/Similarity Calculator/Change Table/similarity_change_calculator.js b/Specialized Areas/Data Quality/Similarity Calculator/Change Table/similarity_change_calculator.js new file mode 100644 index 0000000000..0ec591874b --- /dev/null +++ b/Specialized Areas/Data Quality/Similarity Calculator/Change Table/similarity_change_calculator.js @@ -0,0 +1,97 @@ +// ======================================== +// Similarity Calculator for ServiceNow Changes +// ======================================== +// Purpose: Manually score similarity between change requests using text analysis +// No ML required +// ======================================== + +(function similarityChangeCalculator() { + // --- CONFIG --- + var config = { + table: 'change_request', + baseChangeSysId: 'YOU ID HERE', // Set to the sys_id of the change to compare + fields: ['short_description', 'description'], + maxResults: 50, + minSimilarity: 0 // Minimum similarity % to report + }; + + // --- Helper: Extract keywords from text --- + function extractKeywords(text) { + if (!text) return []; + // Simple keyword extraction: split, lowercase, remove stopwords + var stopwords = ['the','and','a','an','to','of','in','for','on','with','at','by','from','is','it','this','that','as','are','was','were','be','has','have','had','but','or','not','can','will','do','does','did','if','so','then','than','too','very','just','also','into','out','up','down','over','under','again','more','less','most','least','such','no','yes','you','your','our','their','my','me','i']; + var words = text.toLowerCase().replace(/[^a-z0-9 ]/g, ' ').split(/\s+/); + var keywords = []; + for (var i = 0; i < words.length; i++) { + var word = words[i]; + if (word && stopwords.indexOf(word) === -1 && word.length > 2) { + keywords.push(word); + } + } + return keywords; + } + + // --- Helper: Calculate similarity score --- + function calcSimilarity(keywordsA, keywordsB) { + if (!keywordsA.length || !keywordsB.length) return 0; + var mapA = {}; + var mapB = {}; + for (var i = 0; i < keywordsA.length; i++) { + mapA[keywordsA[i]] = true; + } + for (var j = 0; j < keywordsB.length; j++) { + mapB[keywordsB[j]] = true; + } + var intersection = 0; + var unionMap = {}; + for (var k in mapA) { + unionMap[k] = true; + if (mapB[k]) intersection++; + } + for (var l in mapB) { + unionMap[l] = true; + } + var union = Object.keys(unionMap).length; + return union ? (intersection / union * 100) : 0; + } + + // --- Get base change request --- + var baseGr = new GlideRecord(config.table); + if (!baseGr.get(config.baseChangeSysId)) { + gs.error('Base change request not found: ' + config.baseChangeSysId); + return; + } + var baseText = config.fields.map(function(f) { return baseGr.getValue(f); }).join(' '); + var baseKeywords = extractKeywords(baseText); + + // --- Find candidate change requests --- + var gr = new GlideRecord(config.table); + gr.addQuery('active', true); + gr.addQuery('sys_id', '!=', config.baseChangeSysId); + gr.setLimit(config.maxResults); + gr.query(); + + var results = []; + while (gr.next()) { + var compareText = config.fields.map(function(f) { return gr.getValue(f); }).join(' '); + var compareKeywords = extractKeywords(compareText); + var score = calcSimilarity(baseKeywords, compareKeywords); + results.push({ + sys_id: gr.getUniqueValue(), + number: gr.getValue('number'), + short_description: gr.getValue('short_description'), + similarity: score + }); + } + + // --- Sort and print results --- + results.sort(function(a, b) { return b.similarity - a.similarity; }); + gs.info('=== Similarity Results ==='); + for (var i = 0; i < results.length; i++) { + var r = results[i]; + gs.info((i+1) + '. ' + r.number + ' [' + r.sys_id + '] (' + r.similarity.toFixed(1) + '%) - ' + r.short_description); + } + if (results.length === 0) { + gs.info('No similar change requests found above threshold.'); + } +})(); diff --git a/Specialized Areas/Data Quality/Similarity Calculator/README.md b/Specialized Areas/Data Quality/Similarity Calculator/Incident Table/README.md similarity index 100% rename from Specialized Areas/Data Quality/Similarity Calculator/README.md rename to Specialized Areas/Data Quality/Similarity Calculator/Incident Table/README.md diff --git a/Specialized Areas/Data Quality/Similarity Calculator/similarity_calculator.js b/Specialized Areas/Data Quality/Similarity Calculator/Incident Table/similarity_incident_calculator.js similarity index 100% rename from Specialized Areas/Data Quality/Similarity Calculator/similarity_calculator.js rename to Specialized Areas/Data Quality/Similarity Calculator/Incident Table/similarity_incident_calculator.js