Skip to content

feat: scope trgm operators to tables with intentional search (Option C)#841

Merged
pyramation merged 5 commits intomainfrom
devin/1773737091-trgm-scope-fix
Mar 17, 2026
Merged

feat: scope trgm operators to tables with intentional search (Option C)#841
pyramation merged 5 commits intomainfrom
devin/1773737091-trgm-scope-fix

Conversation

@pyramation
Copy link
Contributor

@pyramation pyramation commented Mar 17, 2026

Summary

Previously, createTrgmOperatorFactories() registered similarTo and wordSimilarTo on the global 'String' type, which meant these trgm operators appeared on every string field in every table — regardless of whether the table had any search infrastructure.

This PR implements per-table scoping (Option C from research) by introducing a StringTrgmFilter type variant:

  1. operator-factories.ts: Changed typeNames from 'String' to 'StringTrgm' so trgm operators register on StringTrgmFilter instead of the shared StringFilter.

  2. plugin.ts (init hook): Registers StringTrgmFilter as a new input object type with pgConnectionFilterOperators scope. The connection filter operators plugin auto-propagates all standard string operators (equalTo, startsWith, etc.) to it, plus the custom trgm operators get added via the factory.

  3. plugin.ts (GraphQLInputObjectType_fields hook): For tables where getAdapterColumns() returns trgm columns, swaps string attribute field types from StringFilterStringTrgmFilter. Non-qualifying tables keep plain StringFilter.

  4. plugin.ts (getAdapterColumns): Added @trgmSearch smart tag support — tables or columns tagged with @trgmSearch activate supplementary adapters even without tsvector/BM25 infrastructure.

Result: filter: { name: { similarTo: { value: "jon" } } } only appears on tables with intentional search (or @trgmSearch tag). The unified search plugin's per-table features (trgmName, fullTextSearch, score fields, orderBy) continue working as before.

Snapshot updates

Both test snapshot files were updated to reflect the schema change:

  • StringFilter no longer contains similarTo/wordSimilarTo operators
  • TrgmSearchInput type (and its dependency Float scalar in the introspection snapshot) are no longer registered in schemas without intentional search infrastructure

Review & Testing Checklist for Human

  • Verify similarTo/wordSimilarTo appear on qualifying tables: The test databases don't have tsvector/BM25 columns, so CI only validates the removal path. The positive path (operators appearing on a qualifying table's StringTrgmFilter) is untested by CI. Must be verified manually against a database with a tsvector column.
  • Verify field type identity comparison works at runtime: The swapping logic (field.type === StringFilterType) relies on GraphQL type object identity. If fieldWithHooks wraps or clones the type object, the comparison would silently fail and trgm operators would disappear from ALL tables (including qualifying ones). This needs a live test with a table that has tsvector + text columns.
  • Verify StringTrgmFilter gets the full set of standard operators: It's registered with pgCodecs: [textCodec] (just TYPES.text), while StringFilter has all string codecs (text, varchar, bpchar, name, char, citext). Confirm that ConnectionFilterOperatorsPlugin generates identical standard operators on both types.
  • Test the @trgmSearch smart tag path: Add COMMENT ON TABLE foo IS E'@trgmSearch'; on a table without tsvector/BM25 and verify trgm operators appear on its string fields.
  • Verify Object.assign({}, field, ...) shallow clone preserves Graphile field metadata: If fieldWithHooks attaches non-enumerable properties, the cloned field may lose important internal state (apply functions, scope data, etc.).

Recommended test plan: Start the GraphQL server against a database with at least one table that has a tsvector column and one that doesn't. Use GraphiQL to introspect both table filters and verify similarTo/wordSimilarTo appear only on the qualifying table's string fields.

Notes

  • Build has pre-existing TypeScript errors (connection filter types not resolved) — these are identical on main and not introduced by this PR.
  • The as any casts for smart tag checking and scope registration are consistent with existing patterns in this file, but worth noting.
  • CI is green (41/41 checks passing).

Link to Devin session: https://app.devin.ai/sessions/08765fef6135466bb5403f799e933fbe
Requested by: @pyramation

- Change createTrgmOperatorFactories() to target 'StringTrgm' type instead
  of global 'String', so trgm operators register on StringTrgmFilter rather
  than the shared StringFilter used by every string column everywhere.

- Register StringTrgmFilter type in unified search plugin init hook with
  pgConnectionFilterOperators scope so standard operators auto-propagate.

- In GraphQLInputObjectType_fields hook, swap StringFilter to StringTrgmFilter
  for string attribute fields on tables that qualify for trgm (via the existing
  supplementary adapter gating logic).

- Add @trgmSearch smart tag support: tables or columns tagged with @trgmSearch
  will activate supplementary adapters (including trgm) even without tsvector
  or BM25 intentional search infrastructure.
@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

@pyramation pyramation merged commit 7bcede2 into main Mar 17, 2026
43 checks passed
@pyramation pyramation deleted the devin/1773737091-trgm-scope-fix branch March 17, 2026 09:45
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