feat(graphile-pg-textsearch-plugin): auto-discover BM25 indexes with condition, score, and orderBy#769
Merged
pyramation merged 3 commits intomainfrom Mar 2, 2026
Conversation
…condition, score, orderBy, and filter New PostGraphile v5 plugin for pg_textsearch (BM25 ranked full-text search). Auto-discovers all text columns with BM25 indexes and provides: - bm25<Column> condition fields on connections (BM25 ranked search) - bm25<Column>Score computed fields on output types (negative BM25 scores) - BM25_<COLUMN>_SCORE_ASC/DESC orderBy enum values - bm25Matches connection filter operator for String scalar - Bm25SearchPreset for zero-config integration Also wires the plugin into ConstructivePreset in graphile-settings.
Contributor
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
…id snapshot breakage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(graphile-pg-textsearch-plugin): auto-discover BM25 indexes with condition, score, and orderBy
Summary
New PostGraphile v5 plugin package (
graphile-pg-textsearch-plugin) for pg_textsearch BM25 ranked full-text search. Follows the same architecture pattern asgraphile-search-plugin(tsvector) andgraphile-pgvector-plugin(vector).Gather phase (
Bm25CodecPlugin):bm25queryPostgreSQL typepg_am/pg_indexto find all columns with BM25 indexes (WHERE am.amname = 'bm25')Map<string, Bm25IndexInfo>bridging gather → schema buildSchema build phase (
Bm25SearchPlugin):bm25<Column>condition fields on connections (accepts{ query, threshold? })bm25<Column>Scorecomputed fields on output types (returns BM25 score when condition active, null otherwise)BM25_<COLUMN>_SCORE_ASC/DESCorderBy enum valuesIntegration:
ConstructivePresetingraphile-settingspyramation/postgres:17Docker image with pg_textsearch pre-installed)Zero config — just add
Bm25SearchPreset()to your preset and all BM25-indexed columns automatically get search capabilities.Updates since last revision
graphile-search-pluginwith centered Constructive logo, CI status badge, license badge, and npm version badge. Also expanded with full usage examples (preset, plugin, GraphQL queries with variables, orderBy), requirements section with BM25 index creation SQL, and testing instructions.bm25Matchesconnection filter operator (previous revision): The earlier version registered abm25Matchesfilter operator globally onStringtype, which added it toStringFilterfor ALL string columns — not just BM25-indexed ones. This broke introspection snapshots ingraphql/testandgraphql/server-test. Removed entirely; thebm25<Column>condition fields are the proper BM25 search interface and are correctly scoped to only BM25-indexed columns.Review & Testing Checklist for Human
pg_textsearch SQL syntax correctness: Verify that
column <@> to_bm25query(query, index_name)matches the actual pg_textsearch extension API. Check that BM25 scores are indeed negative (lower = more relevant) and the operator behavior matches assumptions inbm25-search.tslines 395-460.Gather phase DB access pattern: The plugin accesses the database in
pgIntrospection_introspectionhook viainfo.resolvedPreset?.pgServices(lines 110-112 inbm25-codec.ts). This is a non-standard approach not used by other codec plugins — verify it works across PostGraphile v5 environments.Condition field behavior without threshold: The
bm25<Column>condition field does NOT add a WHERE clause whenthresholdis omitted — it only adds the score to the SELECT list. This means ALL rows are returned (with scores). Is this the desired behavior, or should it filter to only matching documents by default?Module-level state safety:
bm25IndexStoreandbm25ScoreSlotsare module-level singletons. Verify these don't cause issues in multi-tenant or test environments where multiple PostGraphile instances may share the same module.Test Plan
pyramation/postgres:17Docker image.CREATE INDEX articles_body_idx ON articles USING bm25(body) WITH (text_config='english');bm25Bodycondition field,bm25BodyScorecomputed field, andBM25_BODY_SCORE_ASC/DESCorderBy valuesbm25IndexStore) doesn't leak between instancesNotes
ASC(most negative first = best matches first).Session: https://app.devin.ai/sessions/7f9b6b69895d49b7b3f7fddf52f5d4f6
Requested by: @pyramation