"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.scoreApp = exports.keywordScoreWeighting = exports.getAppResults = exports.appToResult = void 0; var _jsLevenshtein = _interopRequireDefault(require("js-levenshtein")); /* * 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. */ /** weighting factor for scoring keywords */ const keywordScoreWeighting = 0.8; exports.keywordScoreWeighting = keywordScoreWeighting; const getAppResults = (term, apps) => { return apps // Unroll all deepLinks, only if there is a search term .flatMap(app => { var _app$keywords; return term.length > 0 ? flattenDeepLinks(app) : app.searchable ? [{ id: app.id, app, path: app.appRoute, subLinkTitles: [], keywords: (_app$keywords = app.keywords) !== null && _app$keywords !== void 0 ? _app$keywords : [] }] : []; }).map(appLink => ({ appLink, score: scoreApp(term, appLink) })).filter(({ score }) => score > 0).map(({ appLink, score }) => appToResult(appLink, score)); }; exports.getAppResults = getAppResults; const scoreApp = (term, appLink) => { term = term.toLowerCase(); const title = [appLink.app.title, ...appLink.subLinkTitles].join(' ').toLowerCase(); const appScoreByTerms = scoreAppByTerms(term, title); const keywords = [...appLink.app.keywords.map(keyword => keyword.toLowerCase()), ...appLink.keywords.map(keyword => keyword.toLowerCase())]; const appScoreByKeywords = scoreAppByKeywords(term, keywords); return Math.max(appScoreByTerms, appScoreByKeywords * keywordScoreWeighting); }; exports.scoreApp = scoreApp; const scoreAppByTerms = (term, title) => { if (title === term) { // shortcuts to avoid calculating the distance when there is an exact match somewhere. return 100; } if (title.startsWith(term)) { return 90; } if (title.includes(term)) { return 75; } const length = Math.max(term.length, title.length); const distance = (0, _jsLevenshtein.default)(term, title); // maximum lev distance is length, we compute the match ratio (lower distance is better) const ratio = Math.floor((1 - distance / length) * 100); if (ratio >= 60) { return ratio; } return 0; }; const scoreAppByKeywords = (term, keywords) => { const scores = keywords.map(keyword => { return scoreAppByTerms(term, keyword); }); return Math.max(...scores); }; const appToResult = (appLink, score) => { var _appLink$euiIconType, _ref, _appLink$category$id, _appLink$category, _appLink$app$category, _ref2, _appLink$category$lab, _appLink$category2, _appLink$app$category2; const titleParts = // Stack Management app should not include the app title in the concatenated link label appLink.app.id === 'management' && appLink.subLinkTitles.length > 0 ? appLink.subLinkTitles : [appLink.app.title, ...appLink.subLinkTitles]; return { id: appLink.id, // Concatenate title using slashes title: titleParts.join(' / '), type: 'application', icon: (_appLink$euiIconType = appLink.euiIconType) !== null && _appLink$euiIconType !== void 0 ? _appLink$euiIconType : appLink.app.euiIconType, url: appLink.path, meta: { categoryId: (_ref = (_appLink$category$id = (_appLink$category = appLink.category) === null || _appLink$category === void 0 ? void 0 : _appLink$category.id) !== null && _appLink$category$id !== void 0 ? _appLink$category$id : (_appLink$app$category = appLink.app.category) === null || _appLink$app$category === void 0 ? void 0 : _appLink$app$category.id) !== null && _ref !== void 0 ? _ref : null, categoryLabel: (_ref2 = (_appLink$category$lab = (_appLink$category2 = appLink.category) === null || _appLink$category2 === void 0 ? void 0 : _appLink$category2.label) !== null && _appLink$category$lab !== void 0 ? _appLink$category$lab : (_appLink$app$category2 = appLink.app.category) === null || _appLink$app$category2 === void 0 ? void 0 : _appLink$app$category2.label) !== null && _ref2 !== void 0 ? _ref2 : null }, score }; }; exports.appToResult = appToResult; const flattenDeepLinks = (app, deepLink) => { var _deepLink$keywords; if (!deepLink) { var _app$keywords2; return [...(app.searchable ? [{ id: app.id, app, path: app.appRoute, subLinkTitles: [], keywords: (_app$keywords2 = app === null || app === void 0 ? void 0 : app.keywords) !== null && _app$keywords2 !== void 0 ? _app$keywords2 : [] }] : []), ...app.deepLinks.flatMap(appDeepLink => flattenDeepLinks(app, appDeepLink))]; } return [...(deepLink.path && deepLink.searchable ? [{ ...deepLink, id: `${app.id}-${deepLink.id}`, app, path: `${app.appRoute}${deepLink.path}`, subLinkTitles: [deepLink.title], keywords: [...((_deepLink$keywords = deepLink.keywords) !== null && _deepLink$keywords !== void 0 ? _deepLink$keywords : [])] }] : []), ...deepLink.deepLinks.flatMap(deepDeepLink => flattenDeepLinks(app, deepDeepLink)).map(deepAppLink => ({ ...deepAppLink, // shift current sublink title into array of sub-sublink titles subLinkTitles: [deepLink.title, ...deepAppLink.subLinkTitles], // combine current sublink keywords into array of sub-link keywords keywords: [...deepLink.keywords, ...deepAppLink.keywords] }))]; };