Skip to content

Commit c2bbf02

Browse files
authored
doc: search only returns results from selected version and fix latest version (#9033)
1 parent 3702a75 commit c2bbf02

File tree

2 files changed

+251
-1
lines changed

2 files changed

+251
-1
lines changed
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
// Adapted from code by Matt Walters https://www.mattwalters.net/posts/2018-03-28-hugo-and-lunr/
2+
// Modified to filter search results to only show those from the currently selected version.
3+
4+
(function ($) {
5+
'use strict';
6+
7+
// Detect the current version from the URL path.
8+
// The first path segment is typically the version (e.g., "v1.9", "preview", "docs").
9+
// On the homepage (no path segments), default to "docs" (the latest version's content).
10+
function getCurrentVersion() {
11+
const pathSegments = window.location.pathname.split('/').filter(Boolean);
12+
if (pathSegments.length > 0) {
13+
return pathSegments[0];
14+
}
15+
// On the homepage, default to showing results from the latest version ("docs")
16+
return 'docs';
17+
}
18+
19+
// Extract the version prefix from a search result ref (e.g., "/v1.9/concepts/..." -> "v1.9").
20+
function getVersionFromRef(ref) {
21+
const segments = ref.split('/').filter(Boolean);
22+
if (segments.length > 0) {
23+
return segments[0];
24+
}
25+
return '';
26+
}
27+
28+
// Filter results to only include those from the current version.
29+
function filterToCurrentVersion(results, currentVersion) {
30+
if (!currentVersion) {
31+
return results;
32+
}
33+
34+
return results.filter((r) => {
35+
const resultVersion = getVersionFromRef(r.ref);
36+
return resultVersion === currentVersion;
37+
});
38+
}
39+
40+
$(document).ready(function () {
41+
const $searchInput = $('.td-search input');
42+
43+
//
44+
// Options for popover
45+
//
46+
47+
$searchInput.data('html', true);
48+
$searchInput.data('placement', 'bottom');
49+
$searchInput.data(
50+
'template',
51+
'<div class="td-offline-search-results popover" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'
52+
);
53+
54+
//
55+
// Register handler
56+
//
57+
58+
$searchInput.on('change', (event) => {
59+
render($(event.target));
60+
61+
// Hide keyboard on mobile browser
62+
$searchInput.blur();
63+
});
64+
65+
// Prevent reloading page by enter key on sidebar search.
66+
$searchInput.closest('form').on('submit', () => {
67+
return false;
68+
});
69+
70+
//
71+
// Lunr
72+
//
73+
74+
let idx = null; // Lunr index
75+
const resultDetails = new Map(); // Will hold the data for the search results (titles and summaries)
76+
77+
// Set up for an Ajax call to request the JSON data file that is created by Hugo's build process
78+
$.ajax($searchInput.data('offline-search-index-json-src')).then(
79+
(data) => {
80+
idx = lunr(function () {
81+
this.ref('ref');
82+
83+
// If you added more searchable fields to the search index, list them here.
84+
// Here you can specify searchable fields to the search index - e.g. individual toxonomies for you project
85+
// With "boost" you can add weighting for specific (default weighting without boost: 1)
86+
this.field('title', { boost: 5 });
87+
this.field('categories', { boost: 3 });
88+
this.field('tags', { boost: 3 });
89+
// this.field('projects', { boost: 3 }); // example for an individual toxonomy called projects
90+
this.field('description', { boost: 2 });
91+
this.field('body');
92+
93+
data.forEach((doc) => {
94+
this.add(doc);
95+
96+
resultDetails.set(doc.ref, {
97+
title: doc.title,
98+
excerpt: doc.excerpt,
99+
});
100+
});
101+
});
102+
103+
$searchInput.trigger('change');
104+
}
105+
);
106+
107+
const render = ($targetSearchInput) => {
108+
// Dispose the previous result
109+
$targetSearchInput.popover('dispose');
110+
111+
//
112+
// Search
113+
//
114+
115+
if (idx === null) {
116+
return;
117+
}
118+
119+
const searchQuery = $targetSearchInput.val();
120+
if (searchQuery === '') {
121+
return;
122+
}
123+
124+
const rawResults = idx
125+
.query((q) => {
126+
const tokens = lunr.tokenizer(searchQuery.toLowerCase());
127+
tokens.forEach((token) => {
128+
const queryString = token.toString();
129+
q.term(queryString, {
130+
boost: 100,
131+
});
132+
q.term(queryString, {
133+
wildcard:
134+
lunr.Query.wildcard.LEADING |
135+
lunr.Query.wildcard.TRAILING,
136+
boost: 10,
137+
});
138+
q.term(queryString, {
139+
editDistance: 2,
140+
});
141+
});
142+
});
143+
144+
// Filter to current version FIRST, then slice to max results.
145+
// This ensures we get the full set of relevant results for the
146+
// selected version instead of slicing across all versions first.
147+
const currentVersion = getCurrentVersion();
148+
const results = filterToCurrentVersion(rawResults, currentVersion)
149+
.slice(
150+
0,
151+
$targetSearchInput.data('offline-search-max-results')
152+
);
153+
154+
//
155+
// Make result html
156+
//
157+
158+
const $html = $('<div>');
159+
160+
$html.append(
161+
$('<div>')
162+
.css({
163+
display: 'flex',
164+
justifyContent: 'space-between',
165+
marginBottom: '1em',
166+
})
167+
.append(
168+
$('<span>')
169+
.text('Search results')
170+
.css({ fontWeight: 'bold' })
171+
)
172+
.append(
173+
$('<span>')
174+
.addClass('td-offline-search-results__close-button')
175+
)
176+
);
177+
178+
const $searchResultBody = $('<div>').css({
179+
maxHeight: `calc(100vh - ${
180+
$targetSearchInput.offset().top -
181+
$(window).scrollTop() +
182+
180
183+
}px)`,
184+
overflowY: 'auto',
185+
});
186+
$html.append($searchResultBody);
187+
188+
if (results.length === 0) {
189+
$searchResultBody.append(
190+
$('<p>').text(`No results found for query "${searchQuery}"`)
191+
);
192+
} else {
193+
results.forEach((r) => {
194+
const doc = resultDetails.get(r.ref);
195+
const href =
196+
$searchInput.data('offline-search-base-href') +
197+
r.ref.replace(/^\//, '');
198+
199+
const resultVersion = getVersionFromRef(r.ref);
200+
const $entry = $('<div>').addClass('mt-4');
201+
202+
// Show version badge and path
203+
const $meta = $('<small>').addClass('d-block text-muted');
204+
if (resultVersion) {
205+
const $versionBadge = $('<span>')
206+
.text(resultVersion)
207+
.css({
208+
display: 'inline-block',
209+
padding: '0 0.4em',
210+
marginRight: '0.5em',
211+
fontSize: '0.85em',
212+
fontWeight: '600',
213+
borderRadius: '3px',
214+
backgroundColor: resultVersion === currentVersion ? '#0d6efd' : '#6c757d',
215+
color: '#fff',
216+
});
217+
$meta.append($versionBadge);
218+
}
219+
$meta.append(document.createTextNode(r.ref));
220+
$entry.append($meta);
221+
222+
$entry.append(
223+
$('<a>')
224+
.addClass('d-block')
225+
.css({
226+
fontSize: '1.2rem',
227+
})
228+
.attr('href', href)
229+
.text(doc.title)
230+
);
231+
232+
$entry.append($('<p>').text(doc.excerpt));
233+
234+
$searchResultBody.append($entry);
235+
});
236+
}
237+
238+
$targetSearchInput.on('shown.bs.popover', () => {
239+
$('.td-offline-search-results__close-button').on('click', () => {
240+
$targetSearchInput.val('');
241+
$targetSearchInput.trigger('change');
242+
});
243+
});
244+
245+
$targetSearchInput
246+
.data('content', $html[0])
247+
.popover('show');
248+
};
249+
});
250+
})(jQuery);

website/content/en/preview/concepts/nodeclasses.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ You can provision and assign a role to an IAM instance profile using [CloudForma
714714

715715
{{% alert title="Note" color="primary" %}}
716716

717-
For [private clusters](https://docs.aws.amazon.com/eks/latest/userguide/private-clusters.html) that do not have access to the public internet, using `spec.instanceProfile` is required. `spec.role` cannot be used since Karpenter needs to access IAM endpoints to manage a generated instance profile. IAM [doesn't support private endpoints](https://docs.aws.amazon.com/vpc/latest/privatelink/aws-services-privatelink-support.html) to enable accessing the service without going to the public internet.
717+
For [private clusters](https://docs.aws.amazon.com/eks/latest/userguide/private-clusters.html) without access to their AWS region's IAM API endpoint, using `spec.instanceProfile` is required. `spec.role` cannot be used since Karpenter needs to access IAM endpoints to manage a generated instance profile. IAM [doesn't support private endpoints](https://docs.aws.amazon.com/vpc/latest/privatelink/aws-services-privatelink-support.html) to enable accessing the service without going to the public internet.
718718

719719
{{% /alert %}}
720720

0 commit comments

Comments
 (0)