|
68 | 68 | }) |
69 | 69 | } |
70 | 70 |
|
| 71 | + // Extract the parent page path from a URL (removes anchor and trailing segments) |
| 72 | + function getParentPagePath (url) { |
| 73 | + if (!url) return '' |
| 74 | + // Remove anchor fragment |
| 75 | + var path = url.split('#')[0] |
| 76 | + // Normalize trailing slash |
| 77 | + if (path.endsWith('/')) { |
| 78 | + path = path.slice(0, -1) |
| 79 | + } |
| 80 | + return path |
| 81 | + } |
| 82 | + |
| 83 | + // Check if hit represents a sub-section of a page (has anchor or deeper hierarchy) |
| 84 | + function isSubSection (hit) { |
| 85 | + if (!hit || !hit.url) return false |
| 86 | + return hit.url.indexOf('#') !== -1 |
| 87 | + } |
| 88 | + |
| 89 | + // Get the breadcrumb depth (number of hierarchy levels) |
| 90 | + function getBreadcrumbDepth (hit) { |
| 91 | + if (!hit || !hit.hierarchy) return 0 |
| 92 | + return Object.values(hit.hierarchy).filter(function (lvl) { |
| 93 | + return lvl !== null |
| 94 | + }).length |
| 95 | + } |
| 96 | + |
| 97 | + // Remove duplicate results for the same parent page |
| 98 | + // When parent page is a direct match, exclude its sub-sections |
| 99 | + function deduplicateHits (hits, query) { |
| 100 | + var seenPages = {} |
| 101 | + var parentMatches = {} |
| 102 | + var queryLower = (query || '').toLowerCase().trim() |
| 103 | + |
| 104 | + // First pass: identify parent pages that match the query directly |
| 105 | + hits.forEach(function (hit) { |
| 106 | + var parentPath = getParentPagePath(hit.url) |
| 107 | + var hierarchy = hit.hierarchy || {} |
| 108 | + |
| 109 | + // Check if any top-level hierarchy matches the search query |
| 110 | + var lvl1 = (hierarchy.lvl1 || '').toLowerCase() |
| 111 | + var lvl0 = (hierarchy.lvl0 || '').toLowerCase() |
| 112 | + |
| 113 | + if (lvl1 && lvl1.indexOf(queryLower) !== -1) { |
| 114 | + parentMatches[parentPath] = true |
| 115 | + } |
| 116 | + if (lvl0 && lvl0.indexOf(queryLower) !== -1) { |
| 117 | + parentMatches[parentPath] = true |
| 118 | + } |
| 119 | + }) |
| 120 | + |
| 121 | + // Second pass: filter out sub-sections when parent is already matched |
| 122 | + return hits.filter(function (hit) { |
| 123 | + var parentPath = getParentPagePath(hit.url) |
| 124 | + var isSubSec = isSubSection(hit) |
| 125 | + var depth = getBreadcrumbDepth(hit) |
| 126 | + |
| 127 | + // If this is a sub-section and parent page already matched, skip it |
| 128 | + if (isSubSec && parentMatches[parentPath]) { |
| 129 | + // Only keep the main page hit, not sub-sections |
| 130 | + if (seenPages[parentPath]) { |
| 131 | + return false |
| 132 | + } |
| 133 | + } |
| 134 | + |
| 135 | + // For component pages, only show the main entry (depth <= 2) |
| 136 | + if (isComponentUrl(hit.url) && depth > 2 && seenPages[parentPath]) { |
| 137 | + return false |
| 138 | + } |
| 139 | + |
| 140 | + // Track that we've seen this parent page |
| 141 | + if (!seenPages[parentPath]) { |
| 142 | + seenPages[parentPath] = { depth: depth, hit: hit } |
| 143 | + return true |
| 144 | + } |
| 145 | + |
| 146 | + // If we already have this page, only keep if it's a better match (shallower) |
| 147 | + if (depth < seenPages[parentPath].depth) { |
| 148 | + seenPages[parentPath] = { depth: depth, hit: hit } |
| 149 | + return true |
| 150 | + } |
| 151 | + |
| 152 | + return false |
| 153 | + }) |
| 154 | + } |
| 155 | + |
71 | 156 | function truncateHighlightedHtml (html, maxChars) { |
72 | 157 | if (!html || maxChars <= 0) return '' |
73 | 158 |
|
|
170 | 255 | return |
171 | 256 | } |
172 | 257 | cancel.style.display = 'block' |
| 258 | + var searchQuery = search.value |
173 | 259 | index |
174 | | - .search(search.value, { |
175 | | - hitsPerPage: 20, |
| 260 | + .search(searchQuery, { |
| 261 | + hitsPerPage: 50, |
176 | 262 | }) |
177 | 263 | .then((results) => { |
178 | 264 | // Filter out sub-project results to focus on camel-core documentation |
179 | 265 | const filteredHits = results.hits.filter(function (hit) { |
180 | 266 | return !isSubProjectUrl(hit.url) |
181 | 267 | }) |
182 | | - // Sort to prioritize core docs over components |
183 | | - const sortedHits = sortByCoreDocs(filteredHits).slice(0, RESULTS_LIMIT) |
| 268 | + // Remove duplicate results for the same parent page |
| 269 | + const dedupedHits = deduplicateHits(filteredHits, searchQuery) |
| 270 | + // Sort to prioritize core docs over components and limit results |
| 271 | + const sortedHits = sortByCoreDocs(dedupedHits).slice(0, RESULTS_LIMIT) |
184 | 272 | const data = sortedHits.reduce((data, hit) => { |
185 | 273 | const section = hit.hierarchy.lvl0 |
186 | 274 | const sectionKey = `${section}-${hit.version || ''}` |
|
0 commit comments