fix(database): index invalid (#564)

* fix(database): index invalid

* fix: test error
This commit is contained in:
chenos 2022-07-01 09:33:05 +08:00 committed by GitHub
parent 41cb3da448
commit 70ab4dcf1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 136 additions and 19 deletions

View File

@ -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({

View File

@ -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);
}); });
}); });
}); });

View File

@ -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];

View File

@ -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]);
} }
} }

View File

@ -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;
} }

View File

@ -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 {

View File

@ -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) {

View File

@ -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;
} }

View File

@ -17,6 +17,7 @@ export default defineCollection({
{ {
type: 'string', type: 'string',
name: 'recordId', name: 'recordId',
index: true,
}, },
{ {
type: 'string', type: 'string',

View File

@ -44,6 +44,7 @@ export default {
targetKey: 'name', targetKey: 'name',
foreignKey: 'collectionName', foreignKey: 'collectionName',
sortBy: 'sort', sortBy: 'sort',
constraints: true,
}, },
], ],
} as CollectionOptions; } as CollectionOptions;

View File

@ -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',

View File

@ -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',