feat: deprecate condition argument, move all filtering to filter#801
Open
pyramation wants to merge 19 commits intomainfrom
Open
feat: deprecate condition argument, move all filtering to filter#801pyramation wants to merge 19 commits intomainfrom
pyramation wants to merge 19 commits intomainfrom
Conversation
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.
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:
|
- 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
… types to schema snapshot
…, and proper type ordering
…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.
This was referenced Mar 13, 2026
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: v5-native connection filter plugin + deprecate condition argument
Summary
This PR makes two major changes:
1. New
graphile-connection-filterpackage — A from-scratch PostGraphile v5-native connection filter plugin replacing the upstreampostgraphile-plugin-connection-filter@3.0.0-rc.1. Full TypeScript source, 7 core plugins + relation filter plugins, sameaddConnectionFilterOperatorAPI for satellite plugins (PostGIS, search, pgvector, textsearch). Includes toggleable relation filters (connectionFilterRelationsoption) and a computed column filter plugin.2. Condition deprecation (BREAKING) — Moves the search (tsvector) and BM25 (pg_textsearch) plugins from
isPgConditionscope toisPgConnectionFilterscope. DisablesPgConditionArgumentPluginandPgConditionCustomFieldsPluginin the constructive preset. All tests updated fromcondition: {...}tofilter: {...}syntax.The pgvector plugin was already on filter and served as the reference implementation proving the
getQueryBuilder()meta/ranking pattern works from filter scope.Updates since last revision
Removed
connectionFilterAllowEmptyObjectInputoption entirely — This was a band-aid option that toggled whether empty{}filter inputs were rejected. The root cause: GraphQL cannot distinguish "user didn't pass afilterargument" from "user passedfilter: {}" — both resolve as{}. Instead of making this configurable, the fix is:ConnectionFilterArgPlugin.ts):applyPlannow checksif (value == null || isEmpty(value)) return;— treats empty filter as "no filter" (no-op, no WHERE clause added){}insideand/or/not/relation fields, which is always a mistaketypes.ts,preset.ts,augmentations.ts,ConnectionFilterAttributesPlugin.ts, andutils.tsconnectionFilterAllowEmptyObjectInput: trueworkaround from the pgvector integration testPrevious fixes (CI now fully green, 40/40):
send-email-linkfunction: Migrated hardcodedcondition:GraphQL queries tofilter:syntaxfilterBybehavior registration forpgCodecRelationentitiesReview & Testing Checklist for Human
getQueryBuilder($condition)traversal to injectts_rank/BM25 scores into the SELECT list and store meta for orderBy/computed fields. This traversal was proven on pgvector but should be verified end-to-end for tsvector and BM25 by running the search plugin and BM25 tests locally.--updateSnapshot. Runpnpm test -- -uingraphql/server-testlocally to regenerate from scratch and confirm it matches what's committed.condition:references in the monorepo: The preset disables the condition argument entirely. Any GraphQL queries elsewhere in the codebase (app code, codegen files, other tests) that usecondition: { ... }will break at runtime. Search for remaining usages.{}no-op behavior is correct: The top-levelapplyPlannow returns early for empty objects instead of throwing. Confirm this doesn't silently swallow cases where the user explicitly passesfilter: {}and expects it to mean something. In nested contexts (and/or/not, relation filters, per-column filters), empty objects still throw — verify this is tested.= {} as anyscope destructuring pattern inplugin.ts(search) andbm25-search.ts: Both usescope: { isPgConnectionFilter, pgCodec } = {} as any— this silently falls through if the scope shape is unexpected. Confirm this matches how the filter plugin provides scope.allJobs(filter: { fullTextFullText: "apple" }) { nodes { id fullTextRank } orderBy: FULL_TEXT_RANK_DESC }allArticles(filter: { bm25Body: { query: "database", threshold: -0.5 } }) { nodes { id bm25BodyScore } orderBy: BM25_BODY_SCORE_ASC }Notes
graphile-connection-filterworkspace dependency.connectionFilterRelations: true), allowing filters likeallOrders(filter: { clientByClientId: { name: { startsWith: "Acme" } } }). These add many fields to the schema.filter: {}(or omittingfilterentirely) is now a no-op. Previously, withconnectionFilterAllowEmptyObjectInput: false, this would throw "Empty objects are forbidden". This is more sensible behavior — an empty filter shouldn't constrain results anyway.Session: https://app.devin.ai/sessions/cf88f3fd383b4421a5169ed01612899d
Requested by: @pyramation