Syntax for an array of parameterized types in Ecto

I have a custom parametrized type called OneOf that allows parsing to one of other structs.

embedded_schema do
  field :my_field, OneOf, types: [SchemaA, SchemaB]
end

I would like to use it inside an array.

embedded_schema do
  field :list_of_fields, {:array, {:parametrized, {OneOf, types: [SchemaA, SchemaB]}
end

According to docs, embeds_many has a four-argument version embeds_many(name, schema, opts, list), but if I understand correctly, the last parameter is a do-block Ecto.Schema — Ecto v3.12.5

It seems that I should be able to do that with array:

@type composite() :: {:array, t()} | ...
@type t() :: primitive() | custom()
@type custom() :: module() | {:parameterized, {module(), term()}}

That is why, I’ve used field :list_of_fields, {:array, {:parametrized, {OneOf, types: [SchemaA, SchemaB]}

but I am getting

** (ArgumentError) invalid or unknown composite {:parameterized, {OneOf, [types: [SchemaA, SchemaB], discriminator: :name, mapping: %{"a" => SchemaA, "b" => SchemaB}]}} for field :list_of_fields. Did you mean to use :array or :map as first element of the tuple instead?

Do arrays support parametrized Ecto types?

:wave:

Could it maybe be

field :list_of_fields, {:array, OneOf}, types: [SchemaA, SchemaB]

I think something like this has worked for me before.

3 Likes

Yeah, the opts go on field, not the inner_type.

1 Like

We have this example for Ecto.Enum (which is a parameterized type):

Composite types, such as :array, are also supported which allow selecting multiple values per record:

field :roles, {:array, Ecto.Enum}, values: [:author, :editor, :admin]

(Ecto.Enum — Ecto v3.12.5)

but yes, we should add one to Ecto.ParameterizedType. PRs welcome!

2 Likes

Here it is!

1 Like