Records are just tuples. You can specify table fields manually and then provide tuples in the proper order:
iex(1)> :mnesia.create_schema([node()])
iex(2)> :mnesia.start
iex(3)> :mnesia.create_table(Person, [attributes: [:id, :name, :age]])
iex(4)> :mnesia.dirty_write({Person, 1, "Alice", 30})
iex(5)> :mnesia.dirty_read({Person, 1})
[{Person, 1, "Alice", 30}]
To automatically convert from the struct to the tuple, you could rely on Map.keys and Map.values to fetch fields and values of the struct at runtime, something along the line of:
List.to_tuple([
person.__struct__ |
person |> Map.delete(:__struct__) |> Map.keys
])
When converting the opposite way, you could use :mnesia.table_info(Person, :attributes) to fetch the list of Mnesia table fields, and dynamically populate the map.
If you mean to query the table only using id field, than you could simplify your life by storing the complete map(struct) directly into second field, with the first field holding the id value. Then you don't need to convert anything and can simply wrap the struct in {Person, person.id, person} and store.