import type { FastifyPluginAsyncZod } from 'fastify-type-provider-zod'; import { z } from 'zod'; import { searchAll, type SearchType } from '../lib/search.js'; import { ok, respond } from '../lib/respond.js'; const Result = z.object({ type: z.enum(['cancer', 'gene', 'variant', 'drug', 'trial', 'publication']), id: z.string(), slug: z.string(), name: z.string(), subtitle: z.string().nullable(), score: z.number(), match: z.enum(['exact', 'alias', 'prefix', 'fuzzy']) }); export const searchRoutes: FastifyPluginAsyncZod = async (app) => { app.get('/search', { schema: { tags: ['search'], summary: 'Cross-entity search (exact > alias > prefix > fuzzy; deterministic order; max 20)', querystring: z.object({ q: z.string().trim().min(1).max(200), types: z.string().optional().describe('Comma-separated subset of cancer,gene,variant,drug,trial,publication'), limit: z.coerce.number().int().min(1).max(20).default(20) }), response: ok(z.array(Result)) } }, async (req) => { const types = req.query.types ?.split(',') .map((t) => t.trim()) .filter((t): t is SearchType => ['cancer', 'gene', 'variant', 'drug', 'trial', 'publication'].includes(t)); const results = await searchAll(app.db, req.query.q, types, req.query.limit); return respond(app, results, []); }); };