/** * Migrate RediSearch indices to RespLite FT.* search indices (SPEC_F §F.10). * * For each index in the source Redis: * 1. FT._LIST → enumerate index names * 2. FT.INFO → read schema (prefix patterns, field attributes) * 3. Map RediSearch field types to RespLite TEXT fields * 4. FT.CREATE in RespLite (or reuse the existing destination index when skipExisting=true) * 5. SCAN keys by prefix → HGETALL → addDocument in SQLite batches * 6. FT.SUGGET → import suggestions * * Graceful shutdown: SIGINT/SIGTERM finishes the current document, checkpoints, closes DB. */ import type { MigrationRedisClient } from './redis-client.ts'; /** RediSearch field attribute as read from FT.INFO. */ export interface SearchAttribute { identifier: string; attribute: string; type: string; } export interface SearchIndexInfo { keyType: string; prefixes: string[]; attributes: SearchAttribute[]; } export interface MappedFields { fields: Array<{ name: string; type: string; }>; /** identifier (hash field name) → RespLite field name */ fieldMap: Map; warnings: string[]; } /** Outcome of migrating one index. */ export interface IndexResult { name: string; created: boolean; skipped: boolean; docsImported: number; docsSkipped: number; docErrors: number; sugsImported: number; warnings: string[]; error?: string; } export interface MigrateSearchOptions { /** PRAGMA preset. Default `'default'`. */ pragmaTemplate?: string; /** Restrict to these index names. */ onlyIndices?: string[] | null; /** COUNT hint for SCAN. Default 500. */ scanCount?: number; /** Max Redis requests/s (0 = unlimited). Default 0. */ maxRps?: number; /** Docs per SQLite transaction. Default 200. */ batchDocs?: number; /** Cap for FT.SUGGET import. Default 10000. */ maxSuggestions?: number; /** Reuse an existing destination index and skip FT.CREATE instead of failing. Default true. */ skipExisting?: boolean; /** Also migrate suggestions. Default true. */ withSuggestions?: boolean; onProgress?: (result: IndexResult) => void; } export interface MigrateSearchResult { indices: IndexResult[]; aborted: boolean; } /** * Map RediSearch field attributes to RespLite schema fields. * * - TEXT → TEXT (1:1) * - TAG, NUMERIC → TEXT (with warning; values stringified at import time) * - GEO, VECTOR, … → skipped with a warning * - Always guarantees a `payload` TEXT field exists (added if absent) */ declare function mapFields(attributes: SearchAttribute[]): MappedFields; /** * Build the fields object for addDocument from a HGETALL result. * Fields absent from the hash default to ''. * If `payload` is empty, synthesise it as the concatenation of all other values. * * @param fieldMap identifier → RespLite field name */ declare function buildDocFields(hashData: Record, fieldMap: Map, schemaFields: Array<{ name: string; }>): Record; /** * Read document score from Redis hash fields. * Prefers `__score`, then `score`, and falls back to `1.0`. */ declare function getDocScore(hashData: Record): number; /** * Migrate all (or selected) RediSearch indices from a Redis source into the RespLite DB. * * On SIGINT/SIGTERM: finish the current document, then stop gracefully. * DB is always closed in a finally block. */ export declare function runMigrateSearch(redisClient: MigrationRedisClient, dbPath: string, options?: MigrateSearchOptions): Promise; export { mapFields, buildDocFields, getDocScore };