Name

global.MatchingRuleProcessor

Description

Utility to process matching rules.

Script

var MatchingRuleProcessor = Class.create();
MatchingRuleProcessor.prototype = {
  initialize: function(resourceType) {
  	if(!gs.nil(resourceType) && gs.tableExists(resourceType))
  		this.resourceType = resourceType;
  	else
  		this.resourceType = "sys_user";
  },
  getUsersInSchedule : function (candidates){
  	var inScheduleCandidates = [];
  	var notInScheduleCandidates = [];
  	var arrayUtil = new global.ArrayUtil();

  	var agents = new GlideRecord(this.resourceType);
  	agents.addEncodedQuery("sys_idIN"+candidates.join());
  	agents.addActiveQuery();
  	agents.query();
  	var tzScheduleMap = {};
  	while(agents.next()){
  		var schedule = agents.getValue("schedule");
  		var currentTime = new GlideDateTime();

  		if(schedule){
  			var sched = new GlideSchedule(schedule, agents.getValue("time_zone"));
  			var time_zone = agents.getValue("time_zone") ? agents.getValue("time_zone") : sched.getTimeZone();
  			var isInSchedule = false;
  			var key = schedule+"--"+time_zone;
  			if(tzScheduleMap.hasOwnProperty(key)){
  				isInSchedule = tzScheduleMap[key];
  			}
  			else{
  				isInSchedule = sched.isInSchedule(currentTime,time_zone);
  				tzScheduleMap[key] = isInSchedule;
  			}
  			if(isInSchedule)
  				inScheduleCandidates.push(agents.getValue("sys_id"));
  			else
  				notInScheduleCandidates.push(agents.getValue("sys_id"));
  		}
  	}
  	var finalCandidates = arrayUtil.diff(candidates, notInScheduleCandidates);
  	return finalCandidates;
  },

  isRecordMatchingConditions : function(taskRecord, matching_table, matching_conditions){
  	if (JSUtil.nil(matching_conditions)) //if no conditions then return true
  		return true;
  	if(taskRecord.getTableName() == matching_table){
  		var filter = new GlideFilter(matching_conditions, "rule-condition");
  		filter.setCaseSensitive(true);
  		var match = filter.match(taskRecord, true);
  		return match;
  	}
  	return false;
  },

  getUserFromMatchingRule: function(supply_table, supply_condition, supply_field_name, rule_sys_id, taskRecord){
  	var candidates = [];
  	var candidatesMap = {};
  	var supplyGR = new GlideRecord(supply_table);
  	supplyGR.addEncodedQuery(supply_condition);
  	//supplyGR.groupBy(supply_field_name);
  	supplyGR.query();
  	while(supplyGR.next()){
  		var candidate = supplyGR.getValue(supply_field_name);
  		if(!candidatesMap.hasOwnProperty(candidate))
  		{
  			candidatesMap[candidate] = true;
  			candidates.push(candidate);
  		}
  	}
  	
  	return candidates;
  },
  
  rankDimensionResultsAndGetCandidates : function(dimensionResults){
  	if(!dimensionResults || !dimensionResults.overallRating)
  		return [];
  	var overallMap = dimensionResults.overallRating;
  	
  	var tuples = [];
  	for (var user in overallMap){
  		tuples.push([user, overallMap[user]]);
  	}
  	tuples.sort(function(a, b) {
  	    a = a[1];
  	    b = b[1];
  	
  	    return a < b ? 1 : (a > b ? -1 : 0);
  	});
  	var returnUsers = [];
  	for (var i = 0; i < tuples.length; i++) {
  	    var key = tuples[i][0];
  	    var value = tuples[i][1];
  		returnUsers.push(key);
  	}
  	return returnUsers;
  },
  
  processDimensions: function(candidates, rule_sys_id, taskRecord, args, dimensionIds){
  	var returnUsers = [];
  	var dimensions = new GlideRecord("matching_dimension_for_assignment");
  	dimensions.addQuery("matching_rule", rule_sys_id);
  	if(dimensionIds && dimensionIds.length > 0)
  		dimensions.addQuery('dimension','IN',dimensionIds);
  	dimensions.addQuery("selected", true);
  	dimensions.orderBy("order");
  	dimensions.query();
  	if(dimensions.getRowCount() == 0)
  		return candidates;
  	var dimensionsObj = {};
  	while(dimensions.next())
  	{
  		var dimensionObj = {
  			"dimension_type":dimensions.getValue("dimension_type"), 
  			"weight":dimensions.getValue("weight"),
  			"threshold":dimensions.getValue("threshold"),
  			"ranking_method":dimensions.getValue("ranking_method"),
  			"taskFieldValues":[],
  			"args":args
  		};
  		dimensionsObj[dimensions.getValue("dimension")] = dimensionObj;
  	}
  	var dimensionResults = new MatchingDimensionProcessor().processDimensions(taskRecord, taskRecord.getTableName(), dimensionsObj, candidates, this.resourceType);
  	this.dimensionResults = dimensionResults;
  	returnUsers = this.rankDimensionResultsAndGetCandidates(dimensionResults);
  	return returnUsers;
  },
  
  getDimensionResults : function() {
  	return this.dimensionResults;
  },

  getUserFromMatchingRuleScript: function(rule, taskRecord, args){
  	var candidates = [];
  	var map = {};
  	map.taskRecord = taskRecord;
  	map.current = taskRecord;
  	map.contextRecord = taskRecord;
  	map.args = args;
  	var evaluator = new GlideScopedEvaluator();
  	candidates = evaluator.evaluateScript(rule, "script", map);
  	
  	return candidates;
  },
  
  processAndGetResources : function (contextRecord, userLimit, resource_table, filterArray){
  	var matchingRuleRecord = this.getMatchingRuleForContext(contextRecord, contextRecord.getTableName(), resource_table);
  	while(matchingRuleRecord.next()){
  		var contextMatch = this.isRecordMatchingConditions(contextRecord, matchingRuleRecord.getValue("table"), matchingRuleRecord.getValue('condition'));
  		if(contextMatch){
  			var candidates = this.processRuleRecord(contextRecord, matchingRuleRecord, userLimit, filterArray);
  			if(candidates.length > 0) {
  				return candidates;
  			}
  		}
  	}
  	return [];
  },
  processAndGetResourcesWithArgs : function (contextRecord, userLimit, resource_table, args){
  	var matchingRuleRecord = this.getMatchingRuleForContext(contextRecord, contextRecord.getTableName(), resource_table);
  	while(matchingRuleRecord.next()){
  		var contextMatch = this.isRecordMatchingConditions(contextRecord, matchingRuleRecord.getValue("table"), matchingRuleRecord.getValue('condition'));
  		if(contextMatch){
  			var candidates = this.processRuleRecord(contextRecord, matchingRuleRecord, userLimit, null, args);
  			if(candidates.length > 0) {
  				return candidates;
  			}
  		}
  	}
  	return [];
  },
  getMatchingRuleForContext : function (contextRecord, tableName, resource_table, query, sys_class_name){
  	if(JSUtil.nil(resource_table) || resource_table.length == 0)
  		resource_table = "sys_user";
  	if(JSUtil.nil(sys_class_name))
  		sys_class_name = "matching_rule";	
  	var matchingRuleRecord = new GlideRecord(sys_class_name);
  	matchingRuleRecord.addActiveQuery();
  	matchingRuleRecord.addQuery("table", tableName);
  	if(query)
  		matchingRuleRecord.addEncodedQuery(query);
  	if(resource_table == "sys_user")
  		matchingRuleRecord.addEncodedQuery("resource_type_table="+resource_table+"^ORresource_type_tableISEMPTY");
  	else
  		matchingRuleRecord.addQuery("resource_type_table", resource_table);
  	matchingRuleRecord.orderBy("order");
  	matchingRuleRecord.query();
  	return matchingRuleRecord;
  },

  processRule : function(taskRecord, matchingRule, userLimit){
  	var matchingRuleRecord = new GlideRecord("matching_rule");
  	matchingRuleRecord.addQuery("sys_id",matchingRule);
  	matchingRuleRecord.addActiveQuery();
  	matchingRuleRecord.query();
  	if(matchingRuleRecord.next())
  		return this.processRuleRecord(taskRecord,matchingRuleRecord,userLimit);
  },
  
  _checkIfMatchingRuleIsForUsers : function (matchingRuleRecord) {
  	
  	return (matchingRuleRecord.getValue("resource_type_table") == this.resourceType);
  },
  
  _checkIfMatchingRuleHasDimensions : function (matchingRuleRecord) {
  	var dimensions = new GlideRecord("matching_dimension_for_assignment");
  	dimensions.addQuery("matching_rule", matchingRuleRecord.getValue("sys_id"));
  	dimensions.addQuery("selected", true);;
  	dimensions.query();
  	if(dimensions.getRowCount() > 0)
  		return true;
  	return false;
  },

  processRuleRecord : function(taskRecord, matchingRuleRecord, userLimit, filterArray, args, dimensionIds){
  	var arrayUtil = new global.ArrayUtil();
  	if(!userLimit)
  		userLimit = 1;
  	if(!args)
  		args = {};
  	var candidates = [];
  	var matching_options = matchingRuleRecord.getValue("matching_options");
  	if(matching_options == "simple"){
  		candidates.push(matchingRuleRecord.getValue("user"));
  	}
  	else if(matching_options == "dynamic"){
  		var fieldName = matchingRuleRecord.getValue("supply_user_field_name");
  		if(!fieldName)
  			fieldName = "sys_id";
  		candidates = this.getUserFromMatchingRule(matchingRuleRecord.getValue("supply_user_table"), matchingRuleRecord.getValue('supply_user_condition'), fieldName, matchingRuleRecord.getValue("sys_id"), taskRecord);
  	}
  	else if(matching_options == "script"){
  		candidates = this.getUserFromMatchingRuleScript(matchingRuleRecord, taskRecord, args);
  	}
  	else if(matching_options == "recommendation criteria" && this._checkIfMatchingRuleIsForUsers(matchingRuleRecord) && this._checkIfMatchingRuleHasDimensions(matchingRuleRecord)) {
  		if(candidates.length == 0)
  			candidates = filterArray;
  		candidates = this.processDimensions(candidates, matchingRuleRecord.getValue("sys_id"), taskRecord, args, dimensionIds);
  	}
  	
  	var shouldUseSchedule = matchingRuleRecord.getValue("schedule_filter");
  	if(shouldUseSchedule > 0 && this._checkIfMatchingRuleIsForUsers(matchingRuleRecord) && !this._checkIfMatchingRuleHasDimensions(matchingRuleRecord)){
  		candidates = this.getUsersInSchedule(candidates);
  	}
  	
  	
  	
  	if(!JSUtil.nil(filterArray))
  		candidates = arrayUtil.intersect(candidates,filterArray);
  	
  	var finalCandidates = candidates;
  	if(finalCandidates)
  		finalCandidates = finalCandidates.slice(0, userLimit);
  	return finalCandidates;
  },

  processAndGetCandidates: function(taskRecord, userLimit, tableName, mode, all_match, filterArray, argsForDimensions, sys_class_name,matching_rule_filter) {
  	var arrayUtil = new global.ArrayUtil();
  	var finalCandidates = [];
      var matching_rule_query = false;
  	if(JSUtil.nil(mode))
  		mode = "forward";

  	if(JSUtil.nil(all_match))
  		all_match = false;

  	if(JSUtil.nil(tableName))
  		tableName = taskRecord.getTableName();
  	if(!JSUtil.nil(matching_rule_filter))
          matching_rule_query = matching_rule_filter;
  	var matchingRuleRecord = this.getMatchingRuleForContext(taskRecord, tableName, this.resourceType, matching_rule_query, sys_class_name);
  	this.matchingRules = [];
  	while(matchingRuleRecord.next()){
  		var ruleCandidates = [];
  		var candidates = [];
  		var index = -1;
  		if(mode == "reverse"){
  			var userRecord = taskRecord;
  			var matching_options = matchingRuleRecord.getValue("matching_options");
  			if((matching_options == "recommendation criteria" || matching_options == "script") && this._checkIfMatchingRuleIsForUsers(matchingRuleRecord) && this._checkIfMatchingRuleHasDimensions(matchingRuleRecord)) {
  				for(var j = 0; j<filterArray.length && j<gs.getProperty("reverse.matchingrule.entity.limit", 30); j++){
  					var taskInFilter = new GlideRecord(tableName);
  					taskInFilter.get(filterArray[j]);
  					candidates = this.processRuleRecord(taskInFilter, matchingRuleRecord, 999999, [userRecord.getValue("sys_id")]);
  					index = arrayUtil.indexOf(candidates, userRecord.getValue("sys_id"), 0);
  					if(index >= 0){
  						break;
  					} else {
  						candidates = [];
  					}
  				}
  			}else if(matching_options == "simple" || matching_options == "dynamic"){
  				candidates = this.processRuleRecord(userRecord, matchingRuleRecord, 999999);
  				index = arrayUtil.indexOf(candidates, userRecord.getValue("sys_id"), 0);
  			}
  			//var index = arrayUtil.indexOf(candidates, userRecord.getValue("sys_id"), 0);
  			if(index >= 0){
  				
  				var tasks = new GlideRecord(tableName);
  				tasks.addEncodedQuery(matchingRuleRecord.getValue('condition'));
  				
  				if(!JSUtil.nil(filterArray))
  					tasks.addEncodedQuery("sys_idIN"+filterArray.join());
  				
  				tasks.query();
  				while(tasks.next()){
  					ruleCandidates.push(tasks.getValue("sys_id"));
  				}
  				
  				if(!JSUtil.nil(filterArray))
  					ruleCandidates = arrayUtil.intersect(filterArray,ruleCandidates);
  			}	
  		}
  		else
  		{
  			var taskMatch = this.isRecordMatchingConditions(taskRecord, matchingRuleRecord.getValue("table"), matchingRuleRecord.getValue('condition'));
  			if(taskMatch){
  			    this.matchingRules.push(matchingRuleRecord.getValue("name"));
  				ruleCandidates = this.processRuleRecord(taskRecord, matchingRuleRecord, userLimit, filterArray, argsForDimensions);
  				
  			}
  		}

  		if(ruleCandidates.length > 0){
  			finalCandidates = arrayUtil.concat(finalCandidates,ruleCandidates);
  			if(!all_match)
  				break;
  		}
  		
  	}
  	var candidateMap = {};
  	var uniqueCandidates = [];
  	for (var i = 0; i < finalCandidates.length; i++) {
  		var candidate = finalCandidates[i];
  		if(!candidateMap.hasOwnProperty(candidate))
  		{
  			candidateMap[candidate] = true;
  			uniqueCandidates.push(candidate);
  		}
  	}
  	return uniqueCandidates;
  },

  type: 'MatchingRuleProcessor'
}

Sys ID

796e0940c33002001c845cb981d3ae59

Offical Documentation

Official Docs: