From 14e15c3d1a5087cda5012123acf4205b369dfe2c Mon Sep 17 00:00:00 2001
From: weareoutman
Date: Thu, 23 May 2024 15:39:12 +0800
Subject: [PATCH] feat: support search based on description and keywords
closes #344
---
.../theme/SearchBar/SuggestionTemplate.ts | 34 +++++++++-----
.../client/theme/SearchPage/SearchPage.tsx | 43 +++++++++++------
.../src/client/utils/SearchSourceFactory.ts | 3 +-
.../processTreeStatusOfSearchResults.spec.ts | 46 +++++++++++++++----
.../utils/processTreeStatusOfSearchResults.ts | 14 +++++-
.../client/utils/sortSearchResults.spec.ts | 21 +++++----
.../src/client/utils/sortSearchResults.ts | 28 ++++++-----
.../src/server/utils/parse.spec.ts | 12 ++++-
.../src/server/utils/parseDocument.spec.ts | 12 ++++-
.../src/server/utils/parseDocument.ts | 4 +-
.../src/server/utils/parsePage.spec.ts | 14 +++++-
.../src/server/utils/parsePage.ts | 5 ++
.../src/server/utils/scanDocuments.spec.ts | 20 ++++++++
.../src/server/utils/scanDocuments.ts | 32 ++++++++++++-
.../src/shared/interfaces.ts | 21 ++++++---
website/docs/intro.md | 6 +++
16 files changed, 243 insertions(+), 72 deletions(-)
diff --git a/docusaurus-search-local/src/client/theme/SearchBar/SuggestionTemplate.ts b/docusaurus-search-local/src/client/theme/SearchBar/SuggestionTemplate.ts
index a8bbc3b8..d8a37a89 100644
--- a/docusaurus-search-local/src/client/theme/SearchBar/SuggestionTemplate.ts
+++ b/docusaurus-search-local/src/client/theme/SearchBar/SuggestionTemplate.ts
@@ -1,4 +1,8 @@
-import { SearchDocument, SearchResult } from "../../../shared/interfaces";
+import {
+ SearchDocument,
+ SearchDocumentType,
+ SearchResult,
+} from "../../../shared/interfaces";
import { concatDocumentPath } from "../../utils/concatDocumentPath";
import { getStemmedPositions } from "../../utils/getStemmedPositions";
import { highlight } from "../../utils/highlight";
@@ -23,8 +27,10 @@ export function SuggestionTemplate({
isInterOfTree,
isLastOfTree,
}: Omit): string {
- const isTitle = type === 0;
- const isHeading = type === 1;
+ const isTitle = type === SearchDocumentType.Title;
+ const isKeywords = type === SearchDocumentType.Keywords;
+ const isTitleRelated = isTitle || isKeywords;
+ const isHeading = type === SearchDocumentType.Heading;
const tree: string[] = [];
if (isInterOfTree) {
tree.push(iconTreeInter);
@@ -35,22 +41,26 @@ export function SuggestionTemplate({
(item) => `${item}`
);
const icon = `${
- isTitle ? iconTitle : isHeading ? iconHeading : iconContent
+ isTitleRelated ? iconTitle : isHeading ? iconHeading : iconContent
}`;
const wrapped = [
- `${highlightStemmed(
- document.t,
- getStemmedPositions(metadata, "t"),
- tokens
- )}`,
+ `${
+ isKeywords
+ ? highlight(document.s!, tokens)
+ : highlightStemmed(
+ document.t,
+ getStemmedPositions(metadata, "t"),
+ tokens
+ )
+ }`,
];
const needsExplicitHitPath =
!isInterOfTree && !isLastOfTree && explicitSearchResultPath;
if (needsExplicitHitPath) {
const pathItems = page
- ? (page.b ?? [])
- .concat(page.t)
+ ? page.b
+ ?.concat(page.t)
.concat(!document.s || document.s === page.t ? [] : document.s)
: document.b;
wrapped.push(
@@ -58,7 +68,7 @@ export function SuggestionTemplate({
pathItems ?? []
)}`
);
- } else if (!isTitle) {
+ } else if (!isTitleRelated) {
wrapped.push(
`${highlight(
(page as SearchDocument).t ||
diff --git a/docusaurus-search-local/src/client/theme/SearchPage/SearchPage.tsx b/docusaurus-search-local/src/client/theme/SearchPage/SearchPage.tsx
index 29981776..2a6f22b0 100644
--- a/docusaurus-search-local/src/client/theme/SearchPage/SearchPage.tsx
+++ b/docusaurus-search-local/src/client/theme/SearchPage/SearchPage.tsx
@@ -10,7 +10,11 @@ import clsx from "clsx";
import useSearchQuery from "../hooks/useSearchQuery";
import { fetchIndexes } from "../SearchBar/fetchIndexes";
import { SearchSourceFactory } from "../../utils/SearchSourceFactory";
-import { SearchDocument, SearchResult } from "../../../shared/interfaces";
+import {
+ SearchDocument,
+ SearchDocumentType,
+ SearchResult,
+} from "../../../shared/interfaces";
import { highlight } from "../../utils/highlight";
import { highlightStemmed } from "../../utils/highlightStemmed";
import { getStemmedPositions } from "../../utils/getStemmedPositions";
@@ -109,7 +113,9 @@ function SearchPageContent(): React.ReactElement {
useEffect(() => {
async function doFetchIndexes() {
const { wrappedIndexes, zhDictionary } =
- !Array.isArray(searchContextByPaths) || searchContext || useAllContextsWithNoSearchContext
+ !Array.isArray(searchContextByPaths) ||
+ searchContext ||
+ useAllContextsWithNoSearchContext
? await fetchIndexes(versionUrl, searchContext)
: { wrappedIndexes: [], zhDictionary: [] };
setSearchSource(() =>
@@ -245,13 +251,19 @@ function SearchResultItem({
}: {
searchResult: SearchResult;
}): React.ReactElement {
- const isTitle = type === 0;
- const isContent = type === 2;
+ const isTitle = type === SearchDocumentType.Title;
+ const isKeywords = type === SearchDocumentType.Keywords;
+ const isDescription = type === SearchDocumentType.Description;
+ const isDescriptionOrKeywords = isDescription || isKeywords;
+ const isTitleRelated = isTitle || isDescriptionOrKeywords;
+ const isContent = type === SearchDocumentType.Content;
const pathItems = (
(isTitle ? document.b : (page as SearchDocument).b) as string[]
).slice();
- const articleTitle = (isContent ? document.s : document.t) as string;
- if (!isTitle) {
+ const articleTitle = (
+ isContent || isDescriptionOrKeywords ? document.s : document.t
+ ) as string;
+ if (!isTitleRelated) {
pathItems.push((page as SearchDocument).t);
}
let search = "";
@@ -268,14 +280,15 @@ function SearchResultItem({
@@ -284,7 +297,7 @@ function SearchResultItem({
{concatDocumentPath(pathItems)}
)}
- {isContent && (
+ {(isContent || isDescription) && (
doc.i === document.p
),
diff --git a/docusaurus-search-local/src/client/utils/processTreeStatusOfSearchResults.spec.ts b/docusaurus-search-local/src/client/utils/processTreeStatusOfSearchResults.spec.ts
index 019aa844..f017c0e3 100644
--- a/docusaurus-search-local/src/client/utils/processTreeStatusOfSearchResults.spec.ts
+++ b/docusaurus-search-local/src/client/utils/processTreeStatusOfSearchResults.spec.ts
@@ -1,4 +1,7 @@
-import { InitialSearchResult } from "../../shared/interfaces";
+import {
+ InitialSearchResult,
+ SearchDocumentType,
+} from "../../shared/interfaces";
import { processTreeStatusOfSearchResults } from "./processTreeStatusOfSearchResults";
describe("processTreeStatusOfSearchResults", () => {
@@ -8,14 +11,21 @@ describe("processTreeStatusOfSearchResults", () => {
document: {
i: 100,
},
- type: 0,
+ type: SearchDocumentType.Title,
page: undefined,
},
{
document: {
i: 200,
},
- type: 0,
+ type: SearchDocumentType.Title,
+ page: undefined,
+ },
+ {
+ document: {
+ i: 300,
+ },
+ type: SearchDocumentType.Title,
page: undefined,
},
] as InitialSearchResult[];
@@ -24,14 +34,14 @@ describe("processTreeStatusOfSearchResults", () => {
document: {
i: 1,
},
- type: 2,
+ type: SearchDocumentType.Content,
page: {},
},
{
document: {
i: 2,
},
- type: 1,
+ type: SearchDocumentType.Heading,
page: {},
},
pageTitles[0],
@@ -39,14 +49,14 @@ describe("processTreeStatusOfSearchResults", () => {
document: {
i: 101,
},
- type: 2,
+ type: SearchDocumentType.Content,
page: pageTitles[0].document,
},
{
document: {
i: 3,
},
- type: 1,
+ type: SearchDocumentType.Heading,
page: {},
},
pageTitles[1],
@@ -54,19 +64,33 @@ describe("processTreeStatusOfSearchResults", () => {
document: {
i: 201,
},
- type: 1,
+ type: SearchDocumentType.Heading,
page: pageTitles[1].document,
},
{
document: {
i: 202,
},
- type: 2,
+ type: SearchDocumentType.Content,
page: pageTitles[1].document,
},
+ {
+ document: {
+ i: 301,
+ },
+ type: SearchDocumentType.Keywords,
+ page: pageTitles[2].document,
+ },
+ {
+ document: {
+ i: 302,
+ },
+ type: SearchDocumentType.Description,
+ page: pageTitles[2].document,
+ },
] as InitialSearchResult[];
processTreeStatusOfSearchResults(results);
- const statuses: [boolean, boolean][] = [
+ const statuses: [boolean | undefined, boolean | undefined][] = [
[undefined, undefined],
[undefined, undefined],
[undefined, undefined],
@@ -75,6 +99,8 @@ describe("processTreeStatusOfSearchResults", () => {
[undefined, undefined],
[true, undefined],
[undefined, true],
+ [undefined, undefined],
+ [undefined, true],
];
results.forEach((item, i) => {
expect([item.isInterOfTree, item.isLastOfTree]).toEqual(statuses[i]);
diff --git a/docusaurus-search-local/src/client/utils/processTreeStatusOfSearchResults.ts b/docusaurus-search-local/src/client/utils/processTreeStatusOfSearchResults.ts
index 9cd29603..2035601c 100644
--- a/docusaurus-search-local/src/client/utils/processTreeStatusOfSearchResults.ts
+++ b/docusaurus-search-local/src/client/utils/processTreeStatusOfSearchResults.ts
@@ -1,4 +1,7 @@
-import { InitialSearchResult } from "../../shared/interfaces";
+import {
+ InitialSearchResult,
+ SearchDocumentType,
+} from "../../shared/interfaces";
export function processTreeStatusOfSearchResults(
results: InitialSearchResult[]
@@ -7,7 +10,14 @@ export function processTreeStatusOfSearchResults(
if (
i > 0 &&
item.page &&
- results.some((prev) => prev.document === item.page)
+ results
+ .slice(0, i)
+ .some(
+ (prev) =>
+ (prev.type === SearchDocumentType.Keywords
+ ? prev.page
+ : prev.document) === item.page
+ )
) {
if (i < results.length - 1 && results[i + 1].page === item.page) {
item.isInterOfTree = true;
diff --git a/docusaurus-search-local/src/client/utils/sortSearchResults.spec.ts b/docusaurus-search-local/src/client/utils/sortSearchResults.spec.ts
index 952ce296..9476dc61 100644
--- a/docusaurus-search-local/src/client/utils/sortSearchResults.spec.ts
+++ b/docusaurus-search-local/src/client/utils/sortSearchResults.spec.ts
@@ -1,4 +1,7 @@
-import { InitialSearchResult } from "../../shared/interfaces";
+import {
+ InitialSearchResult,
+ SearchDocumentType,
+} from "../../shared/interfaces";
import { sortSearchResults } from "./sortSearchResults";
describe("sortSearchResults", () => {
@@ -8,14 +11,14 @@ describe("sortSearchResults", () => {
document: {
i: 100,
},
- type: 0,
+ type: SearchDocumentType.Title,
page: undefined,
},
{
document: {
i: 200,
},
- type: 0,
+ type: SearchDocumentType.Title,
page: undefined,
},
] as InitialSearchResult[];
@@ -24,14 +27,14 @@ describe("sortSearchResults", () => {
document: {
i: 1,
},
- type: 2,
+ type: SearchDocumentType.Content,
page: {},
},
{
document: {
i: 2,
},
- type: 1,
+ type: SearchDocumentType.Heading,
page: {},
},
pageTitles[0],
@@ -39,21 +42,21 @@ describe("sortSearchResults", () => {
document: {
i: 3,
},
- type: 1,
+ type: SearchDocumentType.Heading,
page: {},
},
{
document: {
i: 201,
},
- type: 1,
+ type: SearchDocumentType.Heading,
page: pageTitles[1].document,
},
{
document: {
i: 202,
},
- type: 2,
+ type: SearchDocumentType.Content,
page: pageTitles[1].document,
},
pageTitles[1],
@@ -61,7 +64,7 @@ describe("sortSearchResults", () => {
document: {
i: 101,
},
- type: 2,
+ type: SearchDocumentType.Description,
page: pageTitles[0].document,
},
] as InitialSearchResult[];
diff --git a/docusaurus-search-local/src/client/utils/sortSearchResults.ts b/docusaurus-search-local/src/client/utils/sortSearchResults.ts
index 41509cc8..7c41bdc6 100644
--- a/docusaurus-search-local/src/client/utils/sortSearchResults.ts
+++ b/docusaurus-search-local/src/client/utils/sortSearchResults.ts
@@ -1,20 +1,30 @@
-import { InitialSearchResult, SearchResult } from "../../shared/interfaces";
+import {
+ InitialSearchResult,
+ SearchDocumentType,
+ SearchResult,
+} from "../../shared/interfaces";
export function sortSearchResults(results: InitialSearchResult[]): void {
results.forEach((item, index) => {
item.index = index;
});
- // Put search results of headings and contents just after
+ // Put search results of headings/contents/descriptions just after
// their belonged page's title, if existed.
(results as SearchResult[]).sort((a, b) => {
let aPageIndex =
- a.type > 0 && a.page
+ (a.type === SearchDocumentType.Heading ||
+ a.type === SearchDocumentType.Content ||
+ a.type === SearchDocumentType.Description) &&
+ a.page
? results.findIndex((item) => item.document === a.page)
: a.index;
let bPageIndex =
- b.type > 0 && b.page
+ (b.type === SearchDocumentType.Heading ||
+ b.type === SearchDocumentType.Content ||
+ b.type === SearchDocumentType.Description) &&
+ b.page
? results.findIndex((item) => item.document === b.page)
: b.index;
@@ -27,14 +37,10 @@ export function sortSearchResults(results: InitialSearchResult[]): void {
}
if (aPageIndex === bPageIndex) {
- if (a.type === 0) {
- return -1;
- }
- if (b.type === 0) {
- return 1;
- }
- return a.index - b.index;
+ const diff = (b.type === 0 ? 1 : 0) - (a.type === 0 ? 1 : 0);
+ return diff === 0 ? a.index - b.index : diff;
}
+
return aPageIndex - bPageIndex;
});
}
diff --git a/docusaurus-search-local/src/server/utils/parse.spec.ts b/docusaurus-search-local/src/server/utils/parse.spec.ts
index 53d221f7..69eed58f 100644
--- a/docusaurus-search-local/src/server/utils/parse.spec.ts
+++ b/docusaurus-search-local/src/server/utils/parse.spec.ts
@@ -7,7 +7,11 @@ import { parse } from "./parse";
describe("parse", () => {
test.each<[string, "docs" | "blog" | "page", ParsedDocument | null]>([
[
- `
+ `
+
+
+
+
Hello World
@@ -24,6 +28,8 @@ describe("parse", () => {
"page",
{
pageTitle: "Hello World",
+ description: "Hello Description",
+ keywords: "Hello,Keywords",
sections: [
{
title: "Hello World",
@@ -49,6 +55,8 @@ describe("parse", () => {
"docs",
{
pageTitle: "Hello World",
+ description: "",
+ keywords: "",
sections: [
{
title: "Hello World",
@@ -74,6 +82,8 @@ describe("parse", () => {
"docs",
{
pageTitle: "Hello World",
+ description: "",
+ keywords: "",
sections: [
{
title: "Hello World",
diff --git a/docusaurus-search-local/src/server/utils/parseDocument.spec.ts b/docusaurus-search-local/src/server/utils/parseDocument.spec.ts
index c5522d65..b7033087 100644
--- a/docusaurus-search-local/src/server/utils/parseDocument.spec.ts
+++ b/docusaurus-search-local/src/server/utils/parseDocument.spec.ts
@@ -5,7 +5,11 @@ import { parseDocument } from "./parseDocument";
describe("parseDocument", () => {
test.each<[string, ParsedDocument]>([
[
- `
+ `
+
+
+
+