"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.calcTimes = calcTimes; exports.comparator = void 0; exports.initTree = initTree; exports.normalizeBreakdown = normalizeBreakdown; exports.normalizeIndices = normalizeIndices; exports.normalizeTime = normalizeTime; exports.sortIndices = void 0; exports.timeInMilliseconds = timeInMilliseconds; var _i18n = require("@kbn/i18n"); var _tinycolor = _interopRequireDefault(require("tinycolor2")); var _lodash = _interopRequireDefault(require("lodash")); var _constants = require("./constants"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ const comparator = (v1, v2) => { if (v1 < v2) { return 1; } return v1 > v2 ? -1 : 0; }; exports.comparator = comparator; function getToolTip(key) { switch (key) { case 'build_scorer': return _i18n.i18n.translate('xpack.searchProfiler.buildScorerTimeDescription', { defaultMessage: 'The time taken to create the Scoring object, which is later used to execute the actual scoring of each doc.' }); case 'create_weight': return _i18n.i18n.translate('xpack.searchProfiler.createWeightTimeDescription', { defaultMessage: 'The time taken to create the Weight object, which holds temporary information during scoring.' }); case 'next_doc': return _i18n.i18n.translate('xpack.searchProfiler.nextDocTimeDescription', { defaultMessage: 'The time taken to advance the iterator to the next matching document.' }); case 'score': return _i18n.i18n.translate('xpack.searchProfiler.scoreTimeDescription', { defaultMessage: 'The time taken in actually scoring the document against the query.' }); case 'match': return _i18n.i18n.translate('xpack.searchProfiler.matchTimeDescription', { defaultMessage: 'The time taken to execute a secondary, more precise scoring phase (used by phrase queries).' }); case 'advance': return _i18n.i18n.translate('xpack.searchProfiler.advanceTimeDescription', { defaultMessage: 'The time taken to advance the iterator to the next document.' }); default: return ''; } } function timeInMilliseconds(data) { if (data.time_in_nanos) { return data.time_in_nanos / 1000000; } if (typeof data.time === 'string') { return Number(data.time.replace('ms', '')); } return Number(data.time); } function calcTimes(data, parentId) { let totalTime = 0; // First pass to collect total for (const child of data) { totalTime += timeInMilliseconds(child); child.breakdown = normalizeBreakdown(child.breakdown); let childrenTime = 0; if (child.children != null && child.children.length !== 0) { childrenTime = calcTimes(child.children, child.id); child.hasChildren = true; } child.selfTime = timeInMilliseconds(child) - childrenTime; } return totalTime; } function normalizeBreakdown(breakdown) { const final = []; const total = Object.keys(breakdown).reduce((partialTotal, currentKey) => { if (currentKey.indexOf('_count') === -1) { partialTotal += breakdown[currentKey]; } return partialTotal; }, 0); Object.keys(breakdown).sort().forEach(key => { let relative = 0; if (key.indexOf('_count') === -1) { relative = (breakdown[key] / total * 100).toFixed(1); } final.push({ key, time: breakdown[key], relative, color: _tinycolor.default.mix('#F5F5F5', '#FFAFAF', relative).toHexString(), tip: getToolTip(key) }); }); // Sort by time descending and then key ascending return final.sort((a, b) => { if (comparator(a.time, b.time) !== 0) { return comparator(a.time, b.time); } return -1 * comparator(a.key, b.key); }); } function normalizeIndices(indices, target) { // Sort the shards per-index let sortQueryComponents; if (target === 'searches') { sortQueryComponents = (a, b) => { const aTime = _lodash.default.sumBy(a.searches, search => { return search.treeRoot.time; }); const bTime = _lodash.default.sumBy(b.searches, search => { return search.treeRoot.time; }); return comparator(aTime, bTime); }; } else if (target === 'aggregations') { sortQueryComponents = (a, b) => { const aTime = _lodash.default.sumBy(a.aggregations, agg => { return agg.treeRoot.time; }); const bTime = _lodash.default.sumBy(b.aggregations, agg => { return agg.treeRoot.time; }); return comparator(aTime, bTime); }; } for (const index of Object.values(indices)) { index.shards.sort(sortQueryComponents); for (const shard of index.shards) { shard.relative = (shard.time / index.time * 100).toFixed(2); shard.color = _tinycolor.default.mix('#F5F5F5', '#FFAFAF', shard.relative).toHexString(); } } } function normalizeTime(operation, totalTime) { operation.timePercentage = (timeInMilliseconds(operation) / totalTime * 100).toFixed(2); operation.absoluteColor = _tinycolor.default.mix('#F5F5F5', '#FFAFAF', +operation.timePercentage).toHexString(); } function initTree(data, totalTime, depth = 0, parent = null) { if (_constants.MAX_TREE_DEPTH + 1 === depth) { if (parent) { parent.hasChildren = false; parent.children = []; } return; } for (const child of data) { // For bwc of older profile responses if (!child.description) { child.description = child.lucene; child.lucene = null; child.type = child.query_type; child.query_type = null; } normalizeTime(child, totalTime); child.parent = parent; child.time = timeInMilliseconds(child); child.lucene = child.description; child.query_type = child.type.split('.').pop(); child.visible = +child.timePercentage > 20; child.depth = depth; if (child.children != null && child.children.length !== 0) { initTree(child.children, totalTime, depth + 1, child); } } data.sort((a, b) => comparator(timeInMilliseconds(a), timeInMilliseconds(b))); } const sortIndices = data => { const sortedIndices = []; for (const index of Object.values(data)) { sortedIndices.push(index); } // And now sort the indices themselves sortedIndices.sort((a, b) => comparator(a.time, b.time)); return sortedIndices; }; exports.sortIndices = sortIndices;