Skip to content
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

Add Index on JSON Field #1230

Closed
AzSiAz opened this issue Dec 22, 2020 · 9 comments
Closed

Add Index on JSON Field #1230

AzSiAz opened this issue Dec 22, 2020 · 9 comments
Labels
enhancement New feature or request
Milestone

Comments

@AzSiAz
Copy link

AzSiAz commented Dec 22, 2020

Is your feature request related to a problem? Please describe.
You can't add an index on json field like you would do in sql

CREATE INDEX "test_on_json_index" ON test((data->>'somefield'), "userId");

Describe the solution you'd like
A small update to @Index() to be able to do something like this

import { BaseEntity, Entity, Index, JsonType, PrimaryKey, Property } from '@mikro-orm/core'

@Index({ properties: ["data->>'somefield'", 'userId'] })
@Entity()
export class Test extends BaseEntity<Test, 'id'> {
    @PrimaryKey({ columnType: 'uuid', defaultRaw: 'uuid_generate_v4()' })
    id!: string

    @Property({ columnType: 'timestamptz', defaultRaw: 'CURRENT_TIMESTAMP' })
    createdAt = new Date()

    @Property({ columnType: 'timestamptz', defaultRaw: 'CURRENT_TIMESTAMP', onUpdate: () => new Date() })
    updatedAt = new Date()

    // Or here
    @Index("data->>'somefield'")
    @Property({ type: JsonType })
    data: PossibleData

    @Property()
    @Index()
    userId: UserId
}

Or something like this

import { BaseEntity, Entity, Index, JsonType, PrimaryKey, Property } from '@mikro-orm/core'

@Index({ properties: [{data: { somefield: true }}, 'userId'] })
@Entity()
export class Test extends BaseEntity<Test, 'id'> {
    @PrimaryKey({ columnType: 'uuid', defaultRaw: 'uuid_generate_v4()' })
    id!: string

    @Property({ columnType: 'timestamptz', defaultRaw: 'CURRENT_TIMESTAMP' })
    createdAt = new Date()

    @Property({ columnType: 'timestamptz', defaultRaw: 'CURRENT_TIMESTAMP', onUpdate: () => new Date() })
    updatedAt = new Date()

     // Or here
    @Index({ properties: {data: { somefield: true }} })
    @Property({ type: JsonType })
    data: PossibleData

    @Property()
    @Index()
    userId: UserId
}

Which when you try to create a migration return
MetadataError: Entity Test has wrong index definition: 'data->>'somefield'' does not exist. You need to use property name, not column name.

Describe alternatives you've considered
Manual migration but mikro-orm try to delete my index with every new migration created, but maybe I missed something

@AzSiAz AzSiAz added the enhancement New feature or request label Dec 22, 2020
@B4nan
Copy link
Member

B4nan commented Jan 15, 2021

Manual migration but mikro-orm try to delete my index with every new migration created, but maybe I missed something

A workaround is to define any index with some name, index diffing works just based on index names, so it is enough to have some index with the name and it will be kept intact.

@B4nan
Copy link
Member

B4nan commented Nov 28, 2021

In v5 you can use custom index expressions:

@Index({ name: 'test_on_json_index', expression: `CREATE INDEX "test_on_json_index" ON test((data->>'somefield'), "userId");` })

This works both on entity level and property level and you have absolute control over the index definition.

The diffing works again based on index name (that can be either explicitly defined or inferred from the property name). The previous workaround probably won't work anymore as indexes are now properly diffed not just based on name, but also based on fields and their order.

I still see a room for the proposed solution, probably with dot notation instead of objects or platform specific syntax, like in #2129:

@Index({ properties: ['data.somefield', 'userId'] })
@Entity()
export class Test extends BaseEntity<Test, 'id'> {

    @Index({ properties: 'data.somefield' }) // can be a string too
    @Property({ type: JsonType })
    data: PossibleData

}

@AzSiAz
Copy link
Author

AzSiAz commented Nov 28, 2021

Didn't know about the upcoming v5 change, thanks for the heads up

The idea with object syntax was to provide autocompletion and more importantly TS type error if something change in the future

@B4nan
Copy link
Member

B4nan commented Nov 28, 2021

The idea with object syntax was to provide autocompletion and more importantly TS type error if something change in the future

v5 supports type safe dot notation paths, but in general you can't have anything type safe inside decorators unless you explicitly provide the generic argument (it can't be inferred from the class where the decorator is used)

@AzSiAz
Copy link
Author

AzSiAz commented Nov 28, 2021

Yeah, I was speaking more with using something like

@Index({ properties: { data: { field: true | 'asc' | 'desc' } } } as IndexType<JSONType>)

But if dot notation in paths is typesafe I'm all in 💯

My only downside from using mikro-orm v4 despite all the amazing work you and contributor put in is type safe usage compared to prisma (still lacking micro-orm flexibility)

@SteveMelons
Copy link

What would be the current workaround in v5 for nested Index using the mongo driver?

@SteveMelons
Copy link

What would be the current workaround in v5 for nested Index using the mongo driver?

Found the solution which does not allow for extra options to be passed as far as I understand though

@Index({ options: { "address.location": "2dsphere" } })

@SevenWaysDP
Copy link
Contributor

What would be the current workaround in v5 for nested Index using the mongo driver?

Found the solution which does not allow for extra options to be passed as far as I understand though

@Index({ options: { "address.location": "2dsphere" } })

It works, on entity level! Thanks!

@B4nan B4nan added this to the 6.0 milestone Dec 28, 2022
B4nan added a commit that referenced this issue Sep 23, 2023
To create an index on a JSON property, use an entity-level `@Index()` decorator with a dot path:

```ts
@entity()
@Index({ properties: 'metaData.foo' })
@Index({ properties: ['metaData.foo', 'metaData.bar'] }) // compound index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In PostgreSQL, this will generate a query like the following:

```sql
create index "book_meta_data_foo_index" on "book" (("meta_data"->>'foo'));
```

To create a unique index, use the `@Unique()` decorator:

```ts
@entity()
@unique({ properties: 'metaData.foo' })
@unique({ properties: ['metaData.foo', 'metaData.bar'] }) // compound unique index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In MySQL, you can also set the type explicitly:

```ts
@entity()
@Index({ properties: 'metaData.foo', options: { returning: 'char(200)' } })
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

This will generate a query like the following:

```sql
alter table `book`
  add index `book_meta_data_foo_index`((json_value(`meta_data`, '$.foo' returning char(200))));
```

> MariaDB driver does not support this feature.

Closes #1230
B4nan added a commit that referenced this issue Sep 23, 2023
To create an index on a JSON property, use an entity-level `@Index()` decorator with a dot path:

```ts
@entity()
@Index({ properties: 'metaData.foo' })
@Index({ properties: ['metaData.foo', 'metaData.bar'] }) // compound index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In PostgreSQL, this will generate a query like the following:

```sql
create index "book_meta_data_foo_index" on "book" (("meta_data"->>'foo'));
```

To create a unique index, use the `@Unique()` decorator:

```ts
@entity()
@unique({ properties: 'metaData.foo' })
@unique({ properties: ['metaData.foo', 'metaData.bar'] }) // compound unique index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In MySQL, you can also set the type explicitly:

```ts
@entity()
@Index({ properties: 'metaData.foo', options: { returning: 'char(200)' } })
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

This will generate a query like the following:

```sql
alter table `book`
  add index `book_meta_data_foo_index`((json_value(`meta_data`, '$.foo' returning char(200))));
```

> MariaDB driver does not support this feature.

Closes #1230
B4nan added a commit that referenced this issue Sep 23, 2023
To create an index on a JSON property, use an entity-level `@Index()` decorator with a dot path:

```ts
@entity()
@Index({ properties: 'metaData.foo' })
@Index({ properties: ['metaData.foo', 'metaData.bar'] }) // compound index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In PostgreSQL, this will generate a query like the following:

```sql
create index "book_meta_data_foo_index" on "book" (("meta_data"->>'foo'));
```

To create a unique index, use the `@Unique()` decorator:

```ts
@entity()
@unique({ properties: 'metaData.foo' })
@unique({ properties: ['metaData.foo', 'metaData.bar'] }) // compound unique index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In MySQL, you can also set the type explicitly:

```ts
@entity()
@Index({ properties: 'metaData.foo', options: { returning: 'char(200)' } })
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

This will generate a query like the following:

```sql
alter table `book`
  add index `book_meta_data_foo_index`((json_value(`meta_data`, '$.foo' returning char(200))));
```

> MariaDB driver does not support this feature.

Closes #1230
B4nan added a commit that referenced this issue Sep 23, 2023
To create an index on a JSON property, use an entity-level `@Index()`
decorator with a dot path:

```ts
@entity()
@Index({ properties: 'metaData.foo' })
@Index({ properties: ['metaData.foo', 'metaData.bar'] }) // compound index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In PostgreSQL, this will generate a query like the following:

```sql
create index "book_meta_data_foo_index" on "book" (("meta_data"->>'foo'));
```

To create a unique index, use the `@Unique()` decorator:

```ts
@entity()
@unique({ properties: 'metaData.foo' })
@unique({ properties: ['metaData.foo', 'metaData.bar'] }) // compound unique index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In MySQL, you can also set the type explicitly:

```ts
@entity()
@Index({ properties: 'metaData.foo', options: { returning: 'char(200)' } })
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

This will generate a query like the following:

```sql
alter table `book`
  add index `book_meta_data_foo_index`((json_value(`meta_data`, '$.foo' returning char(200))));
```

> MariaDB driver does not support this feature.

Closes #1230
@B4nan
Copy link
Member

B4nan commented Sep 23, 2023

Closing as implemented in v6 via #4735

@B4nan B4nan closed this as completed Sep 23, 2023
B4nan added a commit that referenced this issue Sep 24, 2023
To create an index on a JSON property, use an entity-level `@Index()`
decorator with a dot path:

```ts
@entity()
@Index({ properties: 'metaData.foo' })
@Index({ properties: ['metaData.foo', 'metaData.bar'] }) // compound index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In PostgreSQL, this will generate a query like the following:

```sql
create index "book_meta_data_foo_index" on "book" (("meta_data"->>'foo'));
```

To create a unique index, use the `@Unique()` decorator:

```ts
@entity()
@unique({ properties: 'metaData.foo' })
@unique({ properties: ['metaData.foo', 'metaData.bar'] }) // compound unique index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In MySQL, you can also set the type explicitly:

```ts
@entity()
@Index({ properties: 'metaData.foo', options: { returning: 'char(200)' } })
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

This will generate a query like the following:

```sql
alter table `book`
  add index `book_meta_data_foo_index`((json_value(`meta_data`, '$.foo' returning char(200))));
```

> MariaDB driver does not support this feature.

Closes #1230
B4nan added a commit that referenced this issue Sep 30, 2023
To create an index on a JSON property, use an entity-level `@Index()`
decorator with a dot path:

```ts
@entity()
@Index({ properties: 'metaData.foo' })
@Index({ properties: ['metaData.foo', 'metaData.bar'] }) // compound index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In PostgreSQL, this will generate a query like the following:

```sql
create index "book_meta_data_foo_index" on "book" (("meta_data"->>'foo'));
```

To create a unique index, use the `@Unique()` decorator:

```ts
@entity()
@unique({ properties: 'metaData.foo' })
@unique({ properties: ['metaData.foo', 'metaData.bar'] }) // compound unique index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In MySQL, you can also set the type explicitly:

```ts
@entity()
@Index({ properties: 'metaData.foo', options: { returning: 'char(200)' } })
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

This will generate a query like the following:

```sql
alter table `book`
  add index `book_meta_data_foo_index`((json_value(`meta_data`, '$.foo' returning char(200))));
```

> MariaDB driver does not support this feature.

Closes #1230
B4nan added a commit that referenced this issue Oct 2, 2023
To create an index on a JSON property, use an entity-level `@Index()`
decorator with a dot path:

```ts
@entity()
@Index({ properties: 'metaData.foo' })
@Index({ properties: ['metaData.foo', 'metaData.bar'] }) // compound index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In PostgreSQL, this will generate a query like the following:

```sql
create index "book_meta_data_foo_index" on "book" (("meta_data"->>'foo'));
```

To create a unique index, use the `@Unique()` decorator:

```ts
@entity()
@unique({ properties: 'metaData.foo' })
@unique({ properties: ['metaData.foo', 'metaData.bar'] }) // compound unique index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In MySQL, you can also set the type explicitly:

```ts
@entity()
@Index({ properties: 'metaData.foo', options: { returning: 'char(200)' } })
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

This will generate a query like the following:

```sql
alter table `book`
  add index `book_meta_data_foo_index`((json_value(`meta_data`, '$.foo' returning char(200))));
```

> MariaDB driver does not support this feature.

Closes #1230
B4nan added a commit that referenced this issue Oct 17, 2023
To create an index on a JSON property, use an entity-level `@Index()`
decorator with a dot path:

```ts
@entity()
@Index({ properties: 'metaData.foo' })
@Index({ properties: ['metaData.foo', 'metaData.bar'] }) // compound index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In PostgreSQL, this will generate a query like the following:

```sql
create index "book_meta_data_foo_index" on "book" (("meta_data"->>'foo'));
```

To create a unique index, use the `@Unique()` decorator:

```ts
@entity()
@unique({ properties: 'metaData.foo' })
@unique({ properties: ['metaData.foo', 'metaData.bar'] }) // compound unique index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In MySQL, you can also set the type explicitly:

```ts
@entity()
@Index({ properties: 'metaData.foo', options: { returning: 'char(200)' } })
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

This will generate a query like the following:

```sql
alter table `book`
  add index `book_meta_data_foo_index`((json_value(`meta_data`, '$.foo' returning char(200))));
```

> MariaDB driver does not support this feature.

Closes #1230
B4nan added a commit that referenced this issue Oct 21, 2023
To create an index on a JSON property, use an entity-level `@Index()`
decorator with a dot path:

```ts
@entity()
@Index({ properties: 'metaData.foo' })
@Index({ properties: ['metaData.foo', 'metaData.bar'] }) // compound index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In PostgreSQL, this will generate a query like the following:

```sql
create index "book_meta_data_foo_index" on "book" (("meta_data"->>'foo'));
```

To create a unique index, use the `@Unique()` decorator:

```ts
@entity()
@unique({ properties: 'metaData.foo' })
@unique({ properties: ['metaData.foo', 'metaData.bar'] }) // compound unique index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In MySQL, you can also set the type explicitly:

```ts
@entity()
@Index({ properties: 'metaData.foo', options: { returning: 'char(200)' } })
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

This will generate a query like the following:

```sql
alter table `book`
  add index `book_meta_data_foo_index`((json_value(`meta_data`, '$.foo' returning char(200))));
```

> MariaDB driver does not support this feature.

Closes #1230
B4nan added a commit that referenced this issue Oct 25, 2023
To create an index on a JSON property, use an entity-level `@Index()`
decorator with a dot path:

```ts
@entity()
@Index({ properties: 'metaData.foo' })
@Index({ properties: ['metaData.foo', 'metaData.bar'] }) // compound index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In PostgreSQL, this will generate a query like the following:

```sql
create index "book_meta_data_foo_index" on "book" (("meta_data"->>'foo'));
```

To create a unique index, use the `@Unique()` decorator:

```ts
@entity()
@unique({ properties: 'metaData.foo' })
@unique({ properties: ['metaData.foo', 'metaData.bar'] }) // compound unique index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In MySQL, you can also set the type explicitly:

```ts
@entity()
@Index({ properties: 'metaData.foo', options: { returning: 'char(200)' } })
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

This will generate a query like the following:

```sql
alter table `book`
  add index `book_meta_data_foo_index`((json_value(`meta_data`, '$.foo' returning char(200))));
```

> MariaDB driver does not support this feature.

Closes #1230
B4nan added a commit that referenced this issue Nov 2, 2023
To create an index on a JSON property, use an entity-level `@Index()`
decorator with a dot path:

```ts
@entity()
@Index({ properties: 'metaData.foo' })
@Index({ properties: ['metaData.foo', 'metaData.bar'] }) // compound index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In PostgreSQL, this will generate a query like the following:

```sql
create index "book_meta_data_foo_index" on "book" (("meta_data"->>'foo'));
```

To create a unique index, use the `@Unique()` decorator:

```ts
@entity()
@unique({ properties: 'metaData.foo' })
@unique({ properties: ['metaData.foo', 'metaData.bar'] }) // compound unique index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In MySQL, you can also set the type explicitly:

```ts
@entity()
@Index({ properties: 'metaData.foo', options: { returning: 'char(200)' } })
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

This will generate a query like the following:

```sql
alter table `book`
  add index `book_meta_data_foo_index`((json_value(`meta_data`, '$.foo' returning char(200))));
```

> MariaDB driver does not support this feature.

Closes #1230
B4nan added a commit that referenced this issue Nov 5, 2023
To create an index on a JSON property, use an entity-level `@Index()`
decorator with a dot path:

```ts
@entity()
@Index({ properties: 'metaData.foo' })
@Index({ properties: ['metaData.foo', 'metaData.bar'] }) // compound index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In PostgreSQL, this will generate a query like the following:

```sql
create index "book_meta_data_foo_index" on "book" (("meta_data"->>'foo'));
```

To create a unique index, use the `@Unique()` decorator:

```ts
@entity()
@unique({ properties: 'metaData.foo' })
@unique({ properties: ['metaData.foo', 'metaData.bar'] }) // compound unique index
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

In MySQL, you can also set the type explicitly:

```ts
@entity()
@Index({ properties: 'metaData.foo', options: { returning: 'char(200)' } })
export class Book {

  @Property({ type: 'json', nullable: true })
  metaData?: { foo: string; bar: number };

}
```

This will generate a query like the following:

```sql
alter table `book`
  add index `book_meta_data_foo_index`((json_value(`meta_data`, '$.foo' returning char(200))));
```

> MariaDB driver does not support this feature.

Closes #1230
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants