Skip to content

Commit cda08a9

Browse files
authored
Add support for search in a sections site (#2541)
1 parent 7588774 commit cda08a9

File tree

7 files changed

+77
-40
lines changed

7 files changed

+77
-40
lines changed

.changeset/lovely-lies-complain.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'gitbook': minor
3+
---
4+
5+
Add support for searching results in a sections site

bun.lockb

-392 Bytes
Binary file not shown.

packages/gitbook/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"clean": "rm -rf ./.next && rm -rf ./public/~gitbook/static"
1717
},
1818
"dependencies": {
19-
"@gitbook/api": "^0.71.0",
19+
"@gitbook/api": "^0.72.0",
2020
"@gitbook/cache-do": "workspace:*",
2121
"@gitbook/emoji-codepoints": "workspace:*",
2222
"@gitbook/icons": "workspace:*",

packages/gitbook/src/components/Search/SearchResults.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import { SearchSectionResultItem } from './SearchSectionResultItem';
1212
import {
1313
getRecommendedQuestions,
1414
OrderedComputedResult,
15-
searchCurrentSpaceContent,
16-
searchSiteContent,
15+
searchSiteSpaceContent,
16+
searchAllSiteContent,
1717
} from './server-actions';
1818
import { Loading } from '../primitives';
1919

@@ -95,8 +95,8 @@ export const SearchResults = React.forwardRef(function SearchResults(
9595
setCursor(null);
9696

9797
const fetchedResults = await (global
98-
? searchSiteContent({ query, pointer })
99-
: searchCurrentSpaceContent(query, pointer, revisionId));
98+
? searchAllSiteContent(query, pointer)
99+
: searchSiteSpaceContent(query, pointer, revisionId));
100100

101101
setResults(withAsk ? withQuestionResult(fetchedResults, query) : fetchedResults);
102102
}, 350);

packages/gitbook/src/components/Search/server-actions.tsx

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { RevisionPage, SearchAIAnswer, SearchPageResult, SiteSpace, Space } from '@gitbook/api';
44
import * as React from 'react';
5+
import { assert } from 'ts-essentials';
56

67
import { streamResponse } from '@/lib/actions';
78
import * as api from '@/lib/api';
@@ -45,47 +46,61 @@ export interface AskAnswerResult {
4546
}
4647

4748
/**
48-
* Search for content in the entire site.
49+
* Search for content in a site by scoping the search to all content, a specific spaces or current space.
4950
*/
50-
export async function searchSiteContent(args: {
51+
async function searchSiteContent(args: {
5152
pointer: api.SiteContentPointer;
5253
query: string;
53-
siteSpaceIds?: string[];
54+
scope:
55+
| { mode: 'all' }
56+
| { mode: 'current'; siteSpaceId: string }
57+
| { mode: 'specific'; siteSpaceIds: string[] };
5458
cacheBust?: string;
5559
}): Promise<OrderedComputedResult[]> {
56-
const { pointer, siteSpaceIds, query, cacheBust } = args;
60+
const { pointer, scope, query, cacheBust } = args;
5761

5862
if (query.length <= 1) {
5963
return [];
6064
}
6165

62-
if (siteSpaceIds?.length === 0) {
63-
// if we have no siteSpaces to search in then we won't find anything. skip the call.
64-
return [];
65-
}
66+
const needsStructure =
67+
scope.mode === 'all' ||
68+
scope.mode === 'current' ||
69+
(scope.mode === 'specific' && scope.siteSpaceIds.length > 1);
6670

67-
const [searchResults, allSiteSpaces] = await Promise.all([
68-
api.searchSiteContent(
69-
pointer.organizationId,
70-
pointer.siteId,
71-
query,
72-
siteSpaceIds,
73-
cacheBust,
74-
),
75-
siteSpaceIds
76-
? null
77-
: api.getSiteSpaces({
71+
const [searchResults, siteStructure] = await Promise.all([
72+
api.searchSiteContent(pointer.organizationId, pointer.siteId, query, scope, cacheBust),
73+
needsStructure
74+
? api.getSiteStructure({
7875
organizationId: pointer.organizationId,
7976
siteId: pointer.siteId,
8077
siteShareKey: pointer.siteShareKey,
81-
}),
78+
})
79+
: null,
8280
]);
8381

84-
if (!siteSpaceIds) {
82+
const siteSpaces = siteStructure
83+
? siteStructure.type === 'siteSpaces'
84+
? siteStructure.structure
85+
: siteStructure.structure.reduce<SiteSpace[]>((prev, section) => {
86+
const sectionSiteSpaces = section.siteSpaces.map((siteSpace) => ({
87+
...siteSpace,
88+
space: {
89+
...siteSpace.space,
90+
title: section.title + ' › ' + siteSpace.space.title,
91+
},
92+
}));
93+
94+
prev.push(...sectionSiteSpaces);
95+
return prev;
96+
}, [])
97+
: null;
98+
99+
if (siteSpaces) {
85100
// We are searching all of this Site's content
86101
return searchResults.items
87102
.map((spaceItem) => {
88-
const siteSpace = allSiteSpaces?.find(
103+
const siteSpace = siteSpaces.find(
89104
(siteSpace) => siteSpace.space.id === spaceItem.id,
90105
);
91106

@@ -102,21 +117,39 @@ export async function searchSiteContent(args: {
102117
}
103118

104119
/**
105-
* Server action to search content in the current space
120+
* Server action to search content in the entire site.
121+
*/
122+
export async function searchAllSiteContent(
123+
query: string,
124+
pointer: api.SiteContentPointer,
125+
): Promise<OrderedComputedResult[]> {
126+
return await searchSiteContent({
127+
pointer,
128+
query,
129+
scope: { mode: 'all' },
130+
});
131+
}
132+
133+
/**
134+
* Server action to search content in a space.
106135
*/
107-
export async function searchCurrentSpaceContent(
136+
export async function searchSiteSpaceContent(
108137
query: string,
109138
pointer: api.SiteContentPointer,
110139
revisionId: string,
111140
): Promise<OrderedComputedResult[]> {
112-
const siteSpaceIds = pointer.siteSpaceId ? [pointer.siteSpaceId] : []; // if we don't have a siteSpaceID search all content
141+
const siteSpaceId = pointer.siteSpaceId;
142+
assert(siteSpaceId, 'Expected siteSpaceId for searchSiteSpaceContent');
113143

114-
// This is a site so use a different function which we can eventually call directly
115-
// We also want to break cache for this specific space if the revisionId is different so use it as a cache busting key
116144
return await searchSiteContent({
117145
pointer,
118-
siteSpaceIds,
119146
query,
147+
// If we have a siteSectionId that means its a sections site use `current` mode
148+
// which searches in the current space + all default spaces of sections
149+
scope: pointer.siteSectionId
150+
? { mode: 'current', siteSpaceId }
151+
: { mode: 'specific', siteSpaceIds: [siteSpaceId] },
152+
// We want to break cache for this specific space if the revisionId is different so use it as a cache busting key
120153
cacheBust: revisionId,
121154
});
122155
}

packages/gitbook/src/lib/api.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,10 @@ export const searchSiteContent = cache({
10581058
organizationId: string,
10591059
siteId: string,
10601060
query: string,
1061-
siteSpaceIds?: string[],
1061+
scope:
1062+
| { mode: 'all' }
1063+
| { mode: 'current'; siteSpaceId: string }
1064+
| { mode: 'specific'; siteSpaceIds: string[] },
10621065
/** A cache bust param to avoid revalidating lot of cache entries by tags */
10631066
cacheBust?: string,
10641067
options?: CacheFunctionOptions,
@@ -1068,11 +1071,7 @@ export const searchSiteContent = cache({
10681071
siteId,
10691072
{
10701073
query,
1071-
...(siteSpaceIds && siteSpaceIds.length > 0
1072-
? { siteSpaceIds }
1073-
: {
1074-
mode: 'all',
1075-
}),
1074+
...scope,
10761075
},
10771076
undefined,
10781077
{

packages/react-contentkit/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
"dependencies": {
1212
"classnames": "^2.5.1",
13-
"@gitbook/api": "^0.66.0",
13+
"@gitbook/api": "^0.72.0",
1414
"assert-never": "^1.2.1"
1515
},
1616
"peerDependencies": {

0 commit comments

Comments
 (0)