Support filtering interface/union types on _typename#1169
Support filtering interface/union types on _typename#1169marcdaniels-toast wants to merge 4 commits intoblock:mainfrom
Conversation
Enables filtering union/interface types by concrete subtype. Uses single underscore (`_typename`) since GraphQL spec prohibits `__` prefix on input fields. Also fixes CamelCaseConverter to preserve leading underscores via lookbehind assertion. This is step 1 for block#1024. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The `_typename` filter field added to abstract type filter inputs needed `name_in_index: "__typename"` so that FilterArgsTranslator maps it to the correct datastore field at query time. Also regenerates schema artifacts (runtime_metadata.yaml gets the name_in_index entry for each abstract FilterInput type) and adds a unit test verifying the translation in filters_spec.rb. Generated with Claude Code
Tests cover: - Union type embedded field (inventor): filter by Company or Person - Interface type embedded field (named_inventor): filter by subtype - Top-level abstract type query (distribution_channels): filter by OnlineStore - Sub-interface query (retailers): _typename filter combined with automatic __typename scoping Also removes the now-stale comment that said __typename filtering was not supported. Generated with Claude Code
Generated with Claude Code
I think this known limitation is a problem. I think we either need to get I think there's a solution, though--and it's one that should support more optimal queries to boot: let's leverage the That said, there's a situation that's tricky to solve. Imagine this: And imagine a query comes in like this: distributionChannels(filter: {
_typename: {equalToAnyOf: ["PhysicalStore", "BrokerWholesale"]}
}) {
# ...
}This query needs to hit both the Now that I've thought of using Also, I just realized something while writing that up--a client might try this: distributionChannels(filter: {
_typename: {equalToAnyOf: ["Wholesale"]}
}) {
# ...
}A client could try that expecting it to return all documents that are subtypes of
I want to hear what you favor but to share my two cents: I like the 2nd option a lot for the type safety it provides, but I think I ultimately lean towards the first option, for a few reasons:
Thoughts @marcdaniels-toast? |
|
Thanks for pushing back on the limitation. It did seem a bit awkward at the time but I didn't know we had such a clean solution as As for filtering by abstract types, what about query-time expansion? When the filter interpreter sees a |
Closes #1024
Finishes the work @myronmarston started in #1027.
Adds a
_typenamefilter field to all interface and union type filter inputs, allowing clients to filter by concrete subtype:{ distribution_channels(filter: { _typename: { equal_to_any_of: ["OnlineStore"] } }) { ... } }What this adds on top of #1027:
name_in_index: "__typename"on the filter field soFilterArgsTranslatormaps_typename→__typenamein the datastore query at runtimename_in_indexentry in runtime metadata for each abstractFilterInputtype_typename→__typenametranslation infilters_spec.rb_typenamefiltering on: top-level abstract type queries (distribution_channels,retailers) where multiple concrete types share an index, embedded union/interface fields (inventor,named_inventor), and aggregationsKnown limitation:
_typenameonly works reliably for types stored in a shared index (where the indexer injects__typename). Types with a dedicated index (e.g.PhysicalStoreinphysical_stores) don't have__typenamestored, soequal_to_any_of: ["PhysicalStore"]returns nothing andnot: { equal_to_any_of: [..., "PhysicalStore"] }fails to exclude them. This is inherent to how dedicated-index types work, not a bug introduced here. As a workaround,equal_to_any_of: [null]correctly matches dedicated-index documents, mirroring howAbstractTypeFilterhandles them internally.