sequelize-typescript icon indicating copy to clipboard operation
sequelize-typescript copied to clipboard

createIndexDecorator has problems with "underscored" or "field" params

Open piotrmoszkowicz opened this issue 5 years ago • 10 comments

Versions

  • sequelize: 5.21.3
  • sequelize-typescript: 1.1.0
  • typescript 3.7.4

I'm submitting a ... [x] bug report [ ] feature request

Actual behavior: I've tried using new feature - createIndexDecorator, but it doesn't take into account the underscored table param (field param on the column doesn't work either) - therefor it errors out on database level, because there are no such fields existing.

Expected behavior: Successful creation of index.

Steps to reproduce: Code is below.

Related code:

const OwnedItemUniqueIndex = createIndexDecorator({type: "UNIQUE"});

@Table({
  tableName: "game_owned_items",
  underscored: true
})
export default class OwnedItem extends Model<OwnedItem> {
  @PrimaryKey
  @AutoIncrement
  @Column
  public id: number;

  @OwnedItemUniqueIndex
  @Column
  public ownerId: number;

  @OwnedItemUniqueIndex
  @Column
  public itemId: number;
}

piotrmoszkowicz avatar Jan 05 '20 05:01 piotrmoszkowicz

"Temporary fix"

const OwnedItemUniqueIndex = createIndexDecorator({ type: "UNIQUE" });

@Table({
  tableName: "game_owned_items",
  underscored: true
})
export default class OwnedItem extends Model<OwnedItem> {
  @PrimaryKey
  @AutoIncrement
  @Column
  public id: number;

  // @ts-ignore
  @OwnedItemUniqueIndex({ name: "owner_id"})
  @ForeignKey(() => Soldier)
  @Column
  public ownerId: number;

  // @ts-ignore
  @OwnedItemUniqueIndex({ name: "item_id" })
  @ForeignKey(() => Item)
  @Column
  public itemId: number;
}

Works with PostgreSQL.

piotrmoszkowicz avatar Jan 05 '20 05:01 piotrmoszkowicz

Hey @piotrmoszkowicz, thanks for reporting.

RobinBuschmann avatar Jan 08 '20 18:01 RobinBuschmann

I have a similar problem where:

@Table({
    underscored: true,
})
class Foo extends Model<Foo> {
    @Index
    @CreatedAt
    createdAt: Date;
}

raises:

SequelizeDatabaseError: column "createdAt" does not exist

My workaround is:

@Table({
    underscored: true,
})
class Foo extends Model<Foo> {
    @Index
    @CreatedAt
    createdAt: Date;

    @Index
    // eslint-disable-next-line @typescript-eslint/camelcase
    get created_at(): Date {
        return this.createdAt;
    }
}

I would love not to have to do this...

jessemyers avatar May 15 '20 00:05 jessemyers

EDIT: Don't use this over-complicated solution. Just use jessemyers solution here instead if you want a decorator that works. Or use my other solution if you don't like decorators for some reason.

~~I also have this issue, and as jessemyers have pointed out, the problem is directly related to the @Index decorator, not just the createIndexDecorator function.~~

~~My solution was to use two fields, one with the correct name for the database (database field) and one virtual field for the code (application field) with magic get/set functions.~~

my_field = database field
myField = application field
@Table({
  tableName: 'my_table',
  underscored: true,
})
export class MyModel extends Model<MyModel> {
  @PrimaryKey
  @Column
  id: string;

  @Index
  @Column
  my_field: string;

  @Column({
    type: DataType.VIRTUAL,
    set(myField: string) {
      this.setDataValue('my_field', myField);
    },
    get(): string {
      return this.getDataValue('my_field');
    },
  })
  myField: string;
}

~~This solution requires some extra code, but the application field will be fully working, just like a normal field. And the database field is persisted and named correctly in the database so when this bug is fixed you just need to remove this workaround code.~~

~~Just remember that, with this solution, when creating a new instance of this model, both the database and application fields will be available. Like myInstance.my_field and myInstance.myField.~~

ErikMartensson avatar May 15 '20 10:05 ErikMartensson

Same here. It's about @Index itself, not just others. I think it is quite critical issue for who has already existing schemas.

It seems to me considering to change sequelize-typescript to TypeORM..

rlaace423 avatar May 25 '20 06:05 rlaace423

Same here when i'm set underscored property is true (Cuz they missing underscore when create indexing) It is critical issues on me .. ;

webhacking avatar Jul 29 '20 08:07 webhacking

I've found another potential fix for this, by not using the @Index decorator at all but instead just specifying the indices in the @Table decorator instead, and manually specifying the field(s). Like this:

@Table({
  tableName: 'user',
  underscored: true,
  indexes: [
    {
      // Column name with underscores here
      fields: ['first_name'],
    },
  ],
})
export class User extends Model<User> {
  @PrimaryKey
  @Column
  id: string;

  @Column
  firstName: string;
});

ErikMartensson avatar Sep 03 '20 16:09 ErikMartensson

I've also been experimenting with a decorator wrapper:

import { snakeCase } from 'lodash';
import { annotateModelWithIndex } from 'sequelize-typescript';

function UnderscoredIndex<T>(target: T, key: string): void {
    annotateModelWithIndex(target, snakeCase(key));
}

Then replace @Index with @UnderscoredIndex.

Still not optimal.

jessemyers avatar Sep 03 '20 16:09 jessemyers

If anyone is interested, extended @jessemyers 's solution to a generic decorator, so we could use it with parameters:

declare type IndexDecoratorOptions = IndexOptions & Pick<IndexFieldOptions, Exclude<keyof IndexFieldOptions, 'name'>>;

type AnnotationFunction = <T>(
    target: T,
    propertyName: string,
) => void;

/**
 * Support @UnderscoredIndex('index_name')
 */
export function UnderscoredIndex(name: string): AnnotationFunction;

/**
 * Support @UnderscoredIndex({ name: 'index_name', unique: true })
 */
export function UnderscoredIndex(indexOptions: IndexOptions): AnnotationFunction;

/**
 * Support @UnderscoredIndex
 */
export function UnderscoredIndex<T>(
    target: T,
    propertyName: string,
    indexDecoratorOptions?: IndexDecoratorOptions,
): void;

/**
 * Overloaded decorator to support all variants of @UnderscoredIndex
 */
export function UnderscoredIndex<T>(...args: unknown[]): AnnotationFunction | void {
    if (arguments.length >= 2) {
        const type: T = <T>args[0];
        const key: string = <string>args[1];
        const indexDecoratorOptions: IndexDecoratorOptions = <IndexDecoratorOptions> args[2];
        annotateModelWithIndex(type, snakeCase(key), indexDecoratorOptions);
    } else {
        return <Type>(target: Type, propertyName: string) => {
            const indexDecoratorOptions: IndexDecoratorOptions = <IndexDecoratorOptions> args[0];
            annotateModelWithIndex(target, snakeCase(propertyName), indexDecoratorOptions);
        };
    }
}

ranhalprin avatar Feb 05 '21 20:02 ranhalprin

Bump

klausXR avatar Jun 23 '22 07:06 klausXR

Same issue duplicated at #1424

Coinhexa avatar Oct 14 '22 17:10 Coinhexa

Fixed as part of the merge of sequelize-typescript in sequelize https://github.com/sequelize/sequelize/pull/15482

ephys avatar Dec 19 '22 09:12 ephys

I'm still having this problem with sequalize-typescript - 2.1.5

sjtfl avatar Sep 22 '23 23:09 sjtfl

For those of us using @ranhalprin 's solution and using a subset of IndexFieldOptions in the decorator...

~~export function UnderscoredIndex(indexOptions: IndexOptions): AnnotationFunction;~~ export function UnderscoredIndex(indexOptions: IndexDecoratorOptions): AnnotationFunction;

argtoastar avatar Dec 14 '23 15:12 argtoastar

still having problem with this on 2.1.6

adilcpm avatar Jan 06 '24 14:01 adilcpm

This issue will not be fixed in sequelize-typescript, but it is fixed in @sequelize/core v7 which has TypeScript as a first class citizen and removes the need for sequelize-typescript. @sequelize/core v7 is still in alpha, but quite stable. Once the first full version is out we will start to deprecate sequelize-typescript

WikiRik avatar Jan 06 '24 15:01 WikiRik

very nice. looking forward to it. Any ETA?

adilcpm avatar Jan 06 '24 15:01 adilcpm