Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 66 additions & 110 deletions packages/gitbook/src/components/Search/server-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,53 +66,6 @@ export interface AskAnswerResult {
sources: AskAnswerSource[];
}

/**
* Server action to search content in the entire site.
*/
export async function searchAllSiteContent(query: string): Promise<OrderedComputedResult[]> {
return traceErrorOnly('Search.searchAllSiteContent', async () => {
const context = await getServerActionBaseContext();
return searchSiteContent(context, {
query,
scope: { mode: 'all' },
});
});
}

/**
* Server action to search content in a space.
*/
export async function searchCurrentSiteSpaceContent(
query: string,
siteSpaceId: string
): Promise<OrderedComputedResult[]> {
return traceErrorOnly('Search.searchSiteSpaceContent', async () => {
const context = await getServerActionBaseContext();

return await searchSiteContent(context, {
query,
scope: { mode: 'current', siteSpaceId },
});
});
}

/**
* Server action to search content in a specific space.
*/
export async function searchSpecificSiteSpaceContent(
query: string,
siteSpaceIds: string[]
): Promise<OrderedComputedResult[]> {
return traceErrorOnly('Search.searchSiteSpaceContent', async () => {
const context = await getServerActionBaseContext();

return await searchSiteContent(context, {
query,
scope: { mode: 'specific', siteSpaceIds },
});
});
}

/**
* Server action to ask a question in a space.
*/
Expand Down Expand Up @@ -245,69 +198,72 @@ export async function streamRecommendedQuestions(args: { siteSpaceId?: string })
/**
* Search for content in a site by scoping the search to all content, a specific spaces or current space.
*/
async function searchSiteContent(
context: GitBookBaseContext,
args: {
query: string;
scope:
| { mode: 'all' }
| { mode: 'current'; siteSpaceId: string }
| { mode: 'specific'; siteSpaceIds: string[] };
}
): Promise<OrderedComputedResult[]> {
const { dataFetcher } = context;
const siteURLData = await getSiteURLDataFromMiddleware();

const { scope, query } = args;

if (query.length <= 1) {
return [];
}

const [searchResults, { structure }] = await Promise.all([
throwIfDataError(
dataFetcher.searchSiteContent({
organizationId: siteURLData.organization,
siteId: siteURLData.site,
query,
scope,
})
),
throwIfDataError(
dataFetcher.getPublishedContentSite({
organizationId: siteURLData.organization,
siteId: siteURLData.site,
siteShareKey: siteURLData.shareKey,
})
),
]);

return (
await Promise.all(
searchResults.map((spaceItem) => {
const found = findSiteSpaceBy(
structure,
(siteSpace) => siteSpace.space.id === spaceItem.id
);
const siteSection = found?.siteSection;
const siteSectionGroup = found?.siteSectionGroup;

return Promise.all(
spaceItem.pages.map((pageItem) =>
transformSitePageResult(context, {
pageItem,
spaceItem,
siteSpace: found?.siteSpace,
space: found?.siteSpace.space,
spaceURL: found?.siteSpace.urls.published,
siteSection: siteSection ?? undefined,
siteSectionGroup: (siteSectionGroup as SiteSectionGroup) ?? undefined,
})
)
);
})
)
).flat(2);
export async function searchSiteContent({
query,
...scope
}: {
query: string;
} & (
| { mode: 'all' }
| { mode: 'current'; siteSpaceId: string }
| { mode: 'specific'; siteSpaceIds: string[] }
)): Promise<OrderedComputedResult[]> {
return traceErrorOnly(`Search.searchSiteContent.${scope.mode}`, async () => {
if (query.length <= 1) {
return [];
}

const [context, { organization, site, shareKey }] = await Promise.all([
getServerActionBaseContext(),
getSiteURLDataFromMiddleware(),
]);

const [searchResults, { structure }] = await Promise.all([
throwIfDataError(
context.dataFetcher.searchSiteContent({
organizationId: organization,
siteId: site,
query,
scope,
})
),
throwIfDataError(
context.dataFetcher.getPublishedContentSite({
organizationId: organization,
siteId: site,
siteShareKey: shareKey,
})
),
]);

return (
await Promise.all(
searchResults.map((spaceItem) => {
const found = findSiteSpaceBy(
structure,
(siteSpace) => siteSpace.space.id === spaceItem.id
);
const siteSection = found?.siteSection;
const siteSectionGroup = found?.siteSectionGroup;

return Promise.all(
spaceItem.pages.map((pageItem) =>
transformSitePageResult(context, {
pageItem,
spaceItem,
siteSpace: found?.siteSpace,
space: found?.siteSpace.space,
spaceURL: found?.siteSpace.urls.published,
siteSection: siteSection ?? undefined,
siteSectionGroup:
(siteSectionGroup as SiteSectionGroup) ?? undefined,
})
)
);
})
)
).flat(2);
});
}

async function transformAnswer(
Expand Down
40 changes: 21 additions & 19 deletions packages/gitbook/src/components/Search/useSearchResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import { assert } from 'ts-essentials';

import {
type OrderedComputedResult,
searchAllSiteContent,
searchCurrentSiteSpaceContent,
searchSpecificSiteSpaceContent,
searchSiteContent,
streamRecommendedQuestions,
} from './server-actions';

import { type Assistant, useAI } from '@/components/AI';
import assertNever from 'assert-never';
import { useTrackEvent } from '../Insights';
import { isQuestion } from './isQuestion';
import type { SearchScope } from './useSearch';
Expand Down Expand Up @@ -123,23 +122,26 @@ export function useSearchResults(props: {
const timeout = setTimeout(async () => {
try {
const results = await (() => {
if (scope === 'all') {
// Search all content on the site
return searchAllSiteContent(query);
switch (scope) {
case 'all':
// Search all content on the site
return searchSiteContent({ query, mode: 'all' });
case 'default':
// Search the current section's variant + matched/default variant for other sections
return searchSiteContent({ query, mode: 'current', siteSpaceId });
case 'extended':
// Search all variants of the current section
return searchSiteContent({ query, mode: 'specific', siteSpaceIds });
case 'current':
// Search only the current section's current variant
return searchSiteContent({
query,
mode: 'specific',
siteSpaceIds: [siteSpaceId],
});
default:
assertNever(scope);
}
if (scope === 'default') {
// Search the current section's variant + matched/default variant for other sections
return searchCurrentSiteSpaceContent(query, siteSpaceId);
}
if (scope === 'extended') {
// Search all variants of the current section
return searchSpecificSiteSpaceContent(query, siteSpaceIds);
}
if (scope === 'current') {
// Search only the current section's current variant
return searchSpecificSiteSpaceContent(query, [siteSpaceId]);
}
throw new Error(`Unhandled search scope: ${scope}`);
})();

if (cancelled) {
Expand Down
25 changes: 17 additions & 8 deletions packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,10 @@ export function SpaceLayout(props: SpaceLayoutProps) {
</div>
)
}
// Displays the search button and/or the space dropdown in the ToC
// according to the header/variant settings.
// E.g if there is no header, the search button will be displayed in the ToC.
innerHeader={
// displays the search button and/or the space dropdown in the ToC according to the header/variant settings. E.g if there is no header, the search button will be displayed in the ToC.
<>
{!withTopHeader && (
<div className="flex gap-2">
Expand All @@ -189,13 +191,20 @@ export function SpaceLayout(props: SpaceLayoutProps) {
section={sections?.current}
spaceTitle={siteSpace.title}
siteSpaceId={siteSpace.id}
siteSpaceIds={siteSpaces
.filter(
(s) =>
s.space.language ===
siteSpace.space.language
)
.map((s) => s.id)}
// If searching all variants of the current section
// (the "extended" scope), filter by language if the language
// is set for both the current and the target site space.
siteSpaceIds={siteSpaces.reduce((acc: string[], ss) => {
if (
!siteSpace.space.language ||
!ss.space.language ||
ss.space.language === siteSpace.space.language
) {
acc.push(ss.id);
}

return acc;
}, [])}
className="max-lg:hidden"
viewport="desktop"
/>
Expand Down
Loading