graphile-search-plugin
Version:
Generate search conditions for your tsvector columns (PostGraphile v5)
76 lines (75 loc) • 3.46 kB
TypeScript
/**
* PostGraphile v5 Search Plugin
*
* Generates search condition fields for tsvector columns. When a search term
* is provided via the condition input, this plugin applies a
* `column @@ websearch_to_tsquery('english', $value)` WHERE clause.
* Results are ordered by `ts_rank` only when explicitly requested via
* the `FULL_TEXT_RANK_ASC/DESC` orderBy enum values (not automatically),
* ensuring cursor pagination digests remain stable across pages.
*
* Additionally provides:
* - `matches` filter operator for postgraphile-plugin-connection-filter
* - `fullTextRank` computed fields on output types (null when no search active)
* - `FULL_TEXT_RANK_ASC/DESC` orderBy enum values
*
* Uses the graphile-build hooks API to extend condition input types with
* search fields for each tsvector column found on a table's codec.
*
* ARCHITECTURE NOTE:
* Uses the Grafast meta system (setMeta/getMeta) to pass data between
* the condition apply phase, the orderBy enum apply, and the output field
* plan, following the pattern from Benjie's postgraphile-plugin-fulltext-filter.
*
* 1. Condition apply (runs first): adds ts_rank to the query builder's
* SELECT list via selectAndReturnIndex, stores { selectIndex, scoreFragment }
* in meta via qb.setMeta(key, { selectIndex, scoreFragment }).
* 2. OrderBy enum apply (runs second): reads the scoreFragment from meta
* and calls qb.orderBy({ fragment, codec, direction }) directly.
* 3. Output field plan (planning phase): calls $select.getMeta(key) which
* returns a Grafast Step that resolves at execution time.
* 4. lambda([$details, $row]) reads the rank from row[details.selectIndex].
*/
import 'graphile-build';
import 'graphile-build-pg';
import type { PgCodecWithAttributes, PgResource } from '@dataplan/pg';
import type { GraphileConfig } from 'graphile-config';
import type { PgSearchPluginOptions } from './types';
declare global {
namespace GraphileBuild {
interface Inflection {
/** Name for the FullText scalar type */
fullTextScalarTypeName(this: Inflection): string;
/** Name for the rank field (e.g. "bodyRank") */
pgTsvRank(this: Inflection, fieldName: string): string;
/** Name for orderBy enum value for column rank */
pgTsvOrderByColumnRankEnum(this: Inflection, codec: PgCodecWithAttributes, attributeName: string, ascending: boolean): string;
/** Name for orderBy enum value for computed column rank */
pgTsvOrderByComputedColumnRankEnum(this: Inflection, codec: PgCodecWithAttributes, resource: PgResource, ascending: boolean): string;
}
interface ScopeObjectFieldsField {
isPgTSVRankField?: boolean;
}
interface BehaviorStrings {
'attributeFtsRank:select': true;
'procFtsRank:select': true;
'attributeFtsRank:orderBy': true;
'procFtsRank:orderBy': true;
}
}
namespace GraphileConfig {
interface Plugins {
PgSearchPlugin: true;
}
}
}
/**
* Creates the search plugin with the given options.
*/
export declare function createPgSearchPlugin(options?: PgSearchPluginOptions): GraphileConfig.Plugin;
/**
* Creates a PgSearchPlugin with the given options.
* This is the main entry point for using the plugin.
*/
export declare const PgSearchPlugin: typeof createPgSearchPlugin;
export default PgSearchPlugin;