mirror of
https://github.com/supabase/supabase.git
synced 2026-05-09 02:09:50 -04:00
ce5cce5030
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? feature ## What is the current behavior? Currently, old GitHub discussions appear in the docs search instead of troubleshooting guides in docs/guides/troubleshooting ## What is the new behavior? Local troubleshooting guides appear in the search ## Additional context <img width="958" height="846" alt="CleanShot 2026-01-31 at 23 37 33@2x" src="https://github.com/user-attachments/assets/445fab5d-764a-4b4d-b4ef-c29ab675a9ae" /> **troubleshooting.ts** - New source loader that reads local MDX files from content/troubleshooting/ directly instead of fetching from GitHub Discussions API - Generates correct docs paths: /guides/troubleshooting/{slug} - Uses type = 'troubleshooting' for proper search result mapping - Sets slug: undefined to avoid trailing # in URLs - Checksum includes title/topics/keywords so metadata-only changes trigger re-indexing - Left comments for review **index.ts** - Replaced GitHub discussion sources with local troubleshooting sources - Removed GitHubDiscussionLoader, fetchDiscussions, buildGithubUrlToSlugMap imports - Added fetchTroubleshootingSources and TroubleshootingSource - Updated SearchSource type union **globalSearchModel.ts** - Changed type mapping from 'github-discussions' to 'troubleshooting' **generate-embeddings.ts** - Removed GitHub App env vars from required list (DOCS_GITHUB_APP_ID, DOCS_GITHUB_APP_INSTALLATION_ID, DOCS_GITHUB_APP_PRIVATE_KEY) since they're no longer needed <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Local troubleshooting articles are now indexed and appear directly in search results for easier access to step‑by‑step guidance. * Search UI now recognizes a Troubleshooting page type and shows appropriate icons/sections. * **Refactor** * Search sourcing switched from external discussion feeds to local troubleshooting sources to improve relevance and indexing consistency. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Illia Basalaiev <illiab@IMB3.local> Co-authored-by: Charis Lam <26616127+charislam@users.noreply.github.com> Co-authored-by: Chris Chinchilla <chris.ward@supabase.io>
141 lines
4.5 KiB
TypeScript
141 lines
4.5 KiB
TypeScript
import { type RootQueryTypeSearchDocsArgs } from '~/__generated__/graphql'
|
|
import { convertPostgrestToApiError, type ApiErrorGeneric } from '~/app/api/utils'
|
|
import { Result } from '~/features/helpers.fn'
|
|
import { openAI } from '~/lib/openAi'
|
|
import { supabase, type DatabaseCorrected } from '~/lib/supabase'
|
|
|
|
import { isFeatureEnabled } from '../../../../packages/common/enabled-features'
|
|
import { GuideModel } from '../guide/guideModel'
|
|
import {
|
|
DB_METADATA_TAG_PLATFORM_CLI,
|
|
ReferenceCLICommandModel,
|
|
} from '../reference/referenceCLIModel'
|
|
import { ReferenceManagementApiModel } from '../reference/referenceManagementApiModel'
|
|
import { ReferenceSDKFunctionModel, SDKLanguageValues } from '../reference/referenceSDKModel'
|
|
import { TroubleshootingModel } from '../troubleshooting/troubleshootingModel'
|
|
import { SearchResultInterface } from './globalSearchInterface'
|
|
|
|
type SearchFunction = 'search_content' | 'search_content_nimbus'
|
|
type SearchHybridFunction = 'search_content_hybrid' | 'search_content_hybrid_nimbus'
|
|
|
|
export abstract class SearchResultModel {
|
|
static async search(
|
|
args: RootQueryTypeSearchDocsArgs,
|
|
requestedFields: Array<string>
|
|
): Promise<Result<SearchResultModel[], ApiErrorGeneric>> {
|
|
const query = args.query.trim()
|
|
const includeFullContent = requestedFields.includes('content')
|
|
const embeddingResult = await openAI().createContentEmbedding(query)
|
|
|
|
const useAltSearchIndex = !isFeatureEnabled('search:fullIndex')
|
|
const searchFunction: SearchFunction = useAltSearchIndex
|
|
? 'search_content_nimbus'
|
|
: 'search_content'
|
|
|
|
return embeddingResult.flatMapAsync(async ({ embedding }) => {
|
|
const matchResult = new Result(
|
|
await supabase().rpc(searchFunction, {
|
|
embedding,
|
|
include_full_content: includeFullContent,
|
|
max_result: args.limit ?? undefined,
|
|
})
|
|
)
|
|
.map((matches) =>
|
|
matches
|
|
.map(createModelFromMatch)
|
|
.filter((item): item is SearchResultInterface => item !== null)
|
|
)
|
|
.mapError(convertPostgrestToApiError)
|
|
|
|
return matchResult
|
|
})
|
|
}
|
|
|
|
static async searchHybrid(
|
|
args: RootQueryTypeSearchDocsArgs,
|
|
requestedFields: Array<string>
|
|
): Promise<Result<SearchResultModel[], ApiErrorGeneric>> {
|
|
const query = args.query.trim()
|
|
const includeFullContent = requestedFields.includes('content')
|
|
const embeddingResult = await openAI().createContentEmbedding(query)
|
|
|
|
const useAltSearchIndex = !isFeatureEnabled('search:fullIndex')
|
|
const searchFunction: SearchHybridFunction = useAltSearchIndex
|
|
? 'search_content_hybrid_nimbus'
|
|
: 'search_content_hybrid'
|
|
|
|
return embeddingResult.flatMapAsync(async ({ embedding }) => {
|
|
const matchResult = new Result(
|
|
await supabase().rpc(searchFunction, {
|
|
query_text: query,
|
|
query_embedding: embedding,
|
|
include_full_content: includeFullContent,
|
|
max_result: args.limit ?? 30,
|
|
})
|
|
)
|
|
.map((matches) =>
|
|
matches
|
|
.map(createModelFromMatch)
|
|
.filter((item): item is SearchResultInterface => item !== null)
|
|
)
|
|
.mapError(convertPostgrestToApiError)
|
|
|
|
return matchResult
|
|
})
|
|
}
|
|
}
|
|
|
|
function createModelFromMatch({
|
|
type,
|
|
page_title,
|
|
href,
|
|
content,
|
|
metadata,
|
|
subsections,
|
|
}: DatabaseCorrected['public']['Functions']['search_content']['Returns'][number]): SearchResultInterface | null {
|
|
switch (type) {
|
|
case 'markdown':
|
|
return new GuideModel({
|
|
title: page_title,
|
|
href,
|
|
content,
|
|
subsections,
|
|
})
|
|
case 'reference':
|
|
const { language } = metadata
|
|
if (language && SDKLanguageValues.includes(language)) {
|
|
return new ReferenceSDKFunctionModel({
|
|
title: page_title,
|
|
href,
|
|
content,
|
|
language,
|
|
methodName: metadata.methodName,
|
|
})
|
|
} else if (metadata.platform === DB_METADATA_TAG_PLATFORM_CLI) {
|
|
return new ReferenceCLICommandModel({
|
|
title: page_title,
|
|
href,
|
|
content,
|
|
subsections,
|
|
})
|
|
// TODO [Charis 2025-06-09] replace with less hacky check
|
|
} else if (metadata.subtitle?.startsWith('Management API Reference')) {
|
|
return new ReferenceManagementApiModel({
|
|
title: page_title,
|
|
href,
|
|
content,
|
|
})
|
|
} else {
|
|
return null
|
|
}
|
|
case 'troubleshooting':
|
|
return new TroubleshootingModel({
|
|
title: page_title,
|
|
href,
|
|
content,
|
|
})
|
|
default:
|
|
return null
|
|
}
|
|
}
|