Skip to content

Feat/add manifest filtering and sorting#2704

Merged
zachdaniel merged 2 commits into
mainfrom
feat/add-manifest-filtering-and-sorting
May 17, 2026
Merged

Feat/add manifest filtering and sorting#2704
zachdaniel merged 2 commits into
mainfrom
feat/add-manifest-filtering-and-sorting

Conversation

@Torkan
Copy link
Copy Markdown
Contributor

@Torkan Torkan commented May 17, 2026

Summary

Extends Ash.Info.Manifest with the filter/sort vocabulary needed by code-gen consumers (e.g. ash_graphql, ash_typescript) so they no longer have to re-derive it from raw Ash internals.

Top-level catalog (%FilterCapabilities{}, %SortCapabilities{}):

  • Every builtin operator, function, custom expression, and data-layer-contributed function, each with normalized argument signatures and predicate flag
  • Sort directions

Per-field resolution on every %Field{}:

  • filter_operators, filter_functions, filter_custom_expressions — lists of %Applicable*{} records pairing each applicable name with its resolved rhs type (:same, :any, {:concrete, t}, {:array, t})
  • NewType subtype fallthrough; ordered-comparison operators (<, >, <=, >=) trimmed for arrays and booleans

Per-relationship: filterable?/sortable? booleans replaced with filterable_via (:traversal | :exists | false) and sortable_via (:traversal | false) tagged atoms reflecting cardinality — one-cardinality relationships can be dot-traversed, many-cardinality need an existential quantifier. Fields keep their booleans (no cardinality distinction applies).

Aggregate fix: %Field{kind: :aggregate}.allow_nil? now derives from aggregate kind (:count/:exists/:list → false; :first/:max/:min/:sum/:avg → true) instead of the unrelated :include_nil? input flag.

Contributor checklist

Leave anything that you believe does not apply unchecked.

  • I accept the AI Policy, or AI was not used in the creation of this PR.
  • Features include unit/acceptance tests


# Heuristic: lowercase, non-Elixir atom is a builtin (`:string`, `:integer`, ...).
# Modules start with uppercase (`Ash.Type.Integer`).
defp builtin_type?(atom) do
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not a good heuristic, as people can add their own custom types. We should always get types from Ash.Type.get_type to turn any value into its proper type module. If you need to know if it's a builtin there is a function IIRC called Ash.Type.builtin?/1


defp concrete_ref(ref) do
case Atom.to_string(ref) do
"Elixir." <> _ -> module_to_string(ref)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You can inspect(ref) if it's an atom? Or if its a module?

…esolution

Adds a global operator/function/custom-expression catalog plus per-field
Applicable* lists with resolved rhs types. Relationships keep
filterable?/sortable? booleans; consumers derive filter/sort shape from
cardinality. Aggregate allow_nil? now derives from kind.
@Torkan Torkan force-pushed the feat/add-manifest-filtering-and-sorting branch from 823b361 to b1236d5 Compare May 17, 2026 12:37
@zachdaniel zachdaniel merged commit ae55d14 into main May 17, 2026
43 of 49 checks passed
@zachdaniel
Copy link
Copy Markdown
Contributor

🚀 Thank you for your contribution! 🚀

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.

2 participants