fix(database): index invalid (#564)
* fix(database): index invalid * fix: test error
This commit is contained in:
		
							parent
							
								
									41cb3da448
								
							
						
					
					
						commit
						70ab4dcf1f
					
				| @ -13,6 +13,22 @@ describe('collection', () => { | |||||||
|     await db.close(); |     await db.close(); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |   test.skip('indexes', async () => { | ||||||
|  |     await db.clean({ drop: true }); | ||||||
|  |     const collection = db.collection({ | ||||||
|  |       name: 'test', | ||||||
|  |       fields: [ | ||||||
|  |         { | ||||||
|  |           type: 'string', | ||||||
|  |           name: 'name', | ||||||
|  |           index: true, | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |     }); | ||||||
|  |     collection.removeField('name'); | ||||||
|  |     await db.sync(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|   test('removeFromDb', async () => { |   test('removeFromDb', async () => { | ||||||
|     await db.clean({ drop: true }); |     await db.clean({ drop: true }); | ||||||
|     const collection = db.collection({ |     const collection = db.collection({ | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import { Collection } from '../collection'; | import { Collection } from '../collection'; | ||||||
| import { Database } from '../database'; | import { Database } from '../database'; | ||||||
| import { updateAssociation, updateAssociations } from '../update-associations'; | import { updateAssociations } from '../update-associations'; | ||||||
| import { mockDatabase } from './'; | import { mockDatabase } from './'; | ||||||
| 
 | 
 | ||||||
| describe('update associations', () => { | describe('update associations', () => { | ||||||
| @ -356,13 +356,14 @@ describe('update associations', () => { | |||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   describe('belongsToMany', () => { |   describe('belongsToMany', () => { | ||||||
|     let db; |     let db: Database; | ||||||
|     let Post; |     let Post: Collection; | ||||||
|     let Tag; |     let Tag: Collection; | ||||||
|     let PostTag; |     let PostTag: Collection; | ||||||
| 
 | 
 | ||||||
|     beforeEach(async () => { |     beforeEach(async () => { | ||||||
|       db = mockDatabase(); |       db = mockDatabase(); | ||||||
|  |       await db.clean({ drop: true }); | ||||||
|       PostTag = db.collection({ |       PostTag = db.collection({ | ||||||
|         name: 'posts_tags', |         name: 'posts_tags', | ||||||
|         fields: [{ type: 'string', name: 'tagged_at' }], |         fields: [{ type: 'string', name: 'tagged_at' }], | ||||||
| @ -389,7 +390,7 @@ describe('update associations', () => { | |||||||
|     afterEach(async () => { |     afterEach(async () => { | ||||||
|       await db.close(); |       await db.close(); | ||||||
|     }); |     }); | ||||||
|     test('set through value', async () => { |     test.only('set through value', async () => { | ||||||
|       const p1 = await Post.repository.create({ |       const p1 = await Post.repository.create({ | ||||||
|         values: { |         values: { | ||||||
|           title: 'hello', |           title: 'hello', | ||||||
| @ -404,9 +405,12 @@ describe('update associations', () => { | |||||||
|           ], |           ], | ||||||
|         }, |         }, | ||||||
|       }); |       }); | ||||||
| 
 |       const count = await PostTag.repository.count({ | ||||||
|       const t1 = (await p1.getTags())[0]; |         filter: { | ||||||
|       expect(t1.posts_tags.tagged_at).toEqual('123'); |           tagged_at: '123', | ||||||
|  |         }, | ||||||
|  |       }); | ||||||
|  |       expect(count).toEqual(1); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|  | |||||||
| @ -1,7 +1,14 @@ | |||||||
| import merge from 'deepmerge'; | import merge from 'deepmerge'; | ||||||
| import { EventEmitter } from 'events'; | import { EventEmitter } from 'events'; | ||||||
| import { default as lodash, default as _ } from 'lodash'; | import { default as lodash, default as _ } from 'lodash'; | ||||||
| import { ModelCtor, ModelOptions, QueryInterfaceDropTableOptions, SyncOptions, Transactionable } from 'sequelize'; | import { | ||||||
|  |   ModelCtor, | ||||||
|  |   ModelOptions, | ||||||
|  |   QueryInterfaceDropTableOptions, | ||||||
|  |   SyncOptions, | ||||||
|  |   Transactionable, | ||||||
|  |   Utils | ||||||
|  | } from 'sequelize'; | ||||||
| import { Database } from './database'; | import { Database } from './database'; | ||||||
| import { Field, FieldOptions } from './fields'; | import { Field, FieldOptions } from './fields'; | ||||||
| import { Model } from './model'; | import { Model } from './model'; | ||||||
| @ -278,6 +285,67 @@ export class Collection< | |||||||
|     this.setField(options.name || name, options); |     this.setField(options.name || name, options); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   addIndex(index: any) { | ||||||
|  |     if (!index) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     let indexes: any = this.model.options.indexes || []; | ||||||
|  |     let indexName = []; | ||||||
|  |     let indexItem; | ||||||
|  |     if (typeof index === 'string') { | ||||||
|  |       indexItem = { | ||||||
|  |         fields: [index], | ||||||
|  |       }; | ||||||
|  |       indexName = [index]; | ||||||
|  |     } else if (Array.isArray(index)) { | ||||||
|  |       indexItem = { | ||||||
|  |         fields: index, | ||||||
|  |       }; | ||||||
|  |       indexName = index; | ||||||
|  |     } else if (index?.fields) { | ||||||
|  |       indexItem = index; | ||||||
|  |       indexName = index.fields; | ||||||
|  |     } | ||||||
|  |     if (lodash.isEqual(this.model.primaryKeyAttributes, indexName)) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     const name: string = this.model.primaryKeyAttributes.join(','); | ||||||
|  |     if (name.startsWith(`${indexName.join(',')},`)) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     for (const item of indexes) { | ||||||
|  |       if (lodash.isEqual(item.fields, indexName)) { | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       const name: string = item.fields.join(','); | ||||||
|  |       if (name.startsWith(`${indexName.join(',')},`)) { | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (!indexItem) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     indexes.push(indexItem); | ||||||
|  |     this.model.options.indexes = indexes; | ||||||
|  |     const tableName = this.model.getTableName(); | ||||||
|  |     // @ts-ignore
 | ||||||
|  |     this.model._indexes = this.model.options.indexes | ||||||
|  |       // @ts-ignore
 | ||||||
|  |       .map((index) => Utils.nameIndex(this.model._conformIndex(index), tableName)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   removeIndex(fields: any) { | ||||||
|  |     if (!fields) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     // @ts-ignore
 | ||||||
|  |     const indexes: any[] = this.model._indexes; | ||||||
|  |     // @ts-ignore
 | ||||||
|  |     this.model._indexes = indexes.filter((item) => { | ||||||
|  |       return !lodash.isEqual(item.fields, fields); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   async sync(syncOptions?: SyncOptions) { |   async sync(syncOptions?: SyncOptions) { | ||||||
|     const modelNames = [this.model.name]; |     const modelNames = [this.model.name]; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -46,7 +46,7 @@ export class BelongsToField extends RelationField { | |||||||
|       // @ts-ignore
 |       // @ts-ignore
 | ||||||
|       this.options.sourceKey = association.sourceKey; |       this.options.sourceKey = association.sourceKey; | ||||||
|     } |     } | ||||||
| 
 |     this.collection.addIndex([this.options.foreignKey]); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -68,6 +68,7 @@ export class BelongsToField extends RelationField { | |||||||
|     delete collection.model.associations[this.name]; |     delete collection.model.associations[this.name]; | ||||||
|     // @ts-ignore
 |     // @ts-ignore
 | ||||||
|     collection.model.refreshAttributes(); |     collection.model.refreshAttributes(); | ||||||
|  |     // this.collection.removeIndex([this.options.foreignKey]);
 | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -58,6 +58,8 @@ export class BelongsToManyField extends RelationField { | |||||||
|     if (!this.options.through) { |     if (!this.options.through) { | ||||||
|       this.options.through = this.through; |       this.options.through = this.through; | ||||||
|     } |     } | ||||||
|  |     Through.addIndex([this.options.foreignKey]); | ||||||
|  |     Through.addIndex([this.options.otherKey]); | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -171,11 +171,17 @@ export abstract class Field { | |||||||
|     model.rawAttributes[this.name] = this.toSequelize(); |     model.rawAttributes[this.name] = this.toSequelize(); | ||||||
|     // @ts-ignore
 |     // @ts-ignore
 | ||||||
|     model.refreshAttributes(); |     model.refreshAttributes(); | ||||||
|  |     if (this.options.index) { | ||||||
|  |       this.context.collection.addIndex([this.name]); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   unbind() { |   unbind() { | ||||||
|     const { model } = this.context.collection; |     const { model } = this.context.collection; | ||||||
|     model.removeAttribute(this.name); |     model.removeAttribute(this.name); | ||||||
|  |     if (this.options.index) { | ||||||
|  |       this.context.collection.removeIndex([this.name]); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   toSequelize(): any { |   toSequelize(): any { | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ import { | |||||||
|   HasManyOptions as SequelizeHasManyOptions, |   HasManyOptions as SequelizeHasManyOptions, | ||||||
|   Utils |   Utils | ||||||
| } from 'sequelize'; | } from 'sequelize'; | ||||||
| 
 | import { Collection } from '../collection'; | ||||||
| import { MultipleRelationFieldOptions, RelationField } from './relation-field'; | import { MultipleRelationFieldOptions, RelationField } from './relation-field'; | ||||||
| 
 | 
 | ||||||
| export interface HasManyFieldOptions extends HasManyOptions { | export interface HasManyFieldOptions extends HasManyOptions { | ||||||
| @ -98,7 +98,6 @@ export class HasManyField extends RelationField { | |||||||
|       as: this.name, |       as: this.name, | ||||||
|       foreignKey: this.foreignKey, |       foreignKey: this.foreignKey, | ||||||
|     }); |     }); | ||||||
| 
 |  | ||||||
|     // inverse relation
 |     // inverse relation
 | ||||||
|     // this.TargetModel.belongsTo(collection.model);
 |     // this.TargetModel.belongsTo(collection.model);
 | ||||||
| 
 | 
 | ||||||
| @ -112,6 +111,15 @@ export class HasManyField extends RelationField { | |||||||
|       // @ts-ignore
 |       // @ts-ignore
 | ||||||
|       this.options.sourceKey = association.sourceKey; |       this.options.sourceKey = association.sourceKey; | ||||||
|     } |     } | ||||||
|  |     let tcoll: Collection; | ||||||
|  |     if (this.target === collection.name) { | ||||||
|  |       tcoll = collection; | ||||||
|  |     } else { | ||||||
|  |       tcoll = database.getCollection(this.target); | ||||||
|  |     } | ||||||
|  |     if (tcoll) { | ||||||
|  |       tcoll.addIndex([this.options.foreignKey]); | ||||||
|  |     } | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -120,7 +128,7 @@ export class HasManyField extends RelationField { | |||||||
|     // 如果关系字段还没建立就删除了,也同步删除待建立关联的关系字段
 |     // 如果关系字段还没建立就删除了,也同步删除待建立关联的关系字段
 | ||||||
|     database.removePendingField(this); |     database.removePendingField(this); | ||||||
|     // 如果关系表内没有显式的创建外键字段,删除关系时,外键也删除掉
 |     // 如果关系表内没有显式的创建外键字段,删除关系时,外键也删除掉
 | ||||||
|     const tcoll = database.collections.get(this.target); |     const tcoll = database.getCollection(this.target); | ||||||
|     const foreignKey = this.options.foreignKey; |     const foreignKey = this.options.foreignKey; | ||||||
|     const field = tcoll.findField((field) => { |     const field = tcoll.findField((field) => { | ||||||
|       if (field.name === foreignKey) { |       if (field.name === foreignKey) { | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ import { | |||||||
|   HasOneOptions as SequelizeHasOneOptions, |   HasOneOptions as SequelizeHasOneOptions, | ||||||
|   Utils |   Utils | ||||||
| } from 'sequelize'; | } from 'sequelize'; | ||||||
|  | import { Collection } from '../collection'; | ||||||
| import { BaseRelationFieldOptions, RelationField } from './relation-field'; | import { BaseRelationFieldOptions, RelationField } from './relation-field'; | ||||||
| 
 | 
 | ||||||
| export interface HasOneFieldOptions extends HasOneOptions { | export interface HasOneFieldOptions extends HasOneOptions { | ||||||
| @ -106,6 +107,15 @@ export class HasOneField extends RelationField { | |||||||
|       // @ts-ignore
 |       // @ts-ignore
 | ||||||
|       this.options.sourceKey = association.sourceKey; |       this.options.sourceKey = association.sourceKey; | ||||||
|     } |     } | ||||||
|  |     let tcoll: Collection; | ||||||
|  |     if (this.target === collection.name) { | ||||||
|  |       tcoll = collection; | ||||||
|  |     } else { | ||||||
|  |       tcoll = database.getCollection(this.target); | ||||||
|  |     } | ||||||
|  |     if (tcoll) { | ||||||
|  |       tcoll.addIndex([this.options.foreignKey]); | ||||||
|  |     } | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -17,6 +17,7 @@ export default defineCollection({ | |||||||
|     { |     { | ||||||
|       type: 'string', |       type: 'string', | ||||||
|       name: 'recordId', |       name: 'recordId', | ||||||
|  |       index: true, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       type: 'string', |       type: 'string', | ||||||
|  | |||||||
| @ -44,6 +44,7 @@ export default { | |||||||
|       targetKey: 'name', |       targetKey: 'name', | ||||||
|       foreignKey: 'collectionName', |       foreignKey: 'collectionName', | ||||||
|       sortBy: 'sort', |       sortBy: 'sort', | ||||||
|  |       constraints: true, | ||||||
|     }, |     }, | ||||||
|   ], |   ], | ||||||
| } as CollectionOptions; | } as CollectionOptions; | ||||||
|  | |||||||
| @ -26,12 +26,14 @@ export default defineCollection({ | |||||||
|       target: 'uiRoutes', |       target: 'uiRoutes', | ||||||
|       sourceKey: 'key', |       sourceKey: 'key', | ||||||
|       foreignKey: 'parentKey', |       foreignKey: 'parentKey', | ||||||
|  |       constraints: true, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       type: 'belongsTo', |       type: 'belongsTo', | ||||||
|       name: 'uiSchema', |       name: 'uiSchema', | ||||||
|       target: 'uiSchemas', |       target: 'uiSchemas', | ||||||
|       foreignKey: 'uiSchemaUid' |       foreignKey: 'uiSchemaUid', | ||||||
|  |       constraints: true, | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       type: 'json', |       type: 'json', | ||||||
|  | |||||||
| @ -63,8 +63,7 @@ export default class UsersPlugin extends Plugin<UserPluginConfig> { | |||||||
|           dataIndex: 'state.currentUser.id', |           dataIndex: 'state.currentUser.id', | ||||||
|           createOnly: true, |           createOnly: true, | ||||||
|           visible: true, |           visible: true, | ||||||
|           onDelete: 'SET NULL', |           index: true, | ||||||
|           onUpdate: 'CASCADE', |  | ||||||
|         }); |         }); | ||||||
|         collection.setField('createdBy', { |         collection.setField('createdBy', { | ||||||
|           type: 'belongsTo', |           type: 'belongsTo', | ||||||
| @ -79,8 +78,7 @@ export default class UsersPlugin extends Plugin<UserPluginConfig> { | |||||||
|           dataType: 'integer', |           dataType: 'integer', | ||||||
|           dataIndex: 'state.currentUser.id', |           dataIndex: 'state.currentUser.id', | ||||||
|           visible: true, |           visible: true, | ||||||
|           onDelete: 'SET NULL', |           index: true, | ||||||
|           onUpdate: 'CASCADE', |  | ||||||
|         }); |         }); | ||||||
|         collection.setField('updatedBy', { |         collection.setField('updatedBy', { | ||||||
|           type: 'belongsTo', |           type: 'belongsTo', | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user