Skip to content

Commit 66aa399

Browse files
authored
Merge pull request #404 from easyops-cn/steve/search-by-desc-n-keywords
feat: support search based on description and keywords
2 parents 8553b40 + 0c12b59 commit 66aa399

18 files changed

+274
-79
lines changed

docusaurus-search-local/src/client/theme/SearchBar/SuggestionTemplate.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { SearchDocumentType } from "../../../shared/interfaces";
12
import { SuggestionTemplate } from "./SuggestionTemplate";
23

34
jest.mock("./icons");
@@ -12,7 +13,7 @@ describe("SuggestionTemplate", () => {
1213
t: "Hello world",
1314
u: "/docs/a",
1415
},
15-
type: 0,
16+
type: SearchDocumentType.Title,
1617
page: false,
1718
metadata: {
1819
hello: {
@@ -65,7 +66,7 @@ describe("SuggestionTemplate", () => {
6566
t: "Hello fruits.",
6667
u: "/docs/b",
6768
},
68-
type: 1,
69+
type: SearchDocumentType.Heading,
6970
page: {
7071
i: 1,
7172
t: "Hello world",
@@ -134,7 +135,7 @@ describe("SuggestionTemplate", () => {
134135
t: "Goodbye fruits.",
135136
u: "/docs/c",
136137
},
137-
type: 2,
138+
type: SearchDocumentType.Content,
138139
page: {
139140
i: 1,
140141
t: "Hello world",

docusaurus-search-local/src/client/theme/SearchBar/SuggestionTemplate.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { SearchDocument, SearchResult } from "../../../shared/interfaces";
1+
import {
2+
SearchDocument,
3+
SearchDocumentType,
4+
SearchResult,
5+
} from "../../../shared/interfaces";
26
import { concatDocumentPath } from "../../utils/concatDocumentPath";
37
import { getStemmedPositions } from "../../utils/getStemmedPositions";
48
import { highlight } from "../../utils/highlight";
@@ -23,8 +27,10 @@ export function SuggestionTemplate({
2327
isInterOfTree,
2428
isLastOfTree,
2529
}: Omit<SearchResult, "score" | "index">): string {
26-
const isTitle = type === 0;
27-
const isHeading = type === 1;
30+
const isTitle = type === SearchDocumentType.Title;
31+
const isKeywords = type === SearchDocumentType.Keywords;
32+
const isTitleRelated = isTitle || isKeywords;
33+
const isHeading = type === SearchDocumentType.Heading;
2834
const tree: string[] = [];
2935
if (isInterOfTree) {
3036
tree.push(iconTreeInter);
@@ -35,30 +41,34 @@ export function SuggestionTemplate({
3541
(item) => `<span class="${styles.hitTree}">${item}</span>`
3642
);
3743
const icon = `<span class="${styles.hitIcon}">${
38-
isTitle ? iconTitle : isHeading ? iconHeading : iconContent
44+
isTitleRelated ? iconTitle : isHeading ? iconHeading : iconContent
3945
}</span>`;
4046
const wrapped = [
41-
`<span class="${styles.hitTitle}">${highlightStemmed(
42-
document.t,
43-
getStemmedPositions(metadata, "t"),
44-
tokens
45-
)}</span>`,
47+
`<span class="${styles.hitTitle}">${
48+
isKeywords
49+
? highlight(document.s!, tokens)
50+
: highlightStemmed(
51+
document.t,
52+
getStemmedPositions(metadata, "t"),
53+
tokens
54+
)
55+
}</span>`,
4656
];
4757

4858
const needsExplicitHitPath =
4959
!isInterOfTree && !isLastOfTree && explicitSearchResultPath;
5060
if (needsExplicitHitPath) {
5161
const pathItems = page
52-
? (page.b ?? [])
53-
.concat(page.t)
62+
? page.b
63+
?.concat(page.t)
5464
.concat(!document.s || document.s === page.t ? [] : document.s)
5565
: document.b;
5666
wrapped.push(
5767
`<span class="${styles.hitPath}">${concatDocumentPath(
5868
pathItems ?? []
5969
)}</span>`
6070
);
61-
} else if (!isTitle) {
71+
} else if (!isTitleRelated) {
6272
wrapped.push(
6373
`<span class="${styles.hitPath}">${highlight(
6474
(page as SearchDocument).t ||

docusaurus-search-local/src/client/theme/SearchPage/SearchPage.tsx

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ import clsx from "clsx";
1010
import useSearchQuery from "../hooks/useSearchQuery";
1111
import { fetchIndexes } from "../SearchBar/fetchIndexes";
1212
import { SearchSourceFactory } from "../../utils/SearchSourceFactory";
13-
import { SearchDocument, SearchResult } from "../../../shared/interfaces";
13+
import {
14+
SearchDocument,
15+
SearchDocumentType,
16+
SearchResult,
17+
} from "../../../shared/interfaces";
1418
import { highlight } from "../../utils/highlight";
1519
import { highlightStemmed } from "../../utils/highlightStemmed";
1620
import { getStemmedPositions } from "../../utils/getStemmedPositions";
@@ -109,7 +113,9 @@ function SearchPageContent(): React.ReactElement {
109113
useEffect(() => {
110114
async function doFetchIndexes() {
111115
const { wrappedIndexes, zhDictionary } =
112-
!Array.isArray(searchContextByPaths) || searchContext || useAllContextsWithNoSearchContext
116+
!Array.isArray(searchContextByPaths) ||
117+
searchContext ||
118+
useAllContextsWithNoSearchContext
113119
? await fetchIndexes(versionUrl, searchContext)
114120
: { wrappedIndexes: [], zhDictionary: [] };
115121
setSearchSource(() =>
@@ -245,13 +251,19 @@ function SearchResultItem({
245251
}: {
246252
searchResult: SearchResult;
247253
}): React.ReactElement {
248-
const isTitle = type === 0;
249-
const isContent = type === 2;
254+
const isTitle = type === SearchDocumentType.Title;
255+
const isKeywords = type === SearchDocumentType.Keywords;
256+
const isDescription = type === SearchDocumentType.Description;
257+
const isDescriptionOrKeywords = isDescription || isKeywords;
258+
const isTitleRelated = isTitle || isDescriptionOrKeywords;
259+
const isContent = type === SearchDocumentType.Content;
250260
const pathItems = (
251261
(isTitle ? document.b : (page as SearchDocument).b) as string[]
252262
).slice();
253-
const articleTitle = (isContent ? document.s : document.t) as string;
254-
if (!isTitle) {
263+
const articleTitle = (
264+
isContent || isDescriptionOrKeywords ? document.s : document.t
265+
) as string;
266+
if (!isTitleRelated) {
255267
pathItems.push((page as SearchDocument).t);
256268
}
257269
let search = "";
@@ -268,14 +280,15 @@ function SearchResultItem({
268280
<Link
269281
to={document.u + search + (document.h || "")}
270282
dangerouslySetInnerHTML={{
271-
__html: isContent
272-
? highlight(articleTitle, tokens)
273-
: highlightStemmed(
274-
articleTitle,
275-
getStemmedPositions(metadata, "t"),
276-
tokens,
277-
100
278-
),
283+
__html:
284+
isContent || isDescriptionOrKeywords
285+
? highlight(articleTitle, tokens)
286+
: highlightStemmed(
287+
articleTitle,
288+
getStemmedPositions(metadata, "t"),
289+
tokens,
290+
100
291+
),
279292
}}
280293
></Link>
281294
</h2>
@@ -284,7 +297,7 @@ function SearchResultItem({
284297
{concatDocumentPath(pathItems)}
285298
</p>
286299
)}
287-
{isContent && (
300+
{(isContent || isDescription) && (
288301
<p
289302
className={styles.searchResultItemSummary}
290303
dangerouslySetInnerHTML={{

docusaurus-search-local/src/client/utils/SearchSourceFactory.spec.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import lunr from "lunr";
2-
import { SearchDocument } from "../../shared/interfaces";
2+
import { SearchDocument, SearchDocumentType } from "../../shared/interfaces";
33
import { SearchSourceFactory } from "./SearchSourceFactory";
44

55
// eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -32,6 +32,22 @@ describe("SearchSourceFactory", () => {
3232
p: 1,
3333
},
3434
];
35+
const documentsOfDescriptions: SearchDocument[] = [
36+
{
37+
i: 1,
38+
t: "First description",
39+
u: "/1",
40+
p: 1,
41+
},
42+
];
43+
const documentsOfKeywords: SearchDocument[] = [
44+
{
45+
i: 1,
46+
t: "First keywords",
47+
u: "/1",
48+
p: 1,
49+
},
50+
];
3551
const documentsOfContents: SearchDocument[] = [
3652
{
3753
i: 3,
@@ -60,17 +76,27 @@ describe("SearchSourceFactory", () => {
6076
{
6177
documents: documentsOfTitles,
6278
index: getIndex(documentsOfTitles),
63-
type: 0,
79+
type: SearchDocumentType.Title,
6480
},
6581
{
6682
documents: documentsOfHeadings,
6783
index: getIndex(documentsOfHeadings),
68-
type: 1,
84+
type: SearchDocumentType.Heading,
85+
},
86+
{
87+
documents: documentsOfDescriptions,
88+
index: getIndex(documentsOfDescriptions),
89+
type: SearchDocumentType.Description,
90+
},
91+
{
92+
documents: documentsOfKeywords,
93+
index: getIndex(documentsOfKeywords),
94+
type: SearchDocumentType.Keywords,
6995
},
7096
{
7197
documents: documentsOfContents,
7298
index: getIndex(documentsOfContents),
73-
type: 2,
99+
type: SearchDocumentType.Content,
74100
},
75101
],
76102
[],
@@ -82,6 +108,9 @@ describe("SearchSourceFactory", () => {
82108
[",", []],
83109
["nothing", []],
84110
["peace", [4, 2]],
111+
["description", [1]],
112+
["keywords", [1]],
113+
["first", [1, 2]],
85114
])(
86115
"SearchSourceFactory('%s', zhDictionary) should return %j",
87116
(input, results) => {

docusaurus-search-local/src/client/utils/SearchSourceFactory.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
SearchResult,
77
SearchDocument,
88
InitialSearchResult,
9+
SearchDocumentType,
910
} from "../../shared/interfaces";
1011
import { sortSearchResults } from "./sortSearchResults";
1112
import { processTreeStatusOfSearchResults } from "./processTreeStatusOfSearchResults";
@@ -58,7 +59,7 @@ export function SearchSourceFactory(
5859
document,
5960
type,
6061
page:
61-
type !== 0 &&
62+
type !== SearchDocumentType.Title &&
6263
wrappedIndexes[0].documents.find(
6364
(doc) => doc.i === document.p
6465
),

docusaurus-search-local/src/client/utils/processTreeStatusOfSearchResults.spec.ts

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { InitialSearchResult } from "../../shared/interfaces";
1+
import {
2+
InitialSearchResult,
3+
SearchDocumentType,
4+
} from "../../shared/interfaces";
25
import { processTreeStatusOfSearchResults } from "./processTreeStatusOfSearchResults";
36

47
describe("processTreeStatusOfSearchResults", () => {
@@ -8,14 +11,21 @@ describe("processTreeStatusOfSearchResults", () => {
811
document: {
912
i: 100,
1013
},
11-
type: 0,
14+
type: SearchDocumentType.Title,
1215
page: undefined,
1316
},
1417
{
1518
document: {
1619
i: 200,
1720
},
18-
type: 0,
21+
type: SearchDocumentType.Title,
22+
page: undefined,
23+
},
24+
{
25+
document: {
26+
i: 300,
27+
},
28+
type: SearchDocumentType.Title,
1929
page: undefined,
2030
},
2131
] as InitialSearchResult[];
@@ -24,49 +34,63 @@ describe("processTreeStatusOfSearchResults", () => {
2434
document: {
2535
i: 1,
2636
},
27-
type: 2,
37+
type: SearchDocumentType.Content,
2838
page: {},
2939
},
3040
{
3141
document: {
3242
i: 2,
3343
},
34-
type: 1,
44+
type: SearchDocumentType.Heading,
3545
page: {},
3646
},
3747
pageTitles[0],
3848
{
3949
document: {
4050
i: 101,
4151
},
42-
type: 2,
52+
type: SearchDocumentType.Content,
4353
page: pageTitles[0].document,
4454
},
4555
{
4656
document: {
4757
i: 3,
4858
},
49-
type: 1,
59+
type: SearchDocumentType.Heading,
5060
page: {},
5161
},
5262
pageTitles[1],
5363
{
5464
document: {
5565
i: 201,
5666
},
57-
type: 1,
67+
type: SearchDocumentType.Heading,
5868
page: pageTitles[1].document,
5969
},
6070
{
6171
document: {
6272
i: 202,
6373
},
64-
type: 2,
74+
type: SearchDocumentType.Content,
6575
page: pageTitles[1].document,
6676
},
77+
{
78+
document: {
79+
i: 301,
80+
},
81+
type: SearchDocumentType.Keywords,
82+
page: pageTitles[2].document,
83+
},
84+
{
85+
document: {
86+
i: 302,
87+
},
88+
type: SearchDocumentType.Description,
89+
page: pageTitles[2].document,
90+
},
6791
] as InitialSearchResult[];
6892
processTreeStatusOfSearchResults(results);
69-
const statuses: [boolean, boolean][] = [
93+
const statuses: [boolean | undefined, boolean | undefined][] = [
7094
[undefined, undefined],
7195
[undefined, undefined],
7296
[undefined, undefined],
@@ -75,6 +99,8 @@ describe("processTreeStatusOfSearchResults", () => {
7599
[undefined, undefined],
76100
[true, undefined],
77101
[undefined, true],
102+
[undefined, undefined],
103+
[undefined, true],
78104
];
79105
results.forEach((item, i) => {
80106
expect([item.isInterOfTree, item.isLastOfTree]).toEqual(statuses[i]);

0 commit comments

Comments
 (0)