Skip to content

Commit 29a0bf2

Browse files
scazanSamyPesse
andauthored
Update Site search endpoint (#2382)
Co-authored-by: Samy Pessé <[email protected]>
1 parent 92bb016 commit 29a0bf2

File tree

3 files changed

+142
-34
lines changed

3 files changed

+142
-34
lines changed

src/components/Search/SearchResults.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export const SearchResults = React.forwardRef(function SearchResults(
9696
const fetchedResults = await (parent
9797
? searchParentContent(parent, query)
9898
: searchSpaceContent(spaceId, revisionId, query));
99+
99100
setResults(withAsk ? withQuestionResult(fetchedResults, query) : fetchedResults);
100101
}, 250);
101102

src/components/Search/server-actions.tsx

Lines changed: 106 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import {
66
SearchAIAnswer,
77
SearchPageResult,
88
Site,
9+
SiteSpace,
910
Space,
1011
} from '@gitbook/api';
11-
import { headers } from 'next/headers';
1212

1313
import { getContentPointer } from '@/app/(space)/fetch';
1414
import { streamResponse } from '@/lib/actions';
@@ -52,6 +52,61 @@ export interface AskAnswerResult {
5252
hasAnswer: boolean;
5353
}
5454

55+
export async function searchSiteContent(args: {
56+
query: string;
57+
siteSpaceIds?: string[];
58+
cacheBust?: string;
59+
}): Promise<OrderedComputedResult[]> {
60+
const { siteSpaceIds, query, cacheBust } = args;
61+
const pointer = getContentPointer();
62+
63+
if (siteSpaceIds?.length === 0) {
64+
// if we have no siteSpaces to search in then we won't find anything. skip the call.
65+
return [];
66+
}
67+
68+
if ('siteId' in pointer && 'organizationId' in pointer) {
69+
const [searchResults, allSiteSpaces] = await Promise.all([
70+
api.searchSiteContent(
71+
pointer.organizationId,
72+
pointer.siteId,
73+
query,
74+
siteSpaceIds,
75+
cacheBust,
76+
),
77+
siteSpaceIds
78+
? null
79+
: api.getSiteSpaces({
80+
organizationId: pointer.organizationId,
81+
siteId: pointer.siteId,
82+
siteShareKey: pointer.siteShareKey,
83+
}),
84+
]);
85+
86+
if (!siteSpaceIds) {
87+
// We are searching all of this Site's content
88+
return searchResults.items
89+
.map((spaceItem) => {
90+
const siteSpace = allSiteSpaces?.find(
91+
(siteSpace) => siteSpace.space.id === spaceItem.id,
92+
);
93+
94+
return spaceItem.pages.map((item) => transformSitePageResult(item, siteSpace));
95+
})
96+
.flat(2);
97+
}
98+
99+
return searchResults.items
100+
.map((spaceItem) => {
101+
return spaceItem.pages.map((item) => transformPageResult(item));
102+
})
103+
.flat(2);
104+
}
105+
106+
// This should never happen
107+
return [];
108+
}
109+
55110
/**
56111
* Server action to search content in a space
57112
*/
@@ -60,6 +115,16 @@ export async function searchSpaceContent(
60115
revisionId: string,
61116
query: string,
62117
): Promise<OrderedComputedResult[]> {
118+
const pointer = getContentPointer();
119+
120+
if ('siteId' in pointer && 'organizationId' in pointer) {
121+
const siteSpaceIds = pointer.siteSpaceId ? [pointer.siteSpaceId] : []; // if we don't have a siteSpaceID search all content
122+
123+
// This is a site so use a different function which we can eventually call directly
124+
// We also want to break cache for this specific space if the revisionId is different so use it as a cache busting key
125+
return await searchSiteContent({ siteSpaceIds, query, cacheBust: revisionId });
126+
}
127+
63128
const data = await api.searchSpaceContent(spaceId, revisionId, query);
64129
return data.items.map((item) => transformPageResult(item, undefined)).flat();
65130
}
@@ -72,36 +137,18 @@ export async function searchParentContent(
72137
query: string,
73138
): Promise<OrderedComputedResult[]> {
74139
const pointer = getContentPointer();
140+
const isSite = 'siteId' in pointer;
141+
142+
if (isSite) {
143+
return searchSiteContent({ query });
144+
}
75145

76-
const [data, collectionSpaces, siteSpaces] = await Promise.all([
146+
const [data, collectionSpaces] = await Promise.all([
77147
api.searchParentContent(parent.id, query),
78148
parent.object === 'collection' ? api.getCollectionSpaces(parent.id) : null,
79-
parent.object === 'site' && 'organizationId' in pointer
80-
? api.getSiteSpaces({
81-
organizationId: pointer.organizationId,
82-
siteId: parent.id,
83-
siteShareKey: pointer.siteShareKey,
84-
})
85-
: null,
86149
]);
87150

88-
let spaces: Space[] = [];
89-
90-
if (collectionSpaces) {
91-
spaces = collectionSpaces;
92-
} else if (siteSpaces) {
93-
spaces = Object.values(
94-
siteSpaces.reduce(
95-
(acc, siteSpace) => {
96-
acc[siteSpace.space.id] = siteSpace.space;
97-
// replace the published url for the "space" with the site's published url
98-
acc[siteSpace.space.id].urls.published = siteSpace.urls.published;
99-
return acc;
100-
},
101-
{} as Record<string, Space>,
102-
),
103-
);
104-
}
151+
let spaces: Space[] = collectionSpaces ? collectionSpaces : [];
105152

106153
return data.items
107154
.map((spaceItem) => {
@@ -183,19 +230,24 @@ function transformAnswer(
183230
};
184231
}
185232

186-
function transformPageResult(item: SearchPageResult, space?: Space) {
233+
function transformSectionsAndPage(args: {
234+
item: SearchPageResult;
235+
space?: Space;
236+
spaceURL?: string;
237+
}): [ComputedPageResult, ComputedSectionResult[]] {
238+
const { item, space, spaceURL } = args;
239+
187240
// Resolve a relative path to an absolute URL
188241
// if the search result is relative to another space, we use the space URL
189-
const getURL = (path: string) => {
190-
if (space) {
191-
let url = space.urls.published ?? space.urls.app;
192-
if (!url.endsWith('/')) {
193-
url += '/';
242+
const getURL = (path: string, spaceURL?: string) => {
243+
if (spaceURL) {
244+
if (!spaceURL.endsWith('/')) {
245+
spaceURL += '/';
194246
}
195247
if (path.startsWith('/')) {
196248
path = path.slice(1);
197249
}
198-
return url + path;
250+
return spaceURL + path;
199251
} else {
200252
return absoluteHref(path);
201253
}
@@ -206,7 +258,7 @@ function transformPageResult(item: SearchPageResult, space?: Space) {
206258
type: 'section',
207259
id: item.id + '/' + section.id,
208260
title: section.title,
209-
href: getURL(section.path),
261+
href: getURL(section.path, spaceURL),
210262
body: section.body,
211263
})) ?? [];
212264

@@ -218,5 +270,25 @@ function transformPageResult(item: SearchPageResult, space?: Space) {
218270
spaceTitle: space?.title,
219271
};
220272

273+
return [page, sections];
274+
}
275+
276+
function transformSitePageResult(item: SearchPageResult, siteSpace?: SiteSpace) {
277+
const [page, sections] = transformSectionsAndPage({
278+
item,
279+
space: siteSpace?.space,
280+
spaceURL: siteSpace?.urls.published,
281+
});
282+
283+
return [page, ...sections];
284+
}
285+
286+
function transformPageResult(item: SearchPageResult, space?: Space) {
287+
const [page, sections] = transformSectionsAndPage({
288+
item,
289+
space,
290+
spaceURL: space?.urls.published ?? space?.urls.app,
291+
});
292+
221293
return [page, ...sections];
222294
}

src/lib/api.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,41 @@ export const searchParentContent = cache(
10461046
},
10471047
);
10481048

1049+
/**
1050+
* Search content in a Site or specific SiteSpaces.
1051+
*/
1052+
export const searchSiteContent = cache(
1053+
'api.searchSiteContent',
1054+
async (
1055+
organizationId: string,
1056+
siteId: string,
1057+
query: string,
1058+
siteSpaceIds?: string[],
1059+
/** A cache bust param to avoid revalidating lot of cache entries by tags */
1060+
cacheBust?: string,
1061+
options?: CacheFunctionOptions,
1062+
) => {
1063+
const response = await api().orgs.searchSiteContent(
1064+
organizationId,
1065+
siteId,
1066+
{
1067+
query,
1068+
siteSpaceIds,
1069+
},
1070+
undefined,
1071+
{
1072+
...noCacheFetchOptions,
1073+
signal: options?.signal,
1074+
},
1075+
);
1076+
1077+
return cacheResponse(response, {
1078+
ttl: 60 * 60,
1079+
tags: [],
1080+
});
1081+
},
1082+
);
1083+
10491084
/**
10501085
* Get a list of recommended questions in a space.
10511086
*/

0 commit comments

Comments
 (0)