- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on 07-10-2019 10:40 AM
You may or may not be aware that the number field for records across the platform is not unique by default. Usually this presents no issue, however when importing records it can cause duplicate numbers to be used.
If you only have a handful, manually renumbering works fine. But what if you have 1000+?
Unfortunately ServiceNow doesn't have a shareable solution for this and I was directed to the community for help with a script. There are a few posts about renumbering duplicate records, but nothing specific to knowledge and certainly none that account for versioning (multiple versions of an article share the same number). I used a great post from Allen A to get me started and built off of that to account for the versioning. You can find the original post here.
I was able to develop a working solution and thought I'd share the code in case anyone has the same issue in the future! This would be run as a background script.
getDupes();
function getDupes() {
var dupNum = [];
var ga = new GlideAggregate('kb_knowledge');
ga.addAggregate('COUNT', 'number');
ga.addQuery('number', '!=', '');
ga.groupBy('number');
ga.addHaving('COUNT', '>', 1);
ga.addQuery('workflow_state', 'published'); //comment this out if you have duplicates in draft
ga.query();
while (ga.next()) {
dupNum.push(ga.number.toString());
}
gs.print('The KB table has ' + ga.getRowCount() + ' duplicates based on number field...');
//Loop through duplicate array
for (i=0;i<dupNum.length;i++) {
var gr = new GlideRecord("kb_knowledge");
gr.addQuery("number",dupNum[i]);
gr.query();
while(gr.next()) {
////versions share number and article ID. In the event of duplicate numbers, article ID can be used to identify versions that should retain the same number as its predecessors
var versions = new GlideRecord ('kb_knowledge');
versions.addQuery('article_id', gr.article_id);
versions.addEncodedQuery("article_id!=NULL");
versions.query();
var nm = new NumberManager('kb_knowledge');
var newNum = nm.getNextObjNumberPadded();
while(versions.next()){
versions.number = newNum;
versions.update();
}
}
}
}
- 13,028 Views
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hey Bryan,
First of all, this is awesome! I'm actually looking at how to renumber KBs (I have 100s of dups!!). Just a couple of questions:
1. Will this renumber them for me in order? So basically, if all my KBs are KB000001, will it automatically do the next as 02, 03, etc?
2. Where do I run this? I want to be able to run instantly.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Glad my script could help you along...
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
This script isn't working for me. :C
When I run it, it just renumbers all of my KBs.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi,
Unfortunately, I can't attest to bryanshaw's script itself (it appears as if he took my script (from my post in the link above) and did a few adjustments - those adjustments I can't really speak for).
As far as it renumbering your knowledge articles, that's what it's supposed to do.
Were you expecting something different?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
It looks like yours was for INC and he commented saying it doesn't work for that (I did just check out your script). Is this not the case?
But yeah, I have a bunch of KBs called KB0000001 or something and I want them to be fixed and adjust the next one as 02, 03, 04, etc. Does that make sense?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
My script works for any table....that has records with "numbers" associated to them...bryanshaw said that it doesn't work for Knowledge (it does) because my script doesn't take versioning in to account...so he then altered it (slightly) to supposedly get that to work (to consider versioning in the renumbering process). That's what this thread is about.
So anyways...the core part of the script, that I had a part in, is to look over the table you specify (incident, demand, project, knowledge, etc.) and look for duplicate records that are literally numbered the exact same. Once it finds those duplicates, it will then pad (number) the duplicates to the next available number.
There are settings in servicenow, for example, where becky, can open an incident in the back-end view, not actually submit it, but open it, and then bail and go...nah...nevermind I don't want to submit...however...since becky opened the form...it "used" that number...so it's no longer available.
So when this padding (numbering) takes place, it's basically using the next number after the last number that was "used"...so it may not take KB0000002...but instead go to KB0000029, then 30, then 31, etc.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Ahh, got it. Where would I run this? As a background script?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Yes, background script. Unfortunately, he accidentally forgot to mention that part if it wasn't a given.
Anyways, if any of my replies have been Helpful, please mark the various ones as Helpful along the way.
Thanks!
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thank you! This was helpful! Going to comment on the OG post too!
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thanks guys. Glad you found this helpful. I've updated my post to mention running as a background script and to call out Allen's post specifically as the source of the original code I used to get started.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thanks
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hey Bryan,
This script is not working. If I am running this piece of code in my background script, it is converting all the KB articles to one number i.e. assigning one number to all the KB articles. Whereas, Allen's script is working fine if I use it for kb_knowledge table.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Monika,
Are you sure you used the script exactly as posted? I just tried it again in my instance and it's working fine. Are you using article versioning?
Thanks, Bryan
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Bryan,
I have few questions. My instance has few duplicates. There are few versions as well. To test, I exported few articles in my developer instance to test this background script. It changed numbering for all the KB articles. It kept the same numbers for same KB articles with different versions which is expected. Although I have few concerns:
1. It is changing numbering for all KB articles. My expectation was that it will change numbering only for duplicate articles.
2. We have different instances. The script after running will give different results in different instances.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Monika,
I'm not sure about #1, for me it only grabs the duplicates. For #2, you are exactly right. I would recommend running it in your PDI and lower environments until you are satisfied it works to your expectations, then run it in Production and clone down soon there after.
Thanks, Bryan
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Bryan !! I was testing your code for Enterprise enterprise_strategy table . It fetched the Duplicated Numbers and then it renumbered the affected Enterprise Number !! Thanks
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Christine Greetings !! I am not sure whether you still require require this piece of update . But you can have check later . I was running the above said Script in my Enterprise table .It worked fine
Before Renumbering Enterprise Number
After Renumbering Enterprise Number
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hi Bryan,
I tried same script it updated all articles with same number, please help if I'm doing anything wrong here.
Thank you.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Ash,
I'm working through the same problem right now and I think the issue is that the query that Bryan is using to get the duplicate kb numbers is unintentionally including all articles that have had any versions created. So if an article has a version 1.0 and a version 2.0 it will be included in the results because the COUNT of the number will now be greater than 1 even though it's not truly a duplicate as the article sys ids are the same. I think this is the same issue that Monika was experiencing above.
I found by adding the following code to the initial query it looks at just the latest version of each number and then only returns those that have the same number but different sys_ids
ga.addQuery('latest', 'true');
You can run the following under "Scripts - Background" to get a list of duplicate KB numbers and a total count to test this out. Just change the value to 'false' or comment it out to see the different numbers and spot check the articles. It does list each KB Number that is returned in the query so be careful if you have many thousands of duplicates.
var ga = new GlideAggregate('kb_knowledge');
ga.addAggregate('COUNT', 'number');
ga.addQuery('number', '!=', '');
ga.groupBy('number');
ga.addHaving('COUNT', '>', 1);
ga.orderBy('number');
ga.addQuery('latest', 'true'); //limit query to only latest version of article
ga.addQuery('workflow_state', 'published'); //comment this out if you have duplicates in draft
ga.query();
gs.print('The KB table has ' + ga.getRowCount() + ' duplicates based on number field...');
while (ga.next()) {
var numberCount = ga.getAggregate('COUNT', 'number');
var kbNumber = ga.getValue('number')
//Comment the following line out if you just want a total number of dup articles
gs.info('KB Article Number: {0} Count {1}', [kbNumber, numberCount]);
}
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
above script returns the count of duplicate incident number if applied on incident table but how to re- number the duplicate incident number found?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Hello,
I have executed Bryan's script and it has made all articles with same number. I am not able see Adam's original post. Can someone help me to fix this?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
/*
* Knowledge Article Number Deduplication
* ServiceNow Zurich
*
* PURPOSE
* -------
* 1. Scan all records in kb_knowledge.
* 2. Identify KB numbers assigned to multiple article_id families.
* 3. Ignore valid version chains where all records share one article_id.
* 4. Keep the oldest article_id family on the original KB number.
* 5. Assign a new KB number to every other conflicting family.
* 6. Update every version belonging to the renumbered family.
*
* RUN AS
* ------
* System Definition > Fix Scripts
*
* SAFETY
* ------
* APPLY_CHANGES defaults to false.
* Run once in preview mode and review all PLAN entries.
*/
(function deduplicateKnowledgeNumbers() {
/******************************************************************
* CONFIGURATION
******************************************************************/
/*
* false = preview only
* true = perform updates
*/
var APPLY_CHANGES = false;
/*
* Hard safety limit.
* The script aborts before making changes if the number of article
* families requiring remediation exceeds this value.
*/
var MAX_FAMILIES_TO_RENUMBER = 1000;
/*
* When true, the script disables business rules and workflow during
* the number correction.
*
* The records will still receive normal sys_updated_on and
* sys_updated_by values because autoSysFields(false) is not used.
*/
var SUPPRESS_WORKFLOW = true;
/*
* When true, print every valid version family that is skipped.
* Leave false unless troubleshooting.
*/
var LOG_VALID_VERSION_CHAINS = false;
var LOG_PREFIX = '[KB-DUPE-RENUMBER] ';
/******************************************************************
* SCRIPT STATE
******************************************************************/
var candidateNumbers = [];
var remediationPlan = [];
var reservedNumbers = {};
var validVersionChains = 0;
var trueDuplicateNumbers = 0;
var skippedMissingArticleId = 0;
var plannedFamilyUpdates = 0;
var plannedRowUpdates = 0;
var successfulFamilyUpdates = 0;
var successfulRowUpdates = 0;
var failedFamilyUpdates = 0;
log('Starting knowledge article number duplicate analysis.');
log('APPLY_CHANGES = ' + APPLY_CHANGES);
/******************************************************************
* STEP 1: FIND CANDIDATE DUPLICATE NUMBERS
*
* This initially finds numbers appearing on more than one row.
* Normal article version chains will be filtered out in Step 2.
******************************************************************/
var numberAggregate = new GlideAggregate('kb_knowledge');
numberAggregate.addNotNullQuery('number');
numberAggregate.addQuery('number', '!=', '');
numberAggregate.addAggregate('COUNT');
numberAggregate.groupBy('number');
numberAggregate.addHaving('COUNT', '>', 1);
numberAggregate.orderBy('number');
numberAggregate.query();
while (numberAggregate.next()) {
candidateNumbers.push(numberAggregate.getValue('number'));
}
log('Candidate duplicate numbers found: ' + candidateNumbers.length);
/******************************************************************
* STEP 2: BUILD THE REMEDIATION PLAN
******************************************************************/
for (var i = 0; i < candidateNumbers.length; i++) {
var duplicateNumber = candidateNumbers[i];
try {
analyzeNumber(duplicateNumber);
} catch (analysisError) {
error(
'Analysis failed for ' +
duplicateNumber +
'. No change planned for this number. Error: ' +
getErrorMessage(analysisError)
);
}
}
/******************************************************************
* STEP 3: PRINT SUMMARY AND ENFORCE GUARDRAILS
******************************************************************/
log('');
log('================ ANALYSIS SUMMARY ================');
log('Candidate duplicate numbers: ' + candidateNumbers.length);
log('Valid version chains skipped: ' + validVersionChains);
log('True duplicate numbers: ' + trueDuplicateNumbers);
log('Numbers skipped due to missing article_id: ' + skippedMissingArticleId);
log('Article families planned for renumbering: ' + plannedFamilyUpdates);
log('Knowledge rows planned for update: ' + plannedRowUpdates);
log('==================================================');
if (plannedFamilyUpdates === 0) {
log('No article families require renumbering.');
return;
}
if (plannedFamilyUpdates > MAX_FAMILIES_TO_RENUMBER) {
error(
'ABORTED: ' +
plannedFamilyUpdates +
' article families require renumbering, exceeding the configured limit of ' +
MAX_FAMILIES_TO_RENUMBER +
'. No records were changed.'
);
return;
}
if (!APPLY_CHANGES) {
log('');
log('PREVIEW COMPLETE. No records were updated.');
log('No new KB numbers were consumed.');
log('Review the PLAN entries above.');
log('Set APPLY_CHANGES = true only after validating the plan.');
return;
}
/******************************************************************
* STEP 4: EXECUTE REMEDIATION
******************************************************************/
log('');
log('================ EXECUTION STARTED ================');
for (var p = 0; p < remediationPlan.length; p++) {
executePlanItem(remediationPlan[p]);
}
/******************************************************************
* STEP 5: EXECUTION SUMMARY
******************************************************************/
log('');
log('================ EXECUTION SUMMARY ================');
log('Successful family updates: ' + successfulFamilyUpdates);
log('Successful row updates: ' + successfulRowUpdates);
log('Failed family updates: ' + failedFamilyUpdates);
log('===================================================');
/******************************************************************
* STEP 6: POST-VALIDATE ALL DUPLICATE NUMBERS
******************************************************************/
log('');
log('================ POST-VALIDATION ==================');
var remainingDuplicateCount = validateRemainingDuplicates();
if (remainingDuplicateCount === 0) {
log('POST-VALIDATION PASSED.');
log('No KB numbers remain assigned to multiple article_id families.');
} else {
error(
'POST-VALIDATION FOUND ' +
remainingDuplicateCount +
' KB number(s) still assigned to multiple article_id families.'
);
}
log('===================================================');
/******************************************************************
* ANALYSIS FUNCTIONS
******************************************************************/
function analyzeNumber(kbNumber) {
/*
* A missing article_id makes it impossible to safely distinguish
* version families. Skip the entire number rather than risk
* partially renumbering it.
*/
var missingArticleIdCount = countRowsMissingArticleId(kbNumber);
if (missingArticleIdCount > 0) {
skippedMissingArticleId++;
warn(
'SKIP ' +
kbNumber +
': ' +
missingArticleIdCount +
' record(s) have an empty article_id.'
);
return;
}
var families = getFamiliesForNumber(kbNumber);
/*
* One article_id means the repeated number belongs to one valid
* article version chain.
*/
if (families.length <= 1) {
validVersionChains++;
if (LOG_VALID_VERSION_CHAINS) {
log(
'VALID VERSION CHAIN: ' +
kbNumber +
' belongs to one article_id family.'
);
}
return;
}
trueDuplicateNumbers++;
/*
* Deterministic ordering:
* 1. Oldest sys_created_on
* 2. article_id as a tie breaker
*/
families.sort(compareFamilies);
var keepFamily = families[0];
log('');
log(
'TRUE DUPLICATE: ' +
kbNumber +
' belongs to ' +
families.length +
' article_id families.'
);
log(
'KEEP: number=' +
kbNumber +
', article_id=' +
keepFamily.articleId +
', rows=' +
keepFamily.rowCount +
', oldest=' +
keepFamily.oldestCreated
);
/*
* Every family after the oldest family receives a new number.
*/
for (var familyIndex = 1; familyIndex < families.length; familyIndex++) {
var family = families[familyIndex];
remediationPlan.push({
oldNumber: kbNumber,
articleId: family.articleId,
expectedRows: family.rowCount,
oldestCreated: family.oldestCreated,
newestCreated: family.newestCreated
});
plannedFamilyUpdates++;
plannedRowUpdates += family.rowCount;
log(
'PLAN: old_number=' +
kbNumber +
', article_id=' +
family.articleId +
', rows=' +
family.rowCount +
', oldest=' +
family.oldestCreated +
', new_number=<assigned during execution>'
);
}
}
function getFamiliesForNumber(kbNumber) {
var families = [];
/*
* GlideAggregate avoids reading reference display values or other
* unnecessary article fields during analysis.
*/
var familyAggregate = new GlideAggregate('kb_knowledge');
familyAggregate.addQuery('number', kbNumber);
familyAggregate.addNotNullQuery('article_id');
familyAggregate.addAggregate('COUNT');
familyAggregate.addAggregate('MIN', 'sys_created_on');
familyAggregate.addAggregate('MAX', 'sys_created_on');
familyAggregate.groupBy('article_id');
familyAggregate.query();
while (familyAggregate.next()) {
families.push({
articleId: familyAggregate.getValue('article_id'),
rowCount: parseInteger(
familyAggregate.getAggregate('COUNT')
),
oldestCreated: familyAggregate.getAggregate(
'MIN',
'sys_created_on'
),
newestCreated: familyAggregate.getAggregate(
'MAX',
'sys_created_on'
)
});
}
return families;
}
function countRowsMissingArticleId(kbNumber) {
var missingAggregate = new GlideAggregate('kb_knowledge');
missingAggregate.addQuery('number', kbNumber);
missingAggregate.addNullQuery('article_id');
missingAggregate.addAggregate('COUNT');
missingAggregate.query();
if (!missingAggregate.next()) {
return 0;
}
return parseInteger(missingAggregate.getAggregate('COUNT'));
}
/******************************************************************
* EXECUTION FUNCTIONS
******************************************************************/
function executePlanItem(planItem) {
var newNumber = '';
try {
/*
* Validate that the original family still exists before consuming
* a new number. This protects against changes between preview
* and execution.
*/
var currentRowCount = countFamilyRows(
planItem.oldNumber,
planItem.articleId
);
if (currentRowCount === 0) {
warn(
'SKIP EXECUTION: article_id=' +
planItem.articleId +
' no longer uses number ' +
planItem.oldNumber +
'.'
);
return;
}
if (currentRowCount !== planItem.expectedRows) {
warn(
'ROW COUNT CHANGED: article_id=' +
planItem.articleId +
', preview_rows=' +
planItem.expectedRows +
', current_rows=' +
currentRowCount +
'. Continuing with current family rows.'
);
}
/*
* Confirm this is still a true collision immediately before
* performing the update.
*/
var currentFamilies = getFamiliesForNumber(planItem.oldNumber);
if (currentFamilies.length <= 1) {
warn(
'SKIP EXECUTION: ' +
planItem.oldNumber +
' is no longer assigned to multiple article_id families.'
);
return;
}
newNumber = getSafeNextKnowledgeNumber();
log(
'UPDATE FAMILY: article_id=' +
planItem.articleId +
', old_number=' +
planItem.oldNumber +
', new_number=' +
newNumber +
', rows=' +
currentRowCount
);
/*
* The query is narrowly constrained by both article_id and
* the original number.
*
* updateMultiple avoids loading every version into the scripting
* engine individually.
*/
var updateFamily = new GlideRecord('kb_knowledge');
updateFamily.addQuery('article_id', planItem.articleId);
updateFamily.addQuery('number', planItem.oldNumber);
updateFamily.setValue('number', newNumber);
if (SUPPRESS_WORKFLOW) {
updateFamily.setWorkflow(false);
}
updateFamily.updateMultiple();
/*
* Validate both sides after the update.
*/
var remainingOldRows = countFamilyRows(
planItem.oldNumber,
planItem.articleId
);
var updatedNewRows = countFamilyRows(
newNumber,
planItem.articleId
);
if (remainingOldRows > 0) {
failedFamilyUpdates++;
error(
'VALIDATION FAILED: article_id=' +
planItem.articleId +
' still has ' +
remainingOldRows +
' row(s) using old number ' +
planItem.oldNumber +
'.'
);
return;
}
if (updatedNewRows !== currentRowCount) {
failedFamilyUpdates++;
error(
'VALIDATION FAILED: article_id=' +
planItem.articleId +
', expected_new_rows=' +
currentRowCount +
', actual_new_rows=' +
updatedNewRows +
', new_number=' +
newNumber +
'.'
);
return;
}
successfulFamilyUpdates++;
successfulRowUpdates += updatedNewRows;
log(
'SUCCESS: article_id=' +
planItem.articleId +
', old_number=' +
planItem.oldNumber +
', new_number=' +
newNumber +
', updated_rows=' +
updatedNewRows
);
} catch (executionError) {
failedFamilyUpdates++;
error(
'UPDATE FAILED: article_id=' +
planItem.articleId +
', old_number=' +
planItem.oldNumber +
', reserved_new_number=' +
newNumber +
', error=' +
getErrorMessage(executionError)
);
}
}
function getSafeNextKnowledgeNumber() {
var numberManager = new NumberManager('kb_knowledge');
for (var attempt = 1; attempt <= 50; attempt++) {
var candidate = numberManager.getNextObjNumberPadded();
if (!candidate) {
throw 'NumberManager returned an empty value.';
}
/*
* Protect against an existing database number and a number
* already reserved during this execution.
*/
if (
!reservedNumbers[candidate] &&
!knowledgeNumberExists(candidate)
) {
reservedNumbers[candidate] = true;
return candidate;
}
warn(
'Generated number already exists or is reserved: ' +
candidate +
'. Retrying.'
);
}
throw 'Unable to generate an unused KB number after 50 attempts.';
}
function knowledgeNumberExists(kbNumber) {
var numberCheck = new GlideAggregate('kb_knowledge');
numberCheck.addQuery('number', kbNumber);
numberCheck.addAggregate('COUNT');
numberCheck.query();
if (!numberCheck.next()) {
return false;
}
return parseInteger(numberCheck.getAggregate('COUNT')) > 0;
}
function countFamilyRows(kbNumber, articleId) {
var rowAggregate = new GlideAggregate('kb_knowledge');
rowAggregate.addQuery('number', kbNumber);
rowAggregate.addQuery('article_id', articleId);
rowAggregate.addAggregate('COUNT');
rowAggregate.query();
if (!rowAggregate.next()) {
return 0;
}
return parseInteger(rowAggregate.getAggregate('COUNT'));
}
/******************************************************************
* FINAL DUPLICATE VALIDATION
******************************************************************/
function validateRemainingDuplicates() {
var remainingCandidates = [];
var duplicateAggregate = new GlideAggregate('kb_knowledge');
duplicateAggregate.addNotNullQuery('number');
duplicateAggregate.addQuery('number', '!=', '');
duplicateAggregate.addAggregate('COUNT');
duplicateAggregate.groupBy('number');
duplicateAggregate.addHaving('COUNT', '>', 1);
duplicateAggregate.query();
while (duplicateAggregate.next()) {
remainingCandidates.push(
duplicateAggregate.getValue('number')
);
}
var trueDuplicatesRemaining = 0;
for (var i = 0; i < remainingCandidates.length; i++) {
var number = remainingCandidates[i];
if (countRowsMissingArticleId(number) > 0) {
warn(
'POST-VALIDATION SKIP: ' +
number +
' contains record(s) with empty article_id.'
);
continue;
}
var families = getFamiliesForNumber(number);
if (families.length > 1) {
trueDuplicatesRemaining++;
error(
'REMAINING DUPLICATE: ' +
number +
' is assigned to ' +
families.length +
' article_id families.'
);
}
}
return trueDuplicatesRemaining;
}
/******************************************************************
* UTILITY FUNCTIONS
******************************************************************/
function compareFamilies(a, b) {
if (a.oldestCreated < b.oldestCreated) {
return -1;
}
if (a.oldestCreated > b.oldestCreated) {
return 1;
}
/*
* Deterministic tie breaker when timestamps match.
*/
if (a.articleId < b.articleId) {
return -1;
}
if (a.articleId > b.articleId) {
return 1;
}
return 0;
}
function parseInteger(value) {
var parsed = parseInt(value, 10);
if (isNaN(parsed)) {
return 0;
}
return parsed;
}
function getErrorMessage(exception) {
if (!exception) {
return 'Unknown error';
}
if (exception.message) {
return exception.message;
}
return exception.toString();
}
function log(message) {
gs.print(LOG_PREFIX + message);
}
function warn(message) {
gs.warn(LOG_PREFIX + message);
}
function error(message) {
gs.error(LOG_PREFIX + message);
}
})();
this worked nicely for me, with an added check on changing the newer articles, preserving the number the older article. and preserving versions. i wanted it to prefer certain kb, but i had scope issues.