Skip to content

Commit

Permalink
feat: support search based on description and keywords
Browse files Browse the repository at this point in the history
closes #344
  • Loading branch information
weareoutman committed May 23, 2024
1 parent 8553b40 commit 14e15c3
Show file tree
Hide file tree
Showing 16 changed files with 243 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -23,8 +27,10 @@ export function SuggestionTemplate({
isInterOfTree,
isLastOfTree,
}: Omit<SearchResult, "score" | "index">): 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);
Expand All @@ -35,30 +41,34 @@ export function SuggestionTemplate({
(item) => `<span class="${styles.hitTree}">${item}</span>`
);
const icon = `<span class="${styles.hitIcon}">${
isTitle ? iconTitle : isHeading ? iconHeading : iconContent
isTitleRelated ? iconTitle : isHeading ? iconHeading : iconContent
}</span>`;
const wrapped = [
`<span class="${styles.hitTitle}">${highlightStemmed(
document.t,
getStemmedPositions(metadata, "t"),
tokens
)}</span>`,
`<span class="${styles.hitTitle}">${
isKeywords
? highlight(document.s!, tokens)
: highlightStemmed(
document.t,
getStemmedPositions(metadata, "t"),
tokens
)
}</span>`,
];

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(
`<span class="${styles.hitPath}">${concatDocumentPath(
pathItems ?? []
)}</span>`
);
} else if (!isTitle) {
} else if (!isTitleRelated) {
wrapped.push(
`<span class="${styles.hitPath}">${highlight(
(page as SearchDocument).t ||
Expand Down
43 changes: 28 additions & 15 deletions docusaurus-search-local/src/client/theme/SearchPage/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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(() =>
Expand Down Expand Up @@ -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 = "";
Expand All @@ -268,14 +280,15 @@ function SearchResultItem({
<Link
to={document.u + search + (document.h || "")}
dangerouslySetInnerHTML={{
__html: isContent
? highlight(articleTitle, tokens)
: highlightStemmed(
articleTitle,
getStemmedPositions(metadata, "t"),
tokens,
100
),
__html:
isContent || isDescriptionOrKeywords
? highlight(articleTitle, tokens)
: highlightStemmed(
articleTitle,
getStemmedPositions(metadata, "t"),
tokens,
100
),
}}
></Link>
</h2>
Expand All @@ -284,7 +297,7 @@ function SearchResultItem({
{concatDocumentPath(pathItems)}
</p>
)}
{isContent && (
{(isContent || isDescription) && (
<p
className={styles.searchResultItemSummary}
dangerouslySetInnerHTML={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
SearchResult,
SearchDocument,
InitialSearchResult,
SearchDocumentType,
} from "../../shared/interfaces";
import { sortSearchResults } from "./sortSearchResults";
import { processTreeStatusOfSearchResults } from "./processTreeStatusOfSearchResults";
Expand Down Expand Up @@ -58,7 +59,7 @@ export function SearchSourceFactory(
document,
type,
page:
type !== 0 &&
type !== SearchDocumentType.Title &&
wrappedIndexes[0].documents.find(
(doc) => doc.i === document.p
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { InitialSearchResult } from "../../shared/interfaces";
import {
InitialSearchResult,
SearchDocumentType,
} from "../../shared/interfaces";
import { processTreeStatusOfSearchResults } from "./processTreeStatusOfSearchResults";

describe("processTreeStatusOfSearchResults", () => {
Expand All @@ -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[];
Expand All @@ -24,49 +34,63 @@ describe("processTreeStatusOfSearchResults", () => {
document: {
i: 1,
},
type: 2,
type: SearchDocumentType.Content,
page: {},
},
{
document: {
i: 2,
},
type: 1,
type: SearchDocumentType.Heading,
page: {},
},
pageTitles[0],
{
document: {
i: 101,
},
type: 2,
type: SearchDocumentType.Content,
page: pageTitles[0].document,
},
{
document: {
i: 3,
},
type: 1,
type: SearchDocumentType.Heading,
page: {},
},
pageTitles[1],
{
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],
Expand All @@ -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]);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { InitialSearchResult } from "../../shared/interfaces";
import {
InitialSearchResult,
SearchDocumentType,
} from "../../shared/interfaces";

export function processTreeStatusOfSearchResults(
results: InitialSearchResult[]
Expand All @@ -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;
Expand Down
21 changes: 12 additions & 9 deletions docusaurus-search-local/src/client/utils/sortSearchResults.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { InitialSearchResult } from "../../shared/interfaces";
import {
InitialSearchResult,
SearchDocumentType,
} from "../../shared/interfaces";
import { sortSearchResults } from "./sortSearchResults";

describe("sortSearchResults", () => {
Expand All @@ -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[];
Expand All @@ -24,44 +27,44 @@ describe("sortSearchResults", () => {
document: {
i: 1,
},
type: 2,
type: SearchDocumentType.Content,
page: {},
},
{
document: {
i: 2,
},
type: 1,
type: SearchDocumentType.Heading,
page: {},
},
pageTitles[0],
{
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],
{
document: {
i: 101,
},
type: 2,
type: SearchDocumentType.Description,
page: pageTitles[0].document,
},
] as InitialSearchResult[];
Expand Down
Loading

0 comments on commit 14e15c3

Please sign in to comment.