UNPKG

roam-research-mcp

Version:
111 lines (110 loc) • 3.44 kB
/** * Sort search results by a specified field and direction */ export function sortResults(matches, options) { const { field, direction } = options; const multiplier = direction === 'asc' ? 1 : -1; return [...matches].sort((a, b) => { let comparison = 0; switch (field) { case 'created': comparison = (a.created || 0) - (b.created || 0); break; case 'modified': comparison = (a.modified || 0) - (b.modified || 0); break; case 'page': comparison = (a.page_title || '').localeCompare(b.page_title || ''); break; } return comparison * multiplier; }); } /** * Group search results by page title */ export function groupByPage(matches) { const groups = {}; for (const match of matches) { const key = match.page_title || '(No Page)'; if (!groups[key]) { groups[key] = []; } groups[key].push(match); } return { groups, meta: { total: matches.length, groups_count: Object.keys(groups).length } }; } /** * Group search results by tag * Matches are grouped by the most specific matching subtag of the search tag * Each match appears only once, under its most specific matching tag */ export function groupByTag(matches, searchTag) { const groups = {}; const normalizedSearchTag = normalizeTagName(searchTag); for (const match of matches) { const tags = match.tags || []; // Find the most specific matching tag (longest path that starts with searchTag) let bestTag = searchTag; // Default to the search tag itself let bestLength = 0; for (const tag of tags) { const normalizedTag = normalizeTagName(tag); // Check if this tag matches or is a subtag of the search tag if (normalizedTag === normalizedSearchTag || normalizedTag.startsWith(normalizedSearchTag + '/')) { if (normalizedTag.length > bestLength) { bestTag = tag; bestLength = normalizedTag.length; } } } // Group under the best matching tag if (!groups[bestTag]) { groups[bestTag] = []; } groups[bestTag].push(match); } // Sort groups by tag name for consistent output const sortedGroups = {}; const sortedKeys = Object.keys(groups).sort(); for (const key of sortedKeys) { sortedGroups[key] = groups[key]; } return { groups: sortedGroups, meta: { total: matches.length, groups_count: Object.keys(sortedGroups).length } }; } /** * Group results by specified field */ export function groupResults(matches, options) { switch (options.by) { case 'page': return groupByPage(matches); case 'tag': return groupByTag(matches, options.searchTag || ''); } } /** * Normalize tag name for comparison (lowercase, trim whitespace) */ function normalizeTagName(tag) { return tag.toLowerCase().trim(); } /** * Get default sort direction for a field */ export function getDefaultDirection(field) { // Dates default to descending (newest first), alphabetical to ascending return field === 'page' ? 'asc' : 'desc'; }