We've updated the ServiceNow Community Code of Conduct, adding guidelines around AI usage, professionalism, and content violations. Read more

De-duplication template scripted condition

maciep
Tera Contributor

Hi all,

 

I'm confused on how the condition script is applied for a de-duplication template.

 

Since the script is supposed to return an array of sys_ids of the de-duplication tasks, is the condition script used to actually assign the tasks to this template? Or does it actually do some filtering from the whole set? And if so, how? 

 

Does the condition script work in conjunction with the "Automatically select all de-duplication tasks that match the selected class" option? Because that makes it sound like the checkbox is doing the task assignment. So would the condition script even run or apply?

 

Admittedly, I stink at finding the explanatory documentation for ServiceNow. I can usually only find the "click these buttons" type of docs. So if this is explained in detail somewhere, feel free to link it to me.

 

Here is the script I'm testing with at the moment for reference (we want to ensure each dup ci in a task is in the computer class and share the same lifecycle stage/status).

 

(function() {
    // return array of task ids
    var validTasks = [];

    // loop through the tasks. We only need the sys_id of the task, since the logic is applied at the audit record
    new GlideQuery('reconcile_duplicate_task')
        .orderByDesc('sys_created_on')
        .limit(10)
        .select('sys_id')
        .forEach(function(t) {
            var sysId = t.sys_id;

            // would prefer to use the Set() object, but no can do here...so we'll use arrays instead
            // these will hold unique values across the CIs related to this task
            var lifecycleStage = [];
            var lifecycleStatus = [];
            var cmdbClass = [];


            // Get all the duplicate audit records associated with this duplication task
            // Validate their lifecycle and class values
            var grAudit = new GlideRecord('duplicate_audit_result');
            grAudit.addQuery('follow_on_task', sysId);
            grAudit.query();
            while (grAudit.next()) {
                // The duplicate_ci field of Document ID type so we're using getRefRecord
                var refRecord = grAudit.duplicate_ci.getRefRecord();

                // Get the fields we need from the duplicate_ci record
                // Add them to the appropriate array if they're not already in there
                var stage = refRecord.getDisplayValue('life_cycle_stage');
                var status = refRecord.getDisplayValue('life_cycle_stage_status');
                var className = refRecord.getDisplayValue('sys_class_name');

                if (stage && lifecycleStage.indexOf(stage) < 0) {
                    lifecycleStage.push(stage);
                }

                if (status && lifecycleStatus.indexOf(status) < 0) {
                    lifecycleStatus.push(status);
                }

                if (className && cmdbClass.indexOf(className) < 0) {
                    cmdbClass.push(className);
                }
            }

            // Now that each duplicate is proccssed, check our arrays to see if the values match across them all
            if (lifecycleStage.length === 1 && lifecycleStatus.length === 1 && cmdbClass.length === 1 && cmdbClass[0] == 'Computer') {
                validTasks.push(sysId);
            }
        });

    return validTasks;
})();

 

 

 

 

1 ACCEPTED SOLUTION

Hi maciep,

Think of that checkbox simply as the "Enable Automation" switch.

If you leave it unchecked, your script effectively sits dormant because the system won't trigger the scheduled job for this template. You would have to manually open tasks and apply the template one by one.

When you check the box, the system wakes up and checks: "Is there a condition script defined?"

  • If Yes: It runs your script and strictly uses the list of sys_ids you return (ignoring the default "select all" behavior).

  • If No: It falls back to selecting everything that matches the class.

So, short answer: You must check the box to make your script run automatically, and your script will take priority over the default selection logic.

If this clears up the workflow, please mark the answer as Accepted Solution.

Best regards, Brandão.

View solution in original post

4 REPLIES 4

Itallo Brandão
Tera Guru

Hi @maciep ,

The documentation on this can be confusing. Basically, think of the Condition Script as a Collector, not a row-level evaluator.

It runs once per scheduled execution and expects you to return an Array of SysIDs. The system then takes that list and applies the template logic to those specific records.

Looking at your code, I see a major logic trap: .limit(10). This is dangerous in production. It means you are only ever checking the 10 most recent tasks. If valid duplicate task #11 exists, your script will never see it because it stops at 10.

I've refactored your script below to remove that limit and added a check for State (so you don't waste performance looping through thousands of closed historical tasks).

 
(function() {
    var validTasks = [];

    // Query 'reconcile_duplicate_task'
    // Filter: State = Open (1) to save performance
    // Order: Newest first
    new GlideQuery('reconcile_duplicate_task')
        .where('state', '1') 
        .orderByDesc('sys_created_on')
        // .limit(10)  <-- REMOVED. This would block older tasks from ever being processed.
        .select('sys_id')
        .forEach(function(t) {
            var sysId = t.sys_id;
            
            // Track unique values
            var lifecycleStage = [];
            var lifecycleStatus = [];
            var cmdbClass = [];
            var isConsistent = true; 

            // Check Audit Results
            var grAudit = new GlideRecord('duplicate_audit_result');
            grAudit.addQuery('follow_on_task', sysId);
            grAudit.query();

            if (!grAudit.hasNext()) return;

            while (grAudit.next()) {
                var refRecord = grAudit.duplicate_ci.getRefRecord();
                
                // Validate if record exists
                if (!refRecord.isValidRecord()) {
                    isConsistent = false;
                    break;
                }

                var stage = refRecord.getValue('life_cycle_stage'); 
                var status = refRecord.getValue('life_cycle_stage_status');
                var className = refRecord.sys_class_name.toString();

                // Logic: If we find a mismatch, break the loop immediately (Optimization)
                if (cmdbClass.length > 0 && cmdbClass.indexOf(className) < 0) {
                    isConsistent = false;
                    break; 
                }

                // Push unique values
                if (lifecycleStage.indexOf(stage) < 0) lifecycleStage.push(stage);
                if (lifecycleStatus.indexOf(status) < 0) lifecycleStatus.push(status);
                if (cmdbClass.indexOf(className) < 0) cmdbClass.push(className);
            }

            // Final check: Must be exactly 1 Class (Computer) and 1 unique Stage/Status
            if (isConsistent && cmdbClass.length === 1 && cmdbClass[0] == 'cmdb_ci_computer' && lifecycleStage.length === 1 && lifecycleStatus.length === 1) {
                validTasks.push(sysId);
            }
        });

    return validTasks;
})();

By adding the .where('state', '1') and removing the limit, you ensure the job catches everything that needs attention without killing the instance memory.

If this script fixes your logic, please mark it as Accepted Solution.

Best regards,
Brandão.

Thanks Brandao. The limit is only since I'm testing. I didn't want to process all of them while figuring it out. But I did not know about the state field and I like the optimization recommendations.

 

Do you know if/how the "Automatically select all de-duplication tasks that match the selected class" checkbox is affects this condition script? If at all?

Hi maciep,

Think of that checkbox simply as the "Enable Automation" switch.

If you leave it unchecked, your script effectively sits dormant because the system won't trigger the scheduled job for this template. You would have to manually open tasks and apply the template one by one.

When you check the box, the system wakes up and checks: "Is there a condition script defined?"

  • If Yes: It runs your script and strictly uses the list of sys_ids you return (ignoring the default "select all" behavior).

  • If No: It falls back to selecting everything that matches the class.

So, short answer: You must check the box to make your script run automatically, and your script will take priority over the default selection logic.

If this clears up the workflow, please mark the answer as Accepted Solution.

Best regards, Brandão.

Ah ok, that does clear it up. Thanks!