Skip to content

feat: consolidate search packages into unified graphile-search plugin#814

Open
pyramation wants to merge 59 commits intomainfrom
devin/1773437586-graphile-search
Open

feat: consolidate search packages into unified graphile-search plugin#814
pyramation wants to merge 59 commits intomainfrom
devin/1773437586-graphile-search

Conversation

@pyramation
Copy link
Contributor

@pyramation pyramation commented Mar 13, 2026

feat: consolidate search packages into unified graphile-search plugin

Summary

Consolidates 4 individual search packages (graphile-tsvector, graphile-bm25, graphile-trgm, graphile-pgvector) into the single graphile-search package. All 4 packages are deleted with no deprecation — codec plugins, operator factories, and adapters now live inside graphile-search with tree-shakable exports.

This builds on the original v1 unified search plugin (adapter pattern) and promotes it from standalone proof-of-concept to the canonical search package.

What was deleted

  • graphile/graphile-tsvector/ — entire package
  • graphile/graphile-bm25/ — entire package
  • graphile/graphile-trgm/ — entire package
  • graphile/graphile-pgvector/ — entire package
  • CI test jobs for graphile-search-plugin and graphile-pgvector-plugin

What was moved into graphile-search/

  • src/codecs/tsvector-codec.ts — tsvector/tsquery type registration (from graphile-tsvector)
  • src/codecs/bm25-codec.ts — BM25 index discovery + codec (from graphile-bm25)
  • src/codecs/vector-codec.ts — pgvector type registration (from graphile-pgvector)
  • src/codecs/operator-factories.tscreateMatchesOperatorFactory + createTrgmOperatorFactories (from graphile-tsvector/graphile-trgm)
  • src/codecs/index.ts — barrel re-exports

What was updated

  • graphile-search/src/index.ts — tree-shakable exports for all codecs, types, and operator factories
  • graphile-search/src/preset.tsUnifiedSearchPreset now includes codec plugins + operator factories
  • graphile-search/src/adapters/bm25.ts — imports from ../codecs/bm25-codec instead of graphile-bm25
  • graphile-settings/src/presets/constructive-preset.ts — imports from graphile-search instead of 4 packages
  • graphile-settings/src/plugins/index.ts — consolidated re-exports from graphile-search only
  • graphile-settings/package.json — removed 4 old deps, added graphile-search
  • .github/workflows/run-tests.yaml — removed old package test entries, added graphile-search + graphile-settings
  • Test file — added mega query v2 (fullTextSearch + searchScore with composite ordering)
  • Snapshot files — updated graphql/server-test and graphql/test snapshots to match unified plugin output (description format changes, new fields/enums)

Architecture (unchanged from v1)

┌─────────────────────────────────────┐
│      createUnifiedSearchPlugin      │
│   (iterates adapters, wires hooks)  │
├─────────────────────────────────────┤
│ tsvectorAdapter │ bm25Adapter       │
│ trgmAdapter     │ pgvectorAdapter   │
├─────────────────────────────────────┤
│           src/codecs/               │
│  tsvector-codec │ bm25-codec        │
│  vector-codec   │ operator-factories│
└─────────────────────────────────────┘

Tree-shakable imports

// Full preset (includes codecs + adapters + operator factories)
import { UnifiedSearchPreset } from 'graphile-search';

// Individual codecs (tree-shakable)
import { TsvectorCodecPlugin, Bm25CodecPlugin, VectorCodecPlugin } from 'graphile-search';

// Operator factories for connection filter integration
import { createMatchesOperatorFactory, createTrgmOperatorFactories } from 'graphile-search';

Generated GraphQL fields (unchanged from v1)

  • Filter inputs: {algorithm}{Column} (e.g. bm25Body, trgmTitle, vectorEmbedding)
  • Composite filter: fullTextSearch: String — fans out to all text-compatible adapters
  • Score fields: {column}{Algorithm}{Metric} (e.g. bodyBm25Score, titleTrgmSimilarity)
  • OrderBy enums: {COLUMN}_{ALGORITHM}_{METRIC}_ASC/DESC + SEARCH_SCORE_ASC/DESC
  • Composite searchScore: Normalized 0..1 aggregating all active search signals

Updates since last revision (consolidation + integration tests)

  1. Moved codec plugins internally — tsvector, bm25, pgvector codecs now live in graphile-search/src/codecs/ instead of separate packages
  2. Moved operator factoriescreateMatchesOperatorFactory and createTrgmOperatorFactories extracted into graphile-search/src/codecs/operator-factories.ts
  3. Deleted 4 packages — graphile-tsvector, graphile-bm25, graphile-trgm, graphile-pgvector removed entirely (68 files deleted, 6719 lines removed)
  4. Updated all imports — ConstructivePreset, graphile-settings plugins, test files now import from graphile-search
  5. Added mega query v2 — New test demonstrating fullTextSearch composite filter + searchScore composite ordering
  6. Tree-shakable exports — codec plugins, types, and operator factories can be imported individually from graphile-search
  7. CI cleanup — removed test jobs for deleted packages
  8. Snapshot updates — Fixed test snapshots for graphql/server-test and graphql/test to match unified plugin output (TRGM description format changes, new searchScore fields, new orderBy enums, fullTextSearch composite filter)
  9. Field naming fix — Renamed test references from fullTextTsvtsvTsv to match new adapter naming convention
  10. NEW: Search integration test — Added comprehensive end-to-end test suite (search.integration.test.ts) with SQL fixture (search-seed/) that validates:
    • tsvector full-text search (filter, score, orderBy)
    • pg_trgm fuzzy matching (filter, score, orderBy)
    • pgvector similarity search (filter, score, orderBy — conditional on extension availability)
    • Composite searchScore and fullTextSearch fields
    • Mega query v1 (per-algorithm filters + multi-column orderBy)
    • Mega query v2 (fullTextSearch composite + SEARCH_SCORE_DESC)
    • Schema introspection (validates all expected fields/filters/enums are present)

Review & Testing Checklist for Human

  • Verify no external consumers — confirm nothing outside the monorepo imports graphile-tsvector, graphile-bm25, graphile-trgm, or graphile-pgvector (breaking change with no deprecation)
  • Verify snapshot changes are correct — CI snapshots were manually edited to match expected output. Review the snapshot diffs carefully to ensure no unintended schema changes slipped through (especially TRGM descriptions, searchScore fields, new orderBy enums, fullTextSearch filter)
  • Verify BM25 module-level store fallback — the BM25 adapter relies on bm25IndexStore from ../codecs/bm25-codec (module-level shared state). If the codec plugin doesn't populate it during gather phase, BM25 filters will silently fail
  • Review search integration test — the new search.integration.test.ts creates a real PostgreSQL database, spins up an HTTP server, and executes GraphQL queries. Verify the SQL fixture and test queries accurately represent real-world usage
  • Run codegen — regenerate GraphQL types since field names changed ({column}{Algorithm}{Metric} naming convention)

Test Plan

  1. ✅ CI is green — 41/41 checks passing (includes all 21 unified search tests + 61 new search integration tests)
  2. ✅ Local pnpm build completed successfully with no type errors
  3. ✅ Search integration tests validate end-to-end behavior:
    • SQL fixture creates articles table with tsvector, text (trgm), and vector columns
    • Tests run against real PostgreSQL with extensions (pg_trgm, pgvector when available)
    • Validates all filter inputs, score fields, orderBy enums, composite fields
    • Tests both mega query v1 (per-algorithm) and v2 (fullTextSearch + searchScore)
  4. ⚠️ BM25 NOT tested in integration suite (no pg_textsearch extension in CI image) — manual testing recommended if BM25 is critical
  5. ⏳ Manual testing recommended:
    • Test fullTextSearch composite filter against multiple columns
    • Test searchScore composite ordering with various combinations
    • Verify null handling (vector/geom columns when filters don't apply)
    • Test with missing extensions to ensure graceful degradation
    • Verify the search fixture creates correct indexes (GIN on tsvector, GIN on trgm)

Notes

  • Breaking change: No deprecation period — the 4 individual packages are deleted entirely
  • Module-level state: BM25 adapter relies on module-level bm25IndexStore populated by codec plugin during gather phase
  • Cumulative diff: This PR builds on feat: add v5-native graphile-connection-filter plugin #797feat: v5-native filter system — complete rewrite with declarative API, full test coverage, and new plugins #813 (connection filter rewrite, plugin renames). The diff includes ~50 files changed, but only the graphile-search/ consolidation + integration tests are new to this PR
  • Snapshot testing: Snapshots were manually edited based on CI diff output rather than regenerated from a local DB. Extra scrutiny recommended during review.
  • Field naming convention: The tsvTsv stutter (algorithm prefix + column name collision) is intentional and unavoidable when DB columns are named after their types (e.g. a tsvector column named tsv)
  • Grafast behavior: Grafast (v5) silently ignores unknown input fields rather than returning validation errors (intentional v5 behavior)
  • Integration test coverage: The new search integration test validates tsvector, trgm, and pgvector (when available) but does NOT test BM25 (pg_textsearch extension not available in CI). BM25 is tested via the existing graphile-search unit tests only.
  • SQL fixture design: The search-seed fixture uses triggers to auto-populate tsvector columns from title+body. Verify the trigger fires correctly before queries execute.

⚠️ Note: This PR builds on top of PRs #797#813. The cumulative diff includes the entire PR chain (connection filter rewrite, plugin renames, etc.). The NEW code specific to this PR is the consolidation of the 4 packages into graphile-search/ + the search integration test suite.

Devin Session: https://app.devin.ai/sessions/cf88f3fd383b4421a5169ed01612899d
Requested by: @pyramation


Open with Devin

Implements a from-scratch PostGraphile v5 native connection filter plugin,
replacing the upstream postgraphile-plugin-connection-filter dependency.

New package: graphile/graphile-connection-filter/

Plugin architecture (7 plugins):
- ConnectionFilterInflectionPlugin: filter type naming conventions
- ConnectionFilterTypesPlugin: registers per-table and per-scalar filter types
- ConnectionFilterArgPlugin: injects filter arg on connections via applyPlan
- ConnectionFilterAttributesPlugin: adds per-column filter fields
- ConnectionFilterOperatorsPlugin: standard/sort/pattern/jsonb/inet/array/range operators
- ConnectionFilterCustomOperatorsPlugin: addConnectionFilterOperator API for satellite plugins
- ConnectionFilterLogicalOperatorsPlugin: and/or/not logical composition

Key features:
- Full v5 native: uses Grafast planning, PgCondition, codec system, behavior registry
- EXPORTABLE pattern for schema caching
- Preserves addConnectionFilterOperator API for PostGIS, search, pgvector, textsearch plugins
- No relation filter plugins (simplifies configuration vs upstream)
- Preset factory: ConnectionFilterPreset(options)

Also updates graphile-settings to use the new workspace package.
…Operator

filterType is for table-level filter types (UserFilter), while filterFieldType
is for scalar operator types (StringFilter). Satellite plugins pass scalar type
names, so the lookup must use filterFieldType to match the registration in
ConnectionFilterTypesPlugin. Previously worked by coincidence since both
inflections produce the same output, but would silently fail if a consumer
overrode one inflection but not the other.
Adds computed column filter support — allows filtering on PostgreSQL functions
that take a table row as their first argument and return a scalar.

Controlled by connectionFilterComputedColumns schema option. The preset factory
includes the plugin only when the option is truthy (default in preset: true,
but constructive-preset sets it to false).
- Remove phantom postgraphile-plugin-connection-filter dep from graphile-pgvector-plugin (never used)
- Remove phantom postgraphile-plugin-connection-filter dep from graphile-pg-textsearch-plugin (never used)
- Update graphile-plugin-connection-filter-postgis to use graphile-connection-filter workspace dep with typed imports
- Update graphile-search-plugin to use graphile-connection-filter workspace dep with typed imports
- Replace (build as any).addConnectionFilterOperator casts with properly typed build.addConnectionFilterOperator
…on-filter

- Update search plugin, pgvector, and postgis test files to import from
  graphile-connection-filter instead of postgraphile-plugin-connection-filter
- Use ConnectionFilterPreset() factory instead of PostGraphileConnectionFilterPreset
- Import ConnectionFilterOperatorSpec type from graphile-connection-filter
- Fix smart quote characters in filter descriptions to match existing snapshots
…ion filter tests

- Add graphile-connection-filter as devDependency in graphile-pgvector-plugin
  (test file imports ConnectionFilterPreset but package had no dependency)
- Skip connectionFilterRelations tests in search plugin (relation filters
  are intentionally not included in the v5-native plugin; they were disabled
  in production via disablePlugins with the old plugin)
…toggle

- ConnectionFilterForwardRelationsPlugin: filter by FK parent relations
- ConnectionFilterBackwardRelationsPlugin: filter by backward relations (one-to-one + one-to-many with some/every/none)
- connectionFilterRelations toggle in preset (default: false)
- Un-skip relation filter tests in search plugin
- Updated augmentations, types, and exports
… at runtime

The preset factory now always includes relation plugins in the plugin list.
Each plugin checks build.options.connectionFilterRelations at runtime and
early-returns if disabled. This allows the toggle to be set by any preset
in the chain, not just the ConnectionFilterPreset() call.
Enables relation filter fields in the production schema:
- Forward: filter by FK parent (e.g. clientByClientId on OrderFilter)
- Backward: filter by children with some/every/none
- Codegen will pick up the new filter fields automatically
- Search plugin: isPgCondition → isPgConnectionFilter scope
- BM25 plugin: isPgCondition → isPgConnectionFilter scope
- Disable PgConditionArgumentPlugin and PgConditionCustomFieldsPlugin in preset
- Update all tests from condition: {...} to filter: {...}
- Add graphile-connection-filter devDependency to BM25 plugin
- Update search plugin graceful degradation tests to use filter

BREAKING CHANGE: The condition argument has been removed entirely.
All filtering now uses the filter argument exclusively.
- Search plugin plugin.test.ts: condition → filter syntax, add ConnectionFilterPreset
- Server-test: condition → filter in query with equalTo operator
- Clear stale snapshots (schema-snapshot, introspection) for regeneration
- Search plugin: update snapshot keys to match renamed filter-based tests
- Schema snapshot: remove all condition arguments and XxxCondition input types
- Introspection snapshot: remove condition arg and UserCondition type
- Kept conditionType in _meta schema (unrelated to deprecated condition arg)
… behavior for pgCodecRelation, update schema snapshot with relation filter types
…y filter at applyPlan level

Top-level empty filter {} is now treated as 'no filter' (skipped) instead of
throwing an error. Nested empty objects in and/or/not and relation filters are
still rejected. This removes the need for the connectionFilterAllowEmptyObjectInput
workaround in pgvector tests.
- Extract shared getQueryBuilder utility into graphile-connection-filter/src/utils.ts
- Remove duplicate getQueryBuilder from search, BM25, and pgvector plugins
- Replace (build as any).dataplanPg with build.dataplanPg (already typed on Build)
- Replace (build as any).behavior with build.behavior (already typed on Build)
- Replace (build as any).input.pgRegistry with build.input.pgRegistry (already typed)
- Remove scope destructuring as any casts (pgCodec already typed on ScopeInputObject)
- Add pgCodec comment to augmentations.ts noting it's already declared by graphile-build-pg
- Export getQueryBuilder from graphile-connection-filter for satellite plugin use
Adds index safety check for relation filter fields. When enabled (default: true),
relation filter fields are only created for FKs with supporting indexes.
This prevents generating EXISTS subqueries that would cause sequential scans
on large tables.

Uses PgIndexBehaviorsPlugin's existing relation.extensions.isIndexed metadata
which is set at gather time. The check runs at schema build time with zero
runtime cost.

Applied to both forward and backward relation filter plugins.
Comprehensive test coverage using graphile-test infrastructure:
- Scalar operators: equalTo, notEqualTo, distinctFrom, isNull, in/notIn,
  lessThan, greaterThan, like, iLike, includes, startsWith, endsWith
- Logical operators: and, or, not, nested combinations
- Relation filters: forward (child->parent), backward one-to-one,
  backward one-to-many (some/every/none), exists fields
- Computed column filters
- Schema introspection: filter types, operator fields, relation fields
- Options toggles: connectionFilterRelations, connectionFilterComputedColumns,
  connectionFilterLogicalOperators, connectionFilterAllowedOperators,
  connectionFilterOperatorNames

Also adds graphile/graphile-connection-filter to CI matrix (41 jobs).
Exercises multiple plugins working together in a single test database:
- Connection filter (scalar operators, logical operators, relation filters)
- PostGIS spatial filters (geometry column)
- pgvector (vector column, search function, distance ordering)
- tsvector search plugin (fullText matches, rank, orderBy)
- BM25 search (pg_textsearch body index, score, orderBy)
- Kitchen sink queries combining multiple plugins

34 test cases across 8 describe blocks, all passing locally.
Added postgres-plus CI job for tests requiring PostGIS/pgvector/pg_textsearch.
… test

The mega query now exercises all SIX plugin types in a single filter:
- tsvector (fullTextTsv)
- BM25 (bm25Body)
- relation filter (category name)
- scalar filter (isActive)
- pgvector (vectorEmbedding nearby)
- PostGIS (geom intersects polygon bbox)

Also validates returned coordinates fall within the bounding box.
New package: graphile-pg-trgm-plugin — a PostGraphile v5 plugin for pg_trgm
trigram-based fuzzy text matching. Zero config, works on any text column.

Features:
- similarTo / wordSimilarTo filter operators on StringFilter
- trgm<Column> direct filter fields on connection filter types
- <column>Similarity computed score fields (0-1, null when inactive)
- SIMILARITY_<COLUMN>_ASC/DESC orderBy enum values
- TrgmSearchPreset for easy composition into presets
- connectionFilterTrgmRequireIndex option (default: false)
- 14 dedicated tests + integrated into mega query as 7th plugin type

Mega query now exercises ALL 7 plugin types in one GraphQL query:
tsvector + BM25 + pgvector + PostGIS + pg_trgm + relation filter + scalar
Updated introspection and SDL snapshots to include new fields from
TrgmSearchPlugin: similarTo/wordSimilarTo operators on StringFilter,
*Similarity computed fields, trgm* filter fields, and SIMILARITY_*
orderBy enum values.
- orderBy: [BM25_BODY_SCORE_ASC, SIMILARITY_NAME_DESC] demonstrates
  multi-signal relevance ranking in a single query
- Added comprehensive JSDoc explaining all 7 plugin types, the 2-phase
  meta system, and ORDER BY priority semantics
- Inline GraphQL comments explain each filter and score field
- Assertion verifies BM25 ASC ordering (primary sort)
- Documents important subtlety: ORDER BY priority follows schema field
  processing order, not the orderBy array order
- graphile-search-plugin → graphile-tsvector
- graphile-pg-textsearch-plugin → graphile-bm25
- graphile-pg-trgm-plugin → graphile-trgm
- graphile-pgvector-plugin → graphile-pgvector

Updated package.json names, all imports in graphile-settings,
CI workflow references, README docs, and source code comments.
pnpm build passes clean.
- GRAPHILE.md: add missing packages (graphile-bm25, graphile-trgm, graphile-connection-filter)
- bm25: rename conditionPrefix option to filterPrefix for consistency
- bm25 README: update examples from condition: to filter:
- tsvector README: update example from condition: to filter:
…uplication

- bm25: bm25BodyScore → bodyBm25Score, BM25_BODY_SCORE_ASC → BODY_BM25_SCORE_ASC
- tsvector: bodyRank → bodyTsvRank, BODY_RANK_ASC → BODY_TSV_RANK_ASC
- trgm: nameSimilarity → nameTrgmSimilarity, SIMILARITY_NAME_ASC → NAME_TRGM_SIMILARITY_ASC
- pgvector: embeddingDistance → embeddingVectorDistance, EMBEDDING_DISTANCE_ASC → EMBEDDING_VECTOR_DISTANCE_ASC
- Dedup: strip trailing algorithm from column names (e.g. fullTextTsv → fullTextTsvRank not fullTextTsvTsvRank)
- Add configurable filterPrefix option to trgm plugin
- Update all tests, READMEs, and source comments
…ing convention

- fullTextRank → fullTextTsvRank in filter.test.ts
- FULL_TEXT_RANK_ASC/DESC → FULL_TEXT_TSV_RANK_ASC/DESC in filter.test.ts
- nameSimilarity → nameTrgmSimilarity in schema-snapshot.test.ts.snap
- SIMILARITY_NAME → NAME_TRGM_SIMILARITY in schema-snapshot.test.ts.snap
New standalone package: graphile-search

Architecture:
- Single plugin iterates over SearchAdapter implementations
- 4 adapters: tsvector (ts_rank), bm25 (pg_textsearch), trgm (similarity), pgvector (distance)
- Composite searchScore field (normalized 0..1) aggregating all active search signals
- Per-algorithm score fields: {column}{Algorithm}{Metric}
- OrderBy enums: {COLUMN}_{ALGORITHM}_{METRIC}_ASC/DESC + SEARCH_SCORE
- Filter fields on connection filter input types

NOT added to ConstructivePreset yet — standalone package for testing/evaluation.
Old plugins remain completely untouched.
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

- Fix 'Must not call build.getTypeByName before init phase is complete' error
  by wrapping type registrations in try/catch instead of using getTypeByName guard
- Add TsvectorCodecPlugin to test preset (was missing, causing 'Could not build
  PgCodec for pg_catalog.tsvector' warning and tsvector adapter failing to detect columns)
- Add graphile-tsvector as devDependency for test imports
…site filter

- Rename tsvector adapter default filterPrefix from 'fullText' to 'tsv' for consistency
- Add supportsTextSearch property and buildTextSearchInput() method to SearchAdapter interface
- Set supportsTextSearch: true on tsvector, bm25, trgm adapters; false on pgvector
- Add enableFullTextSearch option (default: true) to UnifiedSearchOptions
- Implement fullTextSearch composite filter field in plugin.ts that fans out
  plain text queries to all text-compatible adapters with OR logic
- Update all tests: rename fullTextTsv -> tsvTsv, add fullTextSearch tests
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

Comment on lines +105 to +110
const fragment = resolve(sqlIdentifier, sqlValue, value, $where, {
fieldName: parentFieldName ?? null,
operatorName: fieldName,
});

$where.where(fragment);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 trgm operator resolve returns null which is passed unchecked to $where.where()

The similarTo and wordSimilarTo operators in graphile-trgm/src/preset.ts:38-41 and graphile-trgm/src/preset.ts:63-66 can return null when the input value is an empty string (e.g., { value: "" }). The TrgmSearchInput.value field is String! (non-null), but an empty string passes GraphQL validation. When resolve returns null, graphile-connection-filter/src/plugins/operatorApply.ts:110 calls $where.where(null) without checking for null, which will pass null to PgCondition.where() — a method that expects a SQL fragment. This can cause a runtime error or produce invalid SQL.

Trace through the code path
  1. User sends filter: { name: { similarTo: { value: "" } } }
  2. operatorApply.ts:45-89 validates the outer value object (non-null ✓)
  3. operatorApply.ts:105 calls resolve(sqlIdentifier, sqlValue, {value: "", threshold: undefined}, ...)
  4. preset.ts:40: !value evaluates !""true → returns null
  5. operatorApply.ts:110: $where.where(null) is called
Suggested change
const fragment = resolve(sqlIdentifier, sqlValue, value, $where, {
fieldName: parentFieldName ?? null,
operatorName: fieldName,
});
$where.where(fragment);
// Generate the WHERE clause fragment and apply it
const fragment = resolve(sqlIdentifier, sqlValue, value, $where, {
fieldName: parentFieldName ?? null,
operatorName: fieldName,
});
if (fragment != null) {
$where.where(fragment);
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +96 to +99
schema: {
connectionFilterOperatorFactories: [
createTrgmOperatorFactories(),
],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Duplicate connectionFilterOperatorFactories from sub-presets are silently lost when used standalone

Each sub-preset (PgSearchPreset, TrgmSearchPreset, GraphilePostgisPreset) defines its own connectionFilterOperatorFactories array. When these presets are composed via extends, graphile-config replaces arrays (not concatenates). In ConstructivePreset this is handled by explicitly collecting all factories at graphile/graphile-settings/src/presets/constructive-preset.ts:159-163. However, if a user composes the sub-presets directly (e.g., extends: [PgSearchPreset(), TrgmSearchPreset(), GraphilePostgisPreset]), only the last preset's factories survive — the earlier presets' matches and similarTo/wordSimilarTo operators would be silently dropped. The TrgmSearchPreset() preset at graphile/graphile-trgm/src/preset.ts:96-99 and PgSearchPreset() at graphile/graphile-tsvector/src/preset.ts:72-75 each declare their factories independently with no guidance that they'll be overwritten when combined.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

- Move codec plugins into graphile-search/src/codecs/ (tsvector, bm25, pgvector)
- Move operator factories (createMatchesOperatorFactory, createTrgmOperatorFactories)
- Update all imports to use internal codecs (no cross-package deps)
- Add tree-shakable exports from graphile-search index
- Update UnifiedSearchPreset to include codec plugins + operator factories
- Update ConstructivePreset to import from graphile-search
- Update graphile-settings plugins/index.ts re-exports
- Delete graphile-tsvector, graphile-bm25, graphile-trgm, graphile-pgvector
- Remove deleted packages from CI config
- Add mega query v2 (fullTextSearch + searchScore with composite ordering)
@devin-ai-integration devin-ai-integration bot changed the title feat: add unified graphile-search plugin (v1 with adapter pattern) feat: consolidate search packages into unified graphile-search plugin Mar 14, 2026
- Rename fullTextTsv -> tsvTsv in preset-integration tests (filter prefix changed)
- Delete stale snapshots for server-test and graphql/test (will auto-regenerate)
- Update trgm score descriptions to concise format
- Update trgm filter descriptions to concise format
- Update threshold description (remove verbose example text)
- Add searchScore field to Post, Tag, User, Comment types
- Add fullTextSearch filter to PostFilter, TagFilter, UserFilter, CommentFilter
- Add new orderBy enums (TITLE_TRGM, CONTENT_TRGM, EXCERPT_TRGM, etc.)
- Add SEARCH_SCORE_ASC/DESC orderBy enums
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant