Add support for search in a sections site (#2541)

This commit is contained in:
Taran Vohra
2024-10-21 15:54:29 +05:30
committed by GitHub
parent 7588774237
commit cda08a9843
7 changed files with 77 additions and 40 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'gitbook': minor
---
Add support for searching results in a sections site
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -16,7 +16,7 @@
"clean": "rm -rf ./.next && rm -rf ./public/~gitbook/static"
},
"dependencies": {
"@gitbook/api": "^0.71.0",
"@gitbook/api": "^0.72.0",
"@gitbook/cache-do": "workspace:*",
"@gitbook/emoji-codepoints": "workspace:*",
"@gitbook/icons": "workspace:*",
@@ -12,8 +12,8 @@ import { SearchSectionResultItem } from './SearchSectionResultItem';
import {
getRecommendedQuestions,
OrderedComputedResult,
searchCurrentSpaceContent,
searchSiteContent,
searchSiteSpaceContent,
searchAllSiteContent,
} from './server-actions';
import { Loading } from '../primitives';
@@ -95,8 +95,8 @@ export const SearchResults = React.forwardRef(function SearchResults(
setCursor(null);
const fetchedResults = await (global
? searchSiteContent({ query, pointer })
: searchCurrentSpaceContent(query, pointer, revisionId));
? searchAllSiteContent(query, pointer)
: searchSiteSpaceContent(query, pointer, revisionId));
setResults(withAsk ? withQuestionResult(fetchedResults, query) : fetchedResults);
}, 350);
@@ -2,6 +2,7 @@
import { RevisionPage, SearchAIAnswer, SearchPageResult, SiteSpace, Space } from '@gitbook/api';
import * as React from 'react';
import { assert } from 'ts-essentials';
import { streamResponse } from '@/lib/actions';
import * as api from '@/lib/api';
@@ -45,47 +46,61 @@ export interface AskAnswerResult {
}
/**
* Search for content in the entire site.
* Search for content in a site by scoping the search to all content, a specific spaces or current space.
*/
export async function searchSiteContent(args: {
async function searchSiteContent(args: {
pointer: api.SiteContentPointer;
query: string;
siteSpaceIds?: string[];
scope:
| { mode: 'all' }
| { mode: 'current'; siteSpaceId: string }
| { mode: 'specific'; siteSpaceIds: string[] };
cacheBust?: string;
}): Promise<OrderedComputedResult[]> {
const { pointer, siteSpaceIds, query, cacheBust } = args;
const { pointer, scope, query, cacheBust } = args;
if (query.length <= 1) {
return [];
}
if (siteSpaceIds?.length === 0) {
// if we have no siteSpaces to search in then we won't find anything. skip the call.
return [];
}
const needsStructure =
scope.mode === 'all' ||
scope.mode === 'current' ||
(scope.mode === 'specific' && scope.siteSpaceIds.length > 1);
const [searchResults, allSiteSpaces] = await Promise.all([
api.searchSiteContent(
pointer.organizationId,
pointer.siteId,
query,
siteSpaceIds,
cacheBust,
),
siteSpaceIds
? null
: api.getSiteSpaces({
const [searchResults, siteStructure] = await Promise.all([
api.searchSiteContent(pointer.organizationId, pointer.siteId, query, scope, cacheBust),
needsStructure
? api.getSiteStructure({
organizationId: pointer.organizationId,
siteId: pointer.siteId,
siteShareKey: pointer.siteShareKey,
}),
})
: null,
]);
if (!siteSpaceIds) {
const siteSpaces = siteStructure
? siteStructure.type === 'siteSpaces'
? siteStructure.structure
: siteStructure.structure.reduce<SiteSpace[]>((prev, section) => {
const sectionSiteSpaces = section.siteSpaces.map((siteSpace) => ({
...siteSpace,
space: {
...siteSpace.space,
title: section.title + ' › ' + siteSpace.space.title,
},
}));
prev.push(...sectionSiteSpaces);
return prev;
}, [])
: null;
if (siteSpaces) {
// We are searching all of this Site's content
return searchResults.items
.map((spaceItem) => {
const siteSpace = allSiteSpaces?.find(
const siteSpace = siteSpaces.find(
(siteSpace) => siteSpace.space.id === spaceItem.id,
);
@@ -102,21 +117,39 @@ export async function searchSiteContent(args: {
}
/**
* Server action to search content in the current space
* Server action to search content in the entire site.
*/
export async function searchCurrentSpaceContent(
export async function searchAllSiteContent(
query: string,
pointer: api.SiteContentPointer,
): Promise<OrderedComputedResult[]> {
return await searchSiteContent({
pointer,
query,
scope: { mode: 'all' },
});
}
/**
* Server action to search content in a space.
*/
export async function searchSiteSpaceContent(
query: string,
pointer: api.SiteContentPointer,
revisionId: string,
): Promise<OrderedComputedResult[]> {
const siteSpaceIds = pointer.siteSpaceId ? [pointer.siteSpaceId] : []; // if we don't have a siteSpaceID search all content
const siteSpaceId = pointer.siteSpaceId;
assert(siteSpaceId, 'Expected siteSpaceId for searchSiteSpaceContent');
// This is a site so use a different function which we can eventually call directly
// We also want to break cache for this specific space if the revisionId is different so use it as a cache busting key
return await searchSiteContent({
pointer,
siteSpaceIds,
query,
// If we have a siteSectionId that means its a sections site use `current` mode
// which searches in the current space + all default spaces of sections
scope: pointer.siteSectionId
? { mode: 'current', siteSpaceId }
: { mode: 'specific', siteSpaceIds: [siteSpaceId] },
// We want to break cache for this specific space if the revisionId is different so use it as a cache busting key
cacheBust: revisionId,
});
}
+5 -6
View File
@@ -1058,7 +1058,10 @@ export const searchSiteContent = cache({
organizationId: string,
siteId: string,
query: string,
siteSpaceIds?: string[],
scope:
| { mode: 'all' }
| { mode: 'current'; siteSpaceId: string }
| { mode: 'specific'; siteSpaceIds: string[] },
/** A cache bust param to avoid revalidating lot of cache entries by tags */
cacheBust?: string,
options?: CacheFunctionOptions,
@@ -1068,11 +1071,7 @@ export const searchSiteContent = cache({
siteId,
{
query,
...(siteSpaceIds && siteSpaceIds.length > 0
? { siteSpaceIds }
: {
mode: 'all',
}),
...scope,
},
undefined,
{
+1 -1
View File
@@ -10,7 +10,7 @@
},
"dependencies": {
"classnames": "^2.5.1",
"@gitbook/api": "^0.66.0",
"@gitbook/api": "^0.72.0",
"assert-never": "^1.2.1"
},
"peerDependencies": {