Skip to content

Commit 6b30608

Browse files
yang1666204liyang
and
liyang
authored
Fixed the issue where the search box request took too long (apache#1495)
Co-authored-by: liyang <[email protected]>
1 parent 00d1873 commit 6b30608

15 files changed

+6203
-790
lines changed

.github/workflows/manual-deploy-website.yml

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ jobs:
9898
touch build/.dummy
9999
cp .asf-site.yaml ./build/.asf.yaml
100100
cp versions.json ./build/
101+
cp worker.js ./build/
101102
ls build
102103
export DORIS_COMMIT=`git rev-parse HEAD`
103104

package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"@docusaurus/plugin-client-redirects": "3.6.3",
2525
"@docusaurus/plugin-pwa": "3.6.3",
2626
"@docusaurus/preset-classic": "3.6.3",
27-
"@easyops-cn/docusaurus-search-local": "0.45",
27+
"@easyops-cn/docusaurus-search-local": "^0.46.1",
2828
"@mdx-js/react": "^3.0.0",
2929
"antd": "^5.12.2",
3030
"autoprefixer": "^10.4.16",
@@ -45,7 +45,10 @@
4545
},
4646
"devDependencies": {
4747
"@docusaurus/module-type-aliases": "3.6.3",
48+
"@rollup/plugin-commonjs": "^28.0.1",
49+
"@rollup/plugin-node-resolve": "^15.3.0",
4850
"@tsconfig/docusaurus": "2.0.3",
51+
"rollup": "^2.79.2",
4952
"typescript": "^5.2.2"
5053
},
5154
"resolutions": {

rollup.config.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import resolve from '@rollup/plugin-node-resolve';
2+
import commonjs from '@rollup/plugin-commonjs';
3+
4+
export default {
5+
input: 'node_modules/@easyops-cn/docusaurus-search-local/dist/client/client/theme/worker.js',
6+
output: {
7+
file: 'worker.js',
8+
format: 'iife',
9+
name: 'MyBundle',
10+
},
11+
plugins: [
12+
resolve({
13+
browser: true, // 针对浏览器解析依赖
14+
preferBuiltins: false, // 不优先使用 Node.js 内置模块
15+
}),
16+
commonjs({
17+
transformMixedEsModules: true, // 支持混合 ESM 和 CommonJS
18+
}),
19+
],
20+
};

src/hooks/use-search-query.ts

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
import { useHistory, useLocation } from "@docusaurus/router";
8+
import useIsBrowser from "@docusaurus/useIsBrowser";
9+
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
10+
import { searchContextByPaths } from "../utils/proxiedGenerated";
11+
const SEARCH_PARAM_QUERY = "q";
12+
const SEARCH_PARAM_CONTEXT = "ctx";
13+
const SEARCH_PARAM_VERSION = "version";
14+
function useSearchQuery() {
15+
const isBrowser = useIsBrowser();
16+
const history = useHistory();
17+
const location = useLocation();
18+
const { siteConfig: { baseUrl }, } = useDocusaurusContext();
19+
const params = isBrowser ? new URLSearchParams(location.search) : null;
20+
const searchValue = params?.get(SEARCH_PARAM_QUERY) || "";
21+
const searchContext = params?.get(SEARCH_PARAM_CONTEXT) || "";
22+
const searchVersion = params?.get(SEARCH_PARAM_VERSION) || "";
23+
const getSearchParams = (searchValue) => {
24+
const searchParams = new URLSearchParams(location.search);
25+
if (searchValue) {
26+
searchParams.set(SEARCH_PARAM_QUERY, searchValue);
27+
}
28+
else {
29+
searchParams.delete(SEARCH_PARAM_QUERY);
30+
}
31+
return searchParams;
32+
};
33+
return {
34+
searchValue,
35+
searchContext: searchContext &&
36+
Array.isArray(searchContextByPaths) &&
37+
searchContextByPaths.some((item) => typeof item === "string"
38+
? item === searchContext
39+
: item.path === searchContext)
40+
? searchContext
41+
: "",
42+
searchVersion,
43+
updateSearchPath: (searchValue) => {
44+
const searchParams = getSearchParams(searchValue);
45+
history.replace({
46+
search: searchParams.toString(),
47+
});
48+
},
49+
updateSearchContext: (value) => {
50+
const searchParams = new URLSearchParams(location.search);
51+
searchParams.set(SEARCH_PARAM_CONTEXT, value);
52+
history.replace({
53+
search: searchParams.toString(),
54+
});
55+
},
56+
generateSearchPageLink: (searchValue) => {
57+
const searchParams = getSearchParams(searchValue);
58+
// Refer to https://github.com/facebook/docusaurus/pull/2838
59+
return `${baseUrl}search?${searchParams.toString()}`;
60+
},
61+
};
62+
}
63+
export default useSearchQuery;

src/theme/SearchBar/SearchBar.jsx

+48-27
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ import { useHistory, useLocation } from '@docusaurus/router';
66
import { translate } from '@docusaurus/Translate';
77
import { ReactContextError, useDocsPreferredVersion } from '@docusaurus/theme-common';
88
import { useActivePlugin } from '@docusaurus/plugin-content-docs/client';
9-
import { fetchIndexes } from './fetchIndexes';
10-
import { SearchSourceFactory } from '../../utils/SearchSourceFactory';
9+
import { fetchIndexesByWorker, searchByWorker } from './searchByWorker';
1110
import { SuggestionTemplate } from './SuggestionTemplate';
1211
import { EmptyTemplate } from './EmptyTemplate';
1312
import {
14-
searchResultLimits,
1513
Mark,
1614
searchBarShortcut,
1715
searchBarShortcutHint,
@@ -26,6 +24,7 @@ import LoadingRing from '../LoadingRing/LoadingRing';
2624
import { VERSIONS, DEFAULT_VERSION } from '@site/src/constant/common';
2725
import styles from './SearchBar.module.css';
2826
import { normalizeContextByPath } from '../../utils/normalizeContextByPath';
27+
import useIsDocPage from '@site/src/hooks/use-is-doc';
2928
async function fetchAutoCompleteJS() {
3029
const autoCompleteModule = await import('@easyops-cn/autocomplete.js');
3130
const autoComplete = autoCompleteModule.default;
@@ -42,37 +41,54 @@ const SEARCH_PARAM_HIGHLIGHT = '_highlight';
4241
export default function SearchBar({ handleSearchBarToggle }) {
4342
const isBrowser = useIsBrowser();
4443
const [curVersion, setCurVersion] = useState(DEFAULT_VERSION);
44+
const location = useLocation();
4545
const {
4646
siteConfig: { baseUrl },
4747
i18n: { currentLocale },
4848
} = useDocusaurusContext();
4949
// It returns undefined for non-docs pages
5050
const activePlugin = useActivePlugin();
51+
const [isDocsPage] = useIsDocPage(false);
5152
let versionUrl = baseUrl;
53+
if (location?.pathname && location.pathname.includes('zh-CN') && !versionUrl.includes('zh-CN')) {
54+
versionUrl = baseUrl + 'zh-CN/';
55+
}
56+
if (location?.pathname) {
57+
VERSIONS.forEach(version => {
58+
if (location.pathname.includes(version)) {
59+
versionUrl += `docs/${version}/`;
60+
}
61+
});
62+
}
63+
5264
// For non-docs pages while using plugin-content-docs with custom ids,
5365
// this will throw an error of:
5466
// > Docusaurus plugin global data not found for "docusaurus-plugin-content-docs" plugin with id "default".
5567
// It seems that we can not get the correct id for non-docs pages.
56-
try {
57-
// The try-catch is a hack because useDocsPreferredVersion just throws an
58-
// exception when versions are not used.
59-
// The same hack is used in SearchPage.tsx
60-
// eslint-disable-next-line react-hooks/rules-of-hooks
61-
const { preferredVersion } = useDocsPreferredVersion(activePlugin?.pluginId ?? docsPluginIdForPreferredVersion);
62-
if (preferredVersion && !preferredVersion.isLast) {
63-
versionUrl = preferredVersion.path + '/';
64-
}
65-
} catch (e) {
66-
if (indexDocs) {
67-
if (e instanceof ReactContextError) {
68-
/* ignore, happens when website doesn't use versions */
69-
} else {
70-
throw e;
71-
}
72-
}
73-
}
68+
// try {
69+
// // The try-catch is a hack because useDocsPreferredVersion just throws an
70+
// // exception when versions are not used.
71+
// // The same hack is used in SearchPage.tsx
72+
// // eslint-disable-next-line react-hooks/rules-of-hooks
73+
// const { preferredVersion } = useDocsPreferredVersion(activePlugin?.pluginId ?? docsPluginIdForPreferredVersion);
74+
// console.log('preferredVersion',preferredVersion);
75+
76+
// if (preferredVersion && !preferredVersion.isLast) {
77+
// versionUrl = preferredVersion.path + "/";
78+
// }
79+
// }
80+
// catch (e) {
81+
// if (indexDocs) {
82+
// if (e instanceof ReactContextError) {
83+
// /* ignore, happens when website doesn't use versions */
84+
// }
85+
// else {
86+
// throw e;
87+
// }
88+
// }
89+
// }
90+
7491
const history = useHistory();
75-
const location = useLocation();
7692
const searchBarRef = useRef(null);
7793
const indexStateMap = useRef(new Map());
7894
// Should the input be focused after the index is loaded?
@@ -111,16 +127,16 @@ export default function SearchBar({ handleSearchBarToggle }) {
111127
}, [location.pathname, versionUrl]);
112128
const hidden = !!hideSearchBarWithNoSearchContext && Array.isArray(searchContextByPaths) && searchContext === '';
113129
const loadIndex = useCallback(async (forceLoad = false) => {
114-
if ((hidden || indexStateMap.current.get(searchContext)) && !forceLoad) {
130+
if (hidden || indexStateMap.current.get(searchContext) && !forceLoad) {
115131
// Do not load the index (again) if its already loaded or in the process of being loaded.
116132
return;
117133
}
118134
indexStateMap.current.set(searchContext, 'loading');
119135
search.current?.autocomplete.destroy();
120136
setLoading(true);
121-
const [{ wrappedIndexes, zhDictionary }, autoComplete] = await Promise.all([
122-
fetchIndexes(versionUrl, searchContext),
137+
const [autoComplete] = await Promise.all([
123138
fetchAutoCompleteJS(),
139+
fetchIndexesByWorker(versionUrl, searchContext),
124140
]);
125141
const searchFooterLinkElement = ({ query, isEmpty }) => {
126142
const a = document.createElement('a');
@@ -210,7 +226,10 @@ export default function SearchBar({ handleSearchBarToggle }) {
210226
},
211227
[
212228
{
213-
source: SearchSourceFactory(wrappedIndexes, zhDictionary, searchResultLimits),
229+
source: async (input, callback) => {
230+
const result = await searchByWorker(versionUrl, searchContext, input);
231+
callback(result);
232+
},
214233
templates: {
215234
suggestion: SuggestionTemplate,
216235
empty: EmptyTemplate,
@@ -335,6 +354,7 @@ export default function SearchBar({ handleSearchBarToggle }) {
335354
document.removeEventListener('keydown', handleShortcut);
336355
};
337356
}, [isMac, onInputFocus]);
357+
338358
const onClearSearch = useCallback(() => {
339359
const params = new URLSearchParams(location.search);
340360
params.delete(SEARCH_PARAM_HIGHLIGHT);
@@ -347,6 +367,7 @@ export default function SearchBar({ handleSearchBarToggle }) {
347367
setInputValue('');
348368
search.current?.autocomplete.setVal('');
349369
}, [location.pathname, location.search, location.hash, history]);
370+
350371
return (
351372
<div
352373
className={clsx('navbar__search', styles.searchBarContainer, {
@@ -364,7 +385,7 @@ export default function SearchBar({ handleSearchBarToggle }) {
364385
description: 'The ARIA label and placeholder for search button',
365386
})}
366387
aria-label="Search"
367-
className="navbar__search-input"
388+
className={clsx('navbar__search-input', styles.navbarSearchInput)}
368389
onMouseEnter={onInputMouseEnter}
369390
onFocus={onInputFocus}
370391
onBlur={onInputBlur}

src/theme/SearchBar/fetchIndexes.js

-46
This file was deleted.

src/theme/SearchBar/searchByWorker.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import * as Comlink from 'comlink';
2+
let remoteWorkerPromise;
3+
function getWorkerURL(url) {
4+
const content = `importScripts( "${url}" );`;
5+
return URL.createObjectURL(new Blob([content], { type: 'text/javascript' }));
6+
}
7+
function getRemoteWorker() {
8+
if (process.env.NODE_ENV === 'production' && !remoteWorkerPromise) {
9+
remoteWorkerPromise = (async () => {
10+
const Remote = Comlink.wrap(
11+
new Worker(getWorkerURL('https://cdnd.selectdb.com/worker.js')),
12+
);
13+
return await new Remote();
14+
})();
15+
}
16+
return remoteWorkerPromise;
17+
}
18+
export async function fetchIndexesByWorker(baseUrl, searchContext) {
19+
if (process.env.NODE_ENV === 'production') {
20+
const remoteWorker = await getRemoteWorker();
21+
await remoteWorker.fetchIndexes(baseUrl, searchContext);
22+
}
23+
}
24+
export async function searchByWorker(baseUrl, searchContext, input) {
25+
if (process.env.NODE_ENV === 'production') {
26+
const remoteWorker = await getRemoteWorker();
27+
return remoteWorker.search(baseUrl, searchContext, input);
28+
}
29+
return [];
30+
}

0 commit comments

Comments
 (0)