/** * Search tokens, computed in JavaScript for both the index and the query. * * Postgres's text parser depends on the database's locale: on some locales it * drops Japanese text entirely, and PGlite and Neon don't agree. So core never * asks Postgres to tokenize. It builds `tsvector` and `tsquery` literals * itself, and the database only stores, indexes, and matches them. The same * code tokenizes documents and queries, so they always agree. * * Rules: * - Text is NFKC-normalized and lowercased. There is no stemming and there are * no stopwords. * - A word is a run of letters, numbers, and combining marks. Everything else * separates words, so `snake_case`, `kebab-case`, URLs, and paths become * their parts at consecutive positions, and a query for the same text * matches them as a phrase. * - A camelCase or PascalCase word is indexed whole and as its parts, so * "camelCase", "camel", and "case camel" all find it. * - A word longer than Postgres's 2,046-byte limit keeps its longest prefix * that fits. * - Chinese, Japanese, and Korean runs become overlapping character pairs, * because they have no spaces to split on. A query becomes the same pairs * as a phrase, which matches exactly that substring. A run's last character * starts no pair, so it is also indexed alone, and a one-character query * finds every character as a prefix. */ export type SearchWeight = "A" | "B" | "C" | "D"; /** Normalized form used for title and summary comparisons. */ export declare function normalizeSearchText(value: string): string; /** UTF-8 length without encoding: 1-3 bytes per UTF-16 unit, 4 per pair. */ export declare function utf8ByteLength(value: string): number; export interface SearchToken { lexeme: string; position: number; } /** * Document tokens with positions starting at `start`. Returns the next free * position so several fields can share one position space. */ export declare function documentTokens(text: string, start?: number): { tokens: SearchToken[]; next: number; }; /** * Query lexemes for one term, in order. Unlike documents, a query word is * never split on case: "camelCase" looks for the whole word, which documents * index alongside its parts. */ export declare function queryLexemes(text: string): string[]; export interface WeightedField { text: string | null | undefined; weight: SearchWeight; } export interface SearchVector { /** A `tsvector` literal. */ literal: string; /** * False when Postgres's limits made the vector drop or merge word * positions, so phrase matching can't be exact for this document. */ positionsComplete: boolean; } /** * A `tsvector` for the fields, in order, sharing one position space with a * gap between fields, so a phrase never spans two of them. Postgres keeps at * most 255 positions per word and none past 16,383. Past the last position, * each field's words collapse onto its own position, because Postgres merges * equal positions and keeps only the higher weight. A word over 255 * positions keeps its first position in each field. A very large document * keeps one position per word in each field, and one with more distinct * words than fit keeps the words that come first, so the vector stays under * Postgres's size limit. Any of these makes `positionsComplete` false; each * keeps which fields hold each word. */ export declare function buildSearchVector(fields: readonly WeightedField[]): SearchVector; /** * A term with more words than the index can match. The query can't be * answered from the index; the app's fallback search can answer it. */ export declare class SearchTermTooLongError extends RangeError { constructor(); } export interface QueryPhraseOptions { /** Treat the last lexeme as a prefix. */ prefix?: boolean; /** Restrict every lexeme to these weights. */ weights?: string; /** * Match the lexemes anywhere, in any order, instead of as a phrase. For * documents whose positions aren't complete. */ anyOrder?: boolean; } /** * One term as a `tsquery` literal: a single lexeme, or a phrase of adjacent * lexemes. Returns null when the term has nothing to match. Throws * `SearchTermTooLongError` past `MAX_QUERY_LEXEMES`, which Postgres can't * evaluate. */ export declare function termTsquery(text: string, options?: QueryPhraseOptions): string | null; /** Whether a term is more than one lexeme, so it matches as a phrase. */ export declare function isPhraseTerm(text: string): boolean; export declare function anyOfTsquery(parts: readonly (string | null)[]): string | null;