(request: NextRequest)
| 34 | * - Other languages: Keyword search only |
| 35 | */ |
| 36 | export async function GET(request: NextRequest) { |
| 37 | try { |
| 38 | const { query, locale, limit } = getSearchParams(request) |
| 39 | |
| 40 | if (!query || query.trim().length === 0) { |
| 41 | return NextResponse.json([]) |
| 42 | } |
| 43 | |
| 44 | const candidateLimit = limit * 3 |
| 45 | const similarityThreshold = 0.6 |
| 46 | |
| 47 | const localeMap: Record<string, string> = { |
| 48 | en: 'english', |
| 49 | es: 'spanish', |
| 50 | fr: 'french', |
| 51 | de: 'german', |
| 52 | ja: 'simple', // PostgreSQL doesn't have Japanese support, use simple |
| 53 | zh: 'simple', // PostgreSQL doesn't have Chinese support, use simple |
| 54 | } |
| 55 | const tsConfig = localeMap[locale] || 'simple' |
| 56 | |
| 57 | const useVectorSearch = locale === 'en' |
| 58 | let vectorResults: Array<{ |
| 59 | chunkId: string |
| 60 | chunkText: string |
| 61 | sourceDocument: string |
| 62 | sourceLink: string |
| 63 | headerText: string |
| 64 | headerLevel: number |
| 65 | similarity: number |
| 66 | searchType: string |
| 67 | }> = [] |
| 68 | |
| 69 | if (useVectorSearch) { |
| 70 | const queryEmbedding = await generateSearchEmbedding(query) |
| 71 | vectorResults = await db |
| 72 | .select({ |
| 73 | chunkId: docsEmbeddings.chunkId, |
| 74 | chunkText: docsEmbeddings.chunkText, |
| 75 | sourceDocument: docsEmbeddings.sourceDocument, |
| 76 | sourceLink: docsEmbeddings.sourceLink, |
| 77 | headerText: docsEmbeddings.headerText, |
| 78 | headerLevel: docsEmbeddings.headerLevel, |
| 79 | similarity: sql<number>`1 - (${docsEmbeddings.embedding} <=> ${JSON.stringify(queryEmbedding)}::vector)`, |
| 80 | searchType: sql<string>`'vector'`, |
| 81 | }) |
| 82 | .from(docsEmbeddings) |
| 83 | .where( |
| 84 | sql`1 - (${docsEmbeddings.embedding} <=> ${JSON.stringify(queryEmbedding)}::vector) >= ${similarityThreshold}` |
| 85 | ) |
| 86 | .orderBy(sql`${docsEmbeddings.embedding} <=> ${JSON.stringify(queryEmbedding)}::vector`) |
| 87 | .limit(candidateLimit) |
| 88 | } |
| 89 | |
| 90 | const keywordResults = await db |
| 91 | .select({ |
| 92 | chunkId: docsEmbeddings.chunkId, |
| 93 | chunkText: docsEmbeddings.chunkText, |
nothing calls this directly
no test coverage detected