import { brk } from "../../utils/client.js"; import { relevance } from "./text.js"; /** @type {Promise | undefined} */ let catalogPromise; /** @param {any} node @param {string[]} breadcrumbs */ function record(node, breadcrumbs) { if (!node.chart) return undefined; const series = node.chart.series.flatMap((/** @type {any} */ entry) => { try { const metric = entry.metric(brk); return metric?.name ? [{ name: metric.name, label: entry.label }] : []; } catch { return []; } }); return { title: node.chart.title ?? node.title, sectionTitle: node.title, breadcrumbs, description: node.description ?? "", unit: node.chart.unit?.id, series, }; } /** @param {any[]} nodes @param {string[]} ancestors @param {any[]} output */ function walk(nodes, ancestors, output) { for (const node of nodes) { const breadcrumbs = [...ancestors, node.title]; const item = record(node, breadcrumbs); if (item) output.push(item); if (node.children) walk(node.children, breadcrumbs, output); } } async function learnCatalog() { catalogPromise ??= import("../../learn/data/index.js").then(({ sections }) => { /** @type {any[]} */ const output = []; walk(sections, [], output); return output; }); return catalogPromise; } /** @param {string} query @param {number} [limit] */ export async function searchLearn(query, limit = 12) { return (await learnCatalog()) .map((/** @type {any} */ item) => ({ ...item, score: relevance(query, `${item.title} ${item.sectionTitle}`) * 3 + relevance(query, item.breadcrumbs.join(" ")) * 2 + relevance( query, `${item.description} ${item.series.flatMap((/** @type {any} */ series) => [series.label, series.name]).join(" ")}`, ), })) .filter(({ score }) => score >= 16) .sort((left, right) => right.score - left.score) .slice(0, limit); }