-
-
Notifications
You must be signed in to change notification settings - Fork 495
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enumerable Arrays #476
Comments
Not natively supported, but should be possible via custom type: https://mikro-orm.io/docs/custom-types |
Here is an example of creating and retriving an array in postgres. note export class RoleType extends Type {
convertToDatabaseValue(value: any): string {
if (value.length) {
return `{${value.join(',')}}`;
}
throw ValidationError.invalidType(RoleType, value, 'JS');
}
toJSON(value: any) {
return value;
}
getColumnType() {
return 'text[]';
}
} |
### ArrayType In PostgreSQL and MongoDB, it uses native arrays, otherwise it concatenates the values into string separated by commas. This means that you can't use values that contain comma with the `ArrayType` (but you can create custom array type that will handle this case, e.g. by using different separator). By default array of strings is returned from the type. You can also have arrays of numbers or other data types - to do so, you will need to implement custom `hydrate` method that is used for converting the array values to the right type. ```typescript @Property({ type: new ArrayType(i => +i), nullable: true }) array?: number[]; ``` ### BlobType Blob type can be used to store binary data in the database. ```typescript @Property({ type: BlobType, nullable: true }) blob?: Buffer; ``` ### JsonType To store objects we can use `JsonType`. As some drivers are handling objects automatically and some don't, this type will handle the serialization in a driver independent way (calling `parse` and `stringify` only when needed). ```typescript @Property({ type: JsonType, nullable: true }) object?: { foo: string; bar: number }; ``` Related: #476
### ArrayType In PostgreSQL and MongoDB, it uses native arrays, otherwise it concatenates the values into string separated by commas. This means that you can't use values that contain comma with the `ArrayType` (but you can create custom array type that will handle this case, e.g. by using different separator). By default array of strings is returned from the type. You can also have arrays of numbers or other data types - to do so, you will need to implement custom `hydrate` method that is used for converting the array values to the right type. ```typescript @Property({ type: new ArrayType(i => +i), nullable: true }) array?: number[]; ``` ### BlobType Blob type can be used to store binary data in the database. ```typescript @Property({ type: BlobType, nullable: true }) blob?: Buffer; ``` ### JsonType To store objects we can use `JsonType`. As some drivers are handling objects automatically and some don't, this type will handle the serialization in a driver independent way (calling `parse` and `stringify` only when needed). ```typescript @Property({ type: JsonType, nullable: true }) object?: { foo: string; bar: number }; ``` Related: #476
### ArrayType In PostgreSQL and MongoDB, it uses native arrays, otherwise it concatenates the values into string separated by commas. This means that you can't use values that contain comma with the `ArrayType` (but you can create custom array type that will handle this case, e.g. by using different separator). By default array of strings is returned from the type. You can also have arrays of numbers or other data types - to do so, you will need to implement custom `hydrate` method that is used for converting the array values to the right type. ```typescript @Property({ type: new ArrayType(i => +i), nullable: true }) array?: number[]; ``` ### BlobType Blob type can be used to store binary data in the database. ```typescript @Property({ type: BlobType, nullable: true }) blob?: Buffer; ``` ### JsonType To store objects we can use `JsonType`. As some drivers are handling objects automatically and some don't, this type will handle the serialization in a driver independent way (calling `parse` and `stringify` only when needed). ```typescript @Property({ type: JsonType, nullable: true }) object?: { foo: string; bar: number }; ``` Related: #476
### ArrayType In PostgreSQL and MongoDB, it uses native arrays, otherwise it concatenates the values into string separated by commas. This means that you can't use values that contain comma with the `ArrayType` (but you can create custom array type that will handle this case, e.g. by using different separator). By default array of strings is returned from the type. You can also have arrays of numbers or other data types - to do so, you will need to implement custom `hydrate` method that is used for converting the array values to the right type. ```typescript @Property({ type: new ArrayType(i => +i), nullable: true }) array?: number[]; ``` ### BlobType Blob type can be used to store binary data in the database. ```typescript @Property({ type: BlobType, nullable: true }) blob?: Buffer; ``` ### JsonType To store objects we can use `JsonType`. As some drivers are handling objects automatically and some don't, this type will handle the serialization in a driver independent way (calling `parse` and `stringify` only when needed). ```typescript @Property({ type: JsonType, nullable: true }) object?: { foo: string; bar: number }; ``` Related: #476
### ArrayType In PostgreSQL and MongoDB, it uses native arrays, otherwise it concatenates the values into string separated by commas. This means that you can't use values that contain comma with the `ArrayType` (but you can create custom array type that will handle this case, e.g. by using different separator). By default array of strings is returned from the type. You can also have arrays of numbers or other data types - to do so, you will need to implement custom `hydrate` method that is used for converting the array values to the right type. ```typescript @Property({ type: new ArrayType(i => +i), nullable: true }) array?: number[]; ``` ### BlobType Blob type can be used to store binary data in the database. ```typescript @Property({ type: BlobType, nullable: true }) blob?: Buffer; ``` ### JsonType To store objects we can use `JsonType`. As some drivers are handling objects automatically and some don't, this type will handle the serialization in a driver independent way (calling `parse` and `stringify` only when needed). ```typescript @Property({ type: JsonType, nullable: true }) object?: { foo: string; bar: number }; ``` Related: #476
### ArrayType In PostgreSQL and MongoDB, it uses native arrays, otherwise it concatenates the values into string separated by commas. This means that you can't use values that contain comma with the `ArrayType` (but you can create custom array type that will handle this case, e.g. by using different separator). By default array of strings is returned from the type. You can also have arrays of numbers or other data types - to do so, you will need to implement custom `hydrate` method that is used for converting the array values to the right type. ```typescript @Property({ type: new ArrayType(i => +i), nullable: true }) array?: number[]; ``` ### BlobType Blob type can be used to store binary data in the database. ```typescript @Property({ type: BlobType, nullable: true }) blob?: Buffer; ``` ### JsonType To store objects we can use `JsonType`. As some drivers are handling objects automatically and some don't, this type will handle the serialization in a driver independent way (calling `parse` and `stringify` only when needed). ```typescript @Property({ type: JsonType, nullable: true }) object?: { foo: string; bar: number }; ``` Related: #476
### ArrayType In PostgreSQL and MongoDB, it uses native arrays, otherwise it concatenates the values into string separated by commas. This means that you can't use values that contain comma with the `ArrayType` (but you can create custom array type that will handle this case, e.g. by using different separator). By default array of strings is returned from the type. You can also have arrays of numbers or other data types - to do so, you will need to implement custom `hydrate` method that is used for converting the array values to the right type. ```typescript @Property({ type: new ArrayType(i => +i), nullable: true }) array?: number[]; ``` ### BlobType Blob type can be used to store binary data in the database. ```typescript @Property({ type: BlobType, nullable: true }) blob?: Buffer; ``` ### JsonType To store objects we can use `JsonType`. As some drivers are handling objects automatically and some don't, this type will handle the serialization in a driver independent way (calling `parse` and `stringify` only when needed). ```typescript @Property({ type: JsonType, nullable: true }) object?: { foo: string; bar: number }; ``` Related: #476
@B4nan I see you mentioned this issue in that commit ☝️ , is it possible to define a column as |
Not really tbh, I am not sure how it could work. Enums are implemented via text fields in postgres (that's how knex is working), same as arrays, but I don't see a way to have "enum like check constraints" for arrays. Using native postgres enums might help, but that would mean a lot of additional work, and still the pg array type is basically just a concatenated string (so we would still have no type safety on the array value afaict). That is the reason why I did not close this just yet, but thinking about it now, I really do not see a clear way to support it, so I might just close it now... :] You could implement your own custom type that would validate the items for you. We could have such type in the repository, prepared for anyone, that would work based on the enum metadata. |
I have had no complaints using the I prefer to keep validation on a layer above the ORM, so strict enum checking has not been necessary. I haven't checked, but is it possible to (or add) to define custom metadata in the property decorator that could be checked in a custom type, or maybe a validation callback? |
You could pass anything to the custom type via constructor and provide a configured instance of the type: @Property({ type: new MyEnumType(['a', 'b', 'c']) })
enumArray: ('a' | 'b' | 'c')[] |
v4.2 will allow this: enum Role {
User = 'user',
Admin = 'admin',
}
@Enum({ items: () => Role, array: true, default: [Role.User] })
roles: Role[] = [Role.User]; And the items will be checked on flush automatically to mimic the db constraints. |
First of all many thanks for this library. I am new to Typescript, Node.JS eco-system. Any help will be really appreciated. @B4nan : I just tried v4.2 allowed pattern to define role. and still facing error: DriverException: create table `user` (`id` int unsigned not null auto_increment primary key, `created_at` datetime not null, `updated_at` datetime not null, `roles` text not null default 'PUBLIC') default character set utf8mb4 engine = InnoDB; - BLOB, TEXT, GEOMETRY or JSON column 'roles' can't have a default value I am having MySQL DB and plain User.ts for defining entity. I am new to Typescript, can you please help me to resolve this error also I wanted to make roles column as varchar NOT text with defined length? command I used for DB are:
|
You might want to try https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html#data-type-defaults-explicit |
@B4nan For some reason, when I run migration with this, it always create a new migration file like export class Migration20231012023939 extends Migration {
async up(): Promise<void> {
this.addSql('alter table "admin" alter column "permissions" type text[] using ("permissions"::text[]);');
}
async down(): Promise<void> {
this.addSql('alter table "admin" alter column "permissions" type text[] using ("permissions"::text[]);');
}
} I add a bracket outside the enum and the migration command doesn't create a new file anymore
But it prevents me from making changes to permissions as it is incorrect type. |
This is indeed wrong, the callback is supposed to be returning the enum, not an array with the enum being its element. When it returns an array, it needs to return the items, e.g. I can't help you without seeing a complete repro, hard to guess what you are doing and what your setup is. Note that you can enable verbose logging to see why the schema comparator issues new migration - just add |
@B4nan thank for the answer, this is what I did export enum AdminPermission {
ROOT = 'ROOT', // the root admin, required to manage other admins
ACCESS = 'ACCESS', // give access to the dashboard for other admin
}
@Entity()
export class Admin extends Base {
@Enum({
type: 'enumArray',
items: () => AdminPermission,
columnType: 'text',
array: true,
default: [],
})
permissions: AdminPermission[];
} After I removed the These are the verbose logging if I add the [schema] 'enumItems' changed for column public.admin.permissions { column1: { name: 'permissions', type: 'text', mappedType: TextType {}, length: null, precision: null, scale: null, nullable: false, default: "'{}'", unsigned: false, autoincrement: false, comment: null, primary: false, unique: false, enumItems: [] }, column2: { name: 'permissions', type: 'text', mappedType: TextType {}, unsigned: false, autoincrement: false, primary: false, nullable: false, default: "'{}'", enumItems: [ 'ROOT', 'ACCESS' ] }}
[schema] column public.admin.permissions changed { changedProperties: Set(1) { 'enumItems' } }
[schema] 'enumItems' changed for column public.admin.permissions { column1: { name: 'permissions', type: 'text', mappedType: TextType {}, unsigned: false, autoincrement: false, primary: false, nullable: false, default: "'{}'", enumItems: [ 'ROOT', 'ACCESS' ] }, column2: { name: 'permissions', type: 'text', mappedType: TextType {}, length: null, precision: null, scale: null, nullable: false, default: "'{}'", unsigned: false, autoincrement: false, comment: null, primary: false, unique: false, enumItems: [] }}
[schema] column public.admin.permissions changed { changedProperties: Set(1) { 'enumItems' } } |
Works fine for me, I need to see a complete reproduction. edit: actually, drop the |
@B4nan oh yeah, I should probably drop it, the original problem exist before I added Sorry, that's all there is to it, so I don't even know what else to reproduce. I'll give some more information that I think could help. I'm using Postgres and Fastify. This is the migration command, I'm using this because the mikro-orm .bin bash file doesn't work well with my I tried to remove the async up(): Promise<void> {
this.addSql('alter table "admin" drop column "permissions";');
} Then readd it and run migration async up(): Promise<void> {
this.addSql('alter table "admin" add column "permissions" text[] not null default \'{}\';');
} then run migration again async up(): Promise<void> {
this.addSql('alter table "admin" alter column "permissions" type text[] using ("permissions"::text[]);');
} If this doesn't give you any hint, I'm probably gonna just remove the |
You gave me one property definition, I want you to give me full entity definition (simplified, but complete), ORM config, tsconfig.json, package.json, that is a complete reproduction - something I can just pull and run myself. All those things matter, right now I don't even know what version are you using.
What could give me some hint is the debug logs, the ones you provided before were generated with the wrong One thing that might confuse you here is the snapshotting, maybe try disabling that when you debug things. |
@B4nan I made a Codesandbox with the files you need here: https://codesandbox.io/p/sandbox/hardcore-allen-qc869r The loader.js I'm using to resolve the "src/modules/..." path since ts-node can't get it right. I'm running This is the verbose logging for the migrations with the correct
|
Thanks, will try to look into that soon. It's weird I wasn't able to reproduce this in the tests, I can definitely see the problem in the code. |
They are not enforced on schema level, so it does not make sense to compare the items. Related #476
@B4nan I upgraded to the latest version and everything works fine now. Thank you so much. There is one more question regarding the
|
I believe that is about you working with TS in "non-standarad" ways, as |
Hello, I am coming from TypeORM and quite enjoying this library so far, however I seem to have a bit of a snag around arrays of enumerables. This using PostgreSQL by the way.
Is your feature request related to a problem? Please describe.
A typical case with this for me is storing the static roles of users as string enumerables so it doesn't have to be converted in frontend code.
Describe the solution you'd like
I have tried a few variations of the following, however the schema generator does not seem to understand it. The
Role.Unverified
, as the desired default value, is seemingly referenced as a column in this instance.or
Describe alternatives you've considered
It also did not work with an enum of numbers, so I am assuming they are not supported currently.
The text was updated successfully, but these errors were encountered: