Name
global.VAAISearchHelperUtah
Description
No description available
Script
var VAAISearchHelperUtah = Class.create();
// allowed max size of field value.
VAAISearchHelperUtah.ALLOWED_MAX_FIELD_VALUE_SIZE = 400; // this number is bigger than the card's display size.
//highlight start tag
VAAISearchHelperUtah.HIGHLIGHT_START_TAG = '<highlight>';
//highlight end tag
VAAISearchHelperUtah.HIGHLIGHT_END_TAG = '</highlight>';
// Field names that may need to resize the field value if required.
// If registered here, the field value will get resized if the size of the value exceeds the limit- VAAISearchHelperUtah.ALLOWED_MAX_FIELD_VALUE_SIZE
// add here more if needed.
VAAISearchHelperUtah.FIELD_NAMES_FOR_RESIZING
= ['description','header','title', 'name', 'summary', 'ai_search_teaser_text', 'ai_search_teaser_title' ,'columns.description','columns.name'];
// allowed max limit for the topic input
VAAISearchHelperUtah.RESULT_MAX_SIZE = parseInt(gs.getProperty('glide.cs.runtime.user_input_max_length'));
// result too large error
VAAISearchHelperUtah.RESULT_TOO_LARGE = 'result_too_large';
VAAISearchHelperUtah.processResults = function(searchResults, vaSystem, vaInputs, vaVars) {
var results = JSON.parse(searchResults);
vaSystem.setAtlasSearchPerformed();
var result = results.result[0];
var errors = [];
errors = results.result[0].errors;
if ((gs.nil(errors) || errors.length == 0) && (results.result[0].status != "200"))
errors.push({"errorType" : "Return Status", "message": "Return status was not 200"});
var executionResult = result.executionResult;
var geniusSearchResults = null;
var nonGeniusResults = null;
var searchMetadata = {};
if (errors.length == 0) {
// genius results
var anyGeniusResults = executionResult.geniusResultsTemplates.items.length > 0;
if (anyGeniusResults)
geniusSearchResults = executionResult.geniusResultsTemplates.items[0];
// regular results
var items = executionResult.searchResultsTemplates.items.slice(0,
parseInt(gs.getProperty("com.glide.cs.ai_search.max_regular_result")));
if (items.length > 0)
nonGeniusResults = items;
// search metadata
var searchInfo = {
search_id: VAClickMetrics.createGUID(),
is_logged_in: vaVars.is_logged_in,
user_id: vaVars.user_id,
session_id: vaVars.session_id
};
searchMetadata = VAAISearchHelperUtah.buildSearchMetadata(result, searchInfo);
// process the search result metrics.
VAAISearchHelperTokyo.processSearchResultMetrics(searchMetadata, geniusSearchResults, nonGeniusResults, vaSystem, vaInputs, vaVars);
}
var searchMetadataStr = !gs.nil(searchMetadata) ? JSON.stringify(searchMetadata) : null ;
var geniusResultsStr = !gs.nil(geniusSearchResults) ? JSON.stringify(geniusSearchResults) : null ;
var nonGeniusResultsStr = !gs.nil(nonGeniusResults) ? JSON.stringify(nonGeniusResults) : null;
var errorsStr = !gs.nil(errors) ? JSON.stringify(errors) : null;
return {
searchMetadata: searchMetadataStr,
geniusSearchResults: geniusResultsStr,
searchResults: nonGeniusResultsStr,
errors: errorsStr
};
};
VAAISearchHelperUtah.buildSearchMetadata = function(result, searchInfo) {
var executionResult = result.executionResult;
var searchMetadata = {};
searchMetadata.click_metrics = {};
searchMetadata.glide_signals = executionResult.searchMetadata;
// adding user_id and session_id to pass around user_id and session_id
// until the end of the flow for glide-signals's event tracking.
searchMetadata.is_logged_in = searchInfo.is_logged_in;
searchMetadata.user_id = searchInfo.user_id;
searchMetadata.session_id = searchInfo.session_id;
searchMetadata.search_id = searchInfo.search_id;
searchMetadata.search_result_count
= executionResult.geniusResultsTemplates.items.length + executionResult.searchResultsTemplates.items.length;
// the time taken reported by the payload.
searchMetadata.execution_time = result.executionTime;
return searchMetadata;
};
VAAISearchHelperUtah.createResultMap = function(searchMetadata, searchResults, vaSystem, vaInputs, vaVars) {
var logger = VAAISearchHelperTokyo.getLogger(vaVars.loggingContext);
try {
var response = JSON.parse(searchResults);
var groupedResultsMap = {};
var maxResults = VAAISearchHelperUtah.getRegularSearchDisplaySize(response);
for (var i = 0; i < maxResults; i++) {
var sysId = response[i].propValues.model.sysId;
var title = VAAISearchHelperUtah.buildSearchTitle(response[i].propValues);
groupedResultsMap[sysId] = GlideStringUtil.unEscapeHTML(title);
// create SEARCH_RESULT_DISPLAYED event.
// This createResultMap method can be called multiple times.
// So, we need to rely on this flag to avoid sending the same payload multiple times.
var shouldSend = vaVars.sendDisplayedSearchResultClickMetricsEvent;
if (!gs.nil(shouldSend) && shouldSend) {
var searchResult = response[i];
var metadata = JSON.parse(searchMetadata);
VAAISearchClickMetricsURLHelper.createDisplayedSearchResultEventMetrics(metadata, searchResult, vaSystem, vaInputs, vaVars, i);
}
}
return groupedResultsMap;
} catch(ex) {
logger.error('VAAISearchHelperUtah: Error generating regular search result map: {0}', ex);
// Return any links that have been successfully generated
return groupedResultsMap;
}
};
/**
* Generates Genius result card response
*/
VAAISearchHelperUtah.generateGeniusCardResponse = function(searchMetadata, geniusSearchResults, vaSystem, vaInputs, vaVars, dynamicChoiceNodeName) {
return global.VAAISearchHelperUtah.generateSearchCardResponse (
global.AISearchConstants.RESULT_TYPE_GENIUS, searchMetadata, geniusSearchResults, vaSystem, vaInputs, vaVars, dynamicChoiceNodeName, 0);
};
/**
* Generates Regular result card response
*/
VAAISearchHelperUtah.generateSearchResultsResponse = function(searchMetadata, searchResults, vaSystem, vaInputs, vaVars, dynamicChoiceNodeName) {
var logger = VAAISearchHelperTokyo.getLogger(vaVars.loggingContext);
logger.info('Generating search result response');
try {
var metadata = JSON.parse(searchMetadata);
var response = JSON.parse(searchResults);
// Only show first three search results - GroupedPartsOutMsg is limited to 3
var maxResults = VAAISearchHelperUtah.getRegularSearchDisplaySize(response);
for (var i = 0; i < maxResults; i++) {
var searchResult = response[i];
var propValues = searchResult.propValues;
var model = propValues.model;
if (model.sysId == vaInputs.display_options) {
var result = VAAISearchHelperTokyo.stripHighlightTags(JSON.stringify(searchResult));
return global.VAAISearchHelperUtah.generateSearchCardResponse (
global.AISearchConstants.RESULT_TYPE_SEARCH, searchMetadata, result, vaSystem, vaInputs, vaVars, dynamicChoiceNodeName, i);
}
}
return "";
} catch(ex) {
logger.error('VAAISearchHelperUtah: Error generating search response: {0}', ex);
// Return any links that have been successfully generated
return "";
}
};
VAAISearchHelperUtah.getSearchResults = function(searchResults, geniusSearchResults, searchMetadata, vaSystem) {
var resultsArr = [searchResults, geniusSearchResults, searchMetadata];
var resultsTooLarge = false;
for (var i = 0; i < resultsArr.length; i++) {
var result = JSON.parse(resultsArr[i]);
if (result.hasOwnProperty('error') && result.error === VAAISearchHelperUtah.RESULT_TOO_LARGE) {
resultsTooLarge = true;
}
}
if (!resultsTooLarge) {
return {
searchResults: searchResults,
geniusSearchResults: geniusSearchResults,
searchMetadata: searchMetadata,
errors: ''
};
}
return VAAISearchHelperUtah.getSearchResultsFromFDIH(vaSystem.getConversationId(), JSON.parse(searchMetadata));
};
/**
* Search results may be too large to pass from the Topic Block back to the calling topic.
* Grab the most recent search results from the table sys_cs_fdih_incovation for this conversation.
*
* @param conversationId
* @return {*[]} array of search results or empty array
*/
VAAISearchHelperUtah.getSearchResultsFromFDIH = function(conversationId, initialSearchMetadata) {
var fdihInvocationList = sn_cs.VASystemObject.getFDIHInvocationsFromConversation(conversationId);
var fdihInvocations = [];
// fdihInvocationList is a Java list; lets convert it to a JS list of objects so that we can sort
for (var i = 0; i<fdihInvocationList.length; i++) {
var invocation = fdihInvocationList[i];
if (invocation.name !== 'global.atlas_search')
continue;
fdihInvocations.push({
sysCreatedOn : invocation.sysCreatedOn,
outputsAsString: invocation.outputsAsString
});
}
// Sort by sys_created_on
fdihInvocations.sort(function (a, b) {
var gdtA = new GlideDateTime(a.sysCreatedOn);
var gdtB = new GlideDateTime(b.sysCreatedOn);
return gdtB.compareTo(gdtA);
});
var searchResults = [];
var geniusSearchResults = [];
var searchMetadata = [];
var errors = '';
try {
var rawResults = JSON.parse(fdihInvocations[0].outputsAsString);
var result = rawResults.search_result.result[0];
var executionResult = result.executionResult;
searchResults = executionResult.searchResultsTemplates.items;
geniusSearchResults = executionResult.geniusResultsTemplates.items[0];
// if search metadata isn't too large, no need to re-build it
searchMetadata = initialSearchMetadata;
if (initialSearchMetadata.hasOwnProperty("error") && initialSearchMetadata.error === VAAISearchHelperUtah.RESULT_TOO_LARGE) {
searchMetadata = VAAISearchHelperUtah.buildSearchMetadata(result, initialSearchMetadata);
}
} catch (error) {
VAAISearchHelperTokyo.getLogger().warn("No search results found for conversation: {0}", conversationId);
errors = error;
}
var searchMetadataStr = !gs.nil(searchMetadata) ? JSON.stringify(searchMetadata) : null ;
var geniusResultsStr = !gs.nil(geniusSearchResults) ? JSON.stringify(geniusSearchResults) : null ;
var searchResultsStr = !gs.nil(searchResults) ? JSON.stringify(searchResults) : null;
var errorsStr = !gs.nil(errors) ? JSON.stringify(errors) : null;
return {
searchMetadata: searchMetadataStr,
geniusSearchResults: geniusResultsStr,
searchResults: searchResultsStr,
errors: errorsStr
};
};
/**
* Returns the display size for the regular search response.
* @param results : regular search results
* @return the allowed max search result number to display.
*/
VAAISearchHelperUtah.getRegularSearchDisplaySize = function(results) {
var resultSize = gs.nil(results) ? 0 : Math.min(results.length,
parseInt(gs.getProperty("com.glide.cs.ai_search.max_regular_result")));
return Math.min(resultSize, 10);
};
VAAISearchHelperUtah.buildSearchTitle = function(props) {
var titleParts = [];
var titleFields = [
"prefix",
"titleEmoji",
"title"
];
for (var i in titleFields) {
var titleField = titleFields[i];
var titleFieldValue = props[titleField];
if (!gs.nil(titleFieldValue))
titleParts.push(titleFieldValue);
}
var title = titleParts.join(" ");
return VAAISearchHelperTokyo.stripHighlightTags(title);
};
VAAISearchHelperUtah.generateCustomResultList = function(searchMetadata, searchResults, header, vaSystem, vaInputs, vaVars, dynamicChoiceNodeName) {
var logger = VAAISearchHelperTokyo.getLogger(vaVars.loggingContext);
logger.info('Generating custom result list');
try {
var metadata = JSON.parse(searchMetadata);
var response = JSON.parse(searchResults);
var linkOutMsg = new sn_cs.GroupedPartsOutMsg();
linkOutMsg.setHeader(header);
// Only show first three search results - GroupedPartsOutMsg is limited to 3
var maxResults = VAAISearchHelperUtah.getRegularSearchDisplaySize(response);
for (var i = 0; i < maxResults; i++) {
var searchResult = response[i];
var propValues = searchResult.propValues;
var model = propValues.model;
var url = "";
if (model.sysId == vaInputs.display_options) {
// able to provide url to override
if (!gs.nil(searchResult.auto_resolution_url)) {
url = searchResult.auto_resolution_url;
}
if (gs.nil(url)) {
var actionName = propValues.clickAction.name;
var actionPayload = propValues.clickAction.actionPayload;
if (!gs.nil(actionPayload)) {
url = VAAISearchHelper.correctURLIfRequired(actionPayload.url);
url = !gs.nil(url) ? url : VAAISearchHelperTokyo.createURLByActionPayload(actionPayload.table, actionPayload.sysId, actionName, vaSystem, true, dynamicChoiceNodeName);
}
else
url = VAAISearchHelperTokyo.createURLByModel(model.table, model.sysId, vaSystem, true, dynamicChoiceNodeName);
}
// create a click metrics URL.
url = global.VAAISearchClickMetricsURLHelper.createSearchResultEventMetricsURL(metadata, searchResult, vaSystem, vaInputs, vaVars, url, i);
var context = VAAISearchHelperTokyo.buildSearchContext(propValues);
var title = VAAISearchHelperTokyo.buildSearchTitle(propValues);
var description = propValues.description;
logger.debug('Adding custom link response: title={0}, url={1}, description={2}, context={3}',
title, url, description, context);
linkOutMsg.addLinkPart(title, url, description, context);
return linkOutMsg;
}
}
} catch(ex) {
logger.error('VAAISearchHelperUtah: Error generating custom link response: {0}', ex);
// Return any links that have been successfully generated
return linkOutMsg;
}
};
VAAISearchHelperUtah.calculateResultsToDisplay = function(vaSystem, vaInputs, vaVars) {
var resultsToDisplay = "error";
try {
var errors = vaVars.errors;
if (gs.nil(vaVars.errors))
errors = [];
else {
errors = errors.split(',');}
var isError = vaVars.didFdihInvocationTimeOut ||
(gs.nil(vaVars.geniusResults) && gs.nil(vaVars.searchResults)) || errors.length > 0;
var isGeniusResult = global.VAAISearchHelperTokyo.shouldDisplayGeniusResult(vaVars.geniusResults, vaVars.loggingContext);
var isRegularResult = gs.nil(vaVars.geniusResults) && !gs.nil(vaVars.searchResults);
if (isError)
resultsToDisplay = "error";
else if(isGeniusResult)
resultsToDisplay = "geniusResult";
else if(isRegularResult)
resultsToDisplay = "regularResult";
} catch (e) {
global.VAAISearchHelperTokyo.getLogger(vaVars.loggingContext).warn('Unable to calculate which results to display, falling back to error branch.');
}
return resultsToDisplay;
};
/**
* Goes through the passed object recursively and resize the field values if required.
* The candidate field name has to be registered in VAAISearchHelperUtah.FIELD_NAMES_FOR_RESIZING
* For VA, it's important that the return's size is within the allowed size... If the size is bigger than what is allowed,
* we'll have to resize the result.
* This gets called at the end of AI Search topic block, as the size too large problem is for output variables from a topic block
*/
VAAISearchHelperUtah.resizeResultIfRequired = function(result, resultName, vaVars) {
if (gs.nil(result))
return null;
var maxLimit = VAAISearchHelperUtah.RESULT_MAX_SIZE;
var resultStr = JSON.stringify(result);
// if the result size is ok, then return the string.
if (resultStr.length <= maxLimit)
return resultStr;
// if not ok, then let's try to reduce the result size.
resizeResultIfRequired(result);
// Check the size again.
resultStr = JSON.stringify(result);
if (resultStr.length <= maxLimit)
return resultStr;
// special case for metadata - as it is the largest culprit for being too large
if (resultName == 'Search metadata' && !gs.nil(result.glide_signals) && !gs.nil(result.glide_signals.searchResultMetadata)
&& !gs.nil(result.glide_signals.searchResultMetadata.searchAnalyticsPayload)) {
result.glide_signals.searchResultMetadata.searchAnalyticsPayload.searchResults = [];
resultStr = JSON.stringify(result);
if (resultStr.length <= maxLimit) {
VAAISearchHelperTokyo.getLogger().warn("Search metadata was trimmed of search results to make sure it is less than the max input size");
return resultStr;
}
}
var error = { error: VAAISearchHelperUtah.RESULT_TOO_LARGE };
if (resultName == 'Search metadata') {
// these properties were created when processing the results initially
// we are adding them here so that they can be re-added back into the metadata later
error.search_id = result.search_id;
error.is_logged_in = result.is_logged_in;
error.user_id = result.user_id;
error.session_id = result.session_id;
}
// if still wrong, then log it and return error object
global.VAAISearchHelperTokyo.getLogger(
vaVars.loggingContext).error('The result of {0} exceeded the max input size:{1}, but currentSize:{2}', resultName, maxLimit, resultStr.length);
return JSON.stringify(error);
};
/**
* Tests if the filed value for the field name needs to be reduced if the size exceeds the limit.
*/
VAAISearchHelperUtah.shouldReduceFieldValueSize = function(fieldName) {
return !gs.nil(fieldName) && (VAAISearchHelperUtah.FIELD_NAMES_FOR_RESIZING.indexOf(fieldName.toLowerCase()) != -1);
};
/**
* Returns the reduced field value if the length exceeds the limit.
* The original value is returnedd if the length is within the limit.
*/
VAAISearchHelperUtah.getResizedFieldValue = function(fieldValue) {
if (gs.nil(fieldValue))
return fieldValue;
var rtnVal = fieldValue;
// should not break highlight tag. if exists in the field value,
// need to adjust the size carefully.
var fieldValLength = fieldValue.length;
var endPos = fieldValLength - 1 ; // index = length -1
if (fieldValLength > VAAISearchHelperUtah.ALLOWED_MAX_FIELD_VALUE_SIZE) {
endPos = VAAISearchHelperUtah.ALLOWED_MAX_FIELD_VALUE_SIZE - 1;
var indicesOfStartTag = allIndexOf(fieldValue, VAAISearchHelperUtah.HIGHLIGHT_START_TAG);
if(indicesOfStartTag.length >0 ) {
// if start tags exist, find end tags , too.
var indicesOfEndTag = allIndexOf(fieldValue, VAAISearchHelperUtah.HIGHLIGHT_END_TAG);
// check if the end position is between startTag and endTag
// if that happens, we need to include the endTag in order not to break the tags.
// we don't want to remove tags here since it can be utilized by client in the future.
endPos = ajdustEndPosition(indicesOfStartTag, indicesOfEndTag);
}
//substring the original value with the new end position.
if (endPos < fieldValLength)
rtnVal = rtnVal.substring(0, endPos);
}
return rtnVal;
};
VAAISearchHelperUtah.generateSearchCardResponse = function(resultType, searchMetadata, searchResults, vaSystem, vaInputs, vaVars, dynamicChoiceNodeName, order) {
var metadata = JSON.parse(searchMetadata);
var searchResponse = JSON.parse(searchResults);
var index = order;
VAAISearchHelperTokyo.getLogger(vaVars.loggingContext).info('Generating search result - result type:{0}', resultType);
var cardCreator = global.VASearchCardCreatorFactoryUtah.getCreator(resultType, metadata, searchResponse, vaSystem, vaInputs, vaVars, index, dynamicChoiceNodeName);
// check if providing url
if (!gs.nil(searchResponse.auto_resolution_url))
cardCreator.setURL(searchResponse.auto_resolution_url);
return cardCreator.createCard();
};
/**
* Traverse through the passed object recursively and readjust the field value length for the pre-determined field name.
*/
function resizeResultIfRequired(obj, fieldName) {
if (gs.nil(obj))
return null;
if (typeof obj === 'string' || obj instanceof String) {
// if the passed object is string, check the length and return the reduced value if needed.
if (VAAISearchHelperUtah.shouldReduceFieldValueSize(fieldName))
return VAAISearchHelperUtah.getResizedFieldValue(obj);
}
else if (Array.isArray(obj)) {
// if the object is an array, traverse all the elements.
for (var i=0; i< obj.length; i++)
resizeResultIfRequired(obj[i]);
}
else if (typeof obj ==='object') {
for (var name in obj) {
var element = obj[name];
var newVal = resizeResultIfRequired(element, name);
// if the newVal is not null, the value could have been resized. update the object value.
if (!gs.nil(newVal))
obj[name] = newVal;
}
}
return null;
}
/**
* Find all string occurences
*/
function allIndexOf(src, searchStr) {
var indices = [];
for (var pos = src.indexOf(searchStr); pos !== -1; pos = src.indexOf(searchStr, pos + 1))
indices.push(pos);
return indices;
}
/**
* Adjust the end position if the max limit exists between highlight start and end tag.
* highlight start and end tags need to be always guaranteed in pairs
*/
function ajdustEndPosition(indicesOfStartTag, indicesOfEndTag) {
var startTagIndex; // index of highlight start tag
var endTagIndex; // index of highlight end tag
// the end position index. index = max length -1
var endPos = VAAISearchHelperUtah.ALLOWED_MAX_FIELD_VALUE_SIZE -1;
// to be self depensive.
if (endPos<0 )
return 0;
// traverse all both indices and find if the end position is in between startTagIndex and endTagIndex
// if the sizes are different, stop looping at the max one for both.
for (var i=0; i<indicesOfStartTag.length && i<indicesOfEndTag.length; i++) {
startTagIndex = indicesOfStartTag[i];
endTagIndex = indicesOfEndTag[i];
if (startTagIndex <= endPos && endTagIndex >= endPos) {
// return the new end position. It needs to include the entire end tag
endPos = endTagIndex + VAAISearchHelperUtah.HIGHLIGHT_END_TAG.length;
break;
}
else if (endTagIndex <= endPos && endTagIndex + VAAISearchHelperUtah.HIGHLIGHT_END_TAG.length >= endPos) {
// return the new end position. It needs to include the entire end tag
endPos = endTagIndex + VAAISearchHelperUtah.HIGHLIGHT_END_TAG.length;
break;
}
}
return endPos;
}
Sys ID
0e362ca7ebb41110506f7558b5522840