in x-pack/platform/plugins/shared/ml/server/models/data_frame_analytics/analytics_manager.ts [348:570]
private async getAnalyticsMap({
analyticsId,
modelId,
}: GetAnalyticsMapArgs): Promise<AnalyticsMapReturnType> {
const result: AnalyticsMapReturnType = { elements: [], details: {}, error: null };
const modelElements: MapElements[] = [];
const indexPatternElements: MapElements[] = [];
try {
await this.initData();
// Create first node for incoming analyticsId or modelId
let initialData: InitialElementsReturnType = Object.create(null) as InitialElementsReturnType;
const job = analyticsId === undefined ? undefined : this.findJob(analyticsId);
if (analyticsId !== undefined && job !== undefined) {
const jobCreateTime = job.create_time!;
initialData = await this.getInitialElementsJobRoot(analyticsId, jobCreateTime);
} else if (modelId !== undefined) {
initialData = await this.getInitialElementsModelRoot(modelId);
}
const {
resultElements,
details: initialDetails,
modelElements: initialModelElements,
} = initialData;
result.elements.push(...resultElements);
result.details = initialDetails;
modelElements.push(...initialModelElements);
if (isCompleteInitialReturnType(initialData)) {
let { data, nextLinkId, nextType, previousNodeId } = initialData;
let complete = false;
let link: NextLinkReturnType;
let count = 0;
let rootTransform;
let rootIndexPattern;
let modelElement;
let modelDetails;
let edgeElement;
// Add a safeguard against infinite loops.
while (complete === false) {
count++;
if (count >= 100) {
break;
}
try {
link = await this.getNextLink({
id: nextLinkId,
type: nextType,
});
} catch (error) {
result.error = error.message || 'Something went wrong';
break;
}
// If it's index pattern, check meta data to see what to fetch next
if (isIndexPatternLinkReturnType(link) && link.isIndexPattern === true) {
if (link.isWildcardIndexPattern === true) {
// Create index nodes for each of the indices included in the index pattern then break
const { details, elements } = this.getIndexPatternElements(
link.indexData,
previousNodeId
);
indexPatternElements.push(...elements);
result.details = { ...result.details, ...details };
complete = true;
} else {
const nodeId = `${nextLinkId}-${JOB_MAP_NODE_TYPES.INDEX}`;
result.elements.unshift({
data: { id: nodeId, label: nextLinkId, type: JOB_MAP_NODE_TYPES.INDEX },
});
result.details[nodeId] = link.indexData;
}
// Check meta data
if (
link.isWildcardIndexPattern === false &&
(link.meta === undefined ||
link.meta?.created_by.includes(INDEX_CREATED_BY.FILE_DATA_VISUALIZER))
) {
rootIndexPattern = nextLinkId;
complete = true;
break;
}
if (link.meta?.created_by === INDEX_CREATED_BY.DATA_FRAME_ANALYTICS) {
nextLinkId = link.meta.analytics;
nextType = JOB_MAP_NODE_TYPES.ANALYTICS;
}
if (link.meta?.created_by === JOB_MAP_NODE_TYPES.TRANSFORM) {
nextLinkId = link.meta._transform?.transform;
nextType = JOB_MAP_NODE_TYPES.TRANSFORM;
}
} else if (isJobDataLinkReturnType(link) && link.isJob === true) {
// Create missing job node here if job is undefined
data = link.jobData;
const nodeId = `${data?.id ?? nextLinkId}-${JOB_MAP_NODE_TYPES.ANALYTICS}`;
previousNodeId = nodeId;
result.elements.unshift(
data === undefined
? this.getMissingJobNode(nextLinkId)
: {
data: {
id: nodeId,
label: data.id,
type: JOB_MAP_NODE_TYPES.ANALYTICS,
analysisType: getAnalysisType(data?.analysis),
},
}
);
result.details[nodeId] = data;
nextLinkId = data?.source?.index[0];
nextType = JOB_MAP_NODE_TYPES.INDEX;
if (data) {
// Get trained model for analytics job and create model node
({ modelElement, modelDetails, edgeElement } = this.getAnalyticsModelElements(
data.id,
data.create_time
));
if (isAnalyticsMapNodeElement(modelElement)) {
modelElements.push(modelElement);
result.details[modelElement.data.id] = modelDetails;
}
if (isAnalyticsMapEdgeElement(edgeElement)) {
modelElements.push(edgeElement);
}
}
} else if (isTransformLinkReturnType(link) && link.isTransform === true) {
data = link.transformData;
const nodeId = `${data.id}-${JOB_MAP_NODE_TYPES.TRANSFORM}`;
previousNodeId = nodeId;
rootTransform = data.dest.index;
result.elements.unshift({
data: { id: nodeId, label: data.id, type: JOB_MAP_NODE_TYPES.TRANSFORM },
});
result.details[nodeId] = data;
nextLinkId = data?.source?.index[0];
nextType = JOB_MAP_NODE_TYPES.INDEX;
}
} // end while
// create edge elements
const elemLength = result.elements.length - 1;
for (let i = 0; i < elemLength; i++) {
const currentElem = result.elements[i];
const nextElem = result.elements[i + 1];
if (
currentElem !== undefined &&
nextElem !== undefined &&
currentElem?.data?.id.includes('*') === false &&
nextElem?.data?.id.includes('*') === false
) {
result.elements.push({
data: {
id: `${currentElem.data.id}~${nextElem.data.id}`,
source: currentElem.data.id,
target: nextElem.data.id,
},
});
}
}
// fetch all jobs associated with root transform if defined, otherwise check root index
if (rootTransform !== undefined || rootIndexPattern !== undefined) {
const jobs = this._jobs;
const comparator = rootTransform !== undefined ? rootTransform : rootIndexPattern;
for (let i = 0; i < jobs.length; i++) {
if (
jobs[i]?.source?.index[0] === comparator &&
this.isDuplicateElement(jobs[i].id, result.elements) === false
) {
const nodeId = `${jobs[i].id}-${JOB_MAP_NODE_TYPES.ANALYTICS}`;
result.elements.push({
data: {
id: nodeId,
label: jobs[i].id,
type: JOB_MAP_NODE_TYPES.ANALYTICS,
analysisType: getAnalysisType(jobs[i]?.analysis),
},
});
result.details[nodeId] = jobs[i];
const source = `${comparator}-${JOB_MAP_NODE_TYPES.INDEX}`;
result.elements.push({
data: {
id: `${source}~${nodeId}`,
source,
target: nodeId,
},
});
// Get trained model for analytics job and create model node
({ modelElement, modelDetails, edgeElement } = this.getAnalyticsModelElements(
jobs[i].id,
jobs[i].create_time!
));
if (isAnalyticsMapNodeElement(modelElement)) {
modelElements.push(modelElement);
result.details[modelElement.data.id] = modelDetails;
}
if (isAnalyticsMapEdgeElement(edgeElement)) {
modelElements.push(edgeElement);
}
}
}
}
}
// Include model and index pattern nodes in result elements now that all other nodes have been created
result.elements.push(...modelElements, ...indexPatternElements);
return result;
} catch (error) {
result.error = error.message || 'An error occurred fetching map';
return result;
}
}