mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-24 19:32:07 +00:00
Add support for search in a sections site (#2541)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'gitbook': minor
|
||||
---
|
||||
|
||||
Add support for searching results in a sections site
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
{
|
||||
|
||||
@@ -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": {
|
||||
|
||||
Reference in New Issue
Block a user