mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
added ability to create indices and compound indices
This commit is contained in:
parent
80abbb5e23
commit
8902402acc
12
README.md
12
README.md
@ -1,5 +1,13 @@
|
||||
# TypeORM
|
||||
|
||||
> Note: docs are not always up to date because orm is in active development.
|
||||
> Samples are more up to date, try to find your questions there.
|
||||
> Otherwise create a github issue.
|
||||
>
|
||||
> Note: Current version of orm works with typescript >1.9. It means you need to install
|
||||
> typescript@next to work with it. If you want to use older version of orm you can try
|
||||
> to install typeorm@0.0.1
|
||||
|
||||
## What is TypeORM?
|
||||
|
||||
TypeORM is an [Object Relational Mapper](1) (ORM) for node.js written in
|
||||
@ -49,10 +57,6 @@ need to do:
|
||||
|
||||
## Example
|
||||
|
||||
> Note: docs are not always up to date because orm is in active development.
|
||||
> Samples are more up to date, try to find your questions there.
|
||||
> Otherwise a github issue.
|
||||
|
||||
Lets create a sample application - a photo album.
|
||||
|
||||
#### create Photo entity class
|
||||
|
||||
@ -21,7 +21,7 @@ createConnection(options).then(connection => {
|
||||
post.title = "hello";
|
||||
post.likesCount = 100;
|
||||
|
||||
let postRepository = connection.getRepository<Post>(Post);
|
||||
let postRepository = connection.getRepository(Post);
|
||||
|
||||
postRepository
|
||||
.persist(post)
|
||||
|
||||
@ -19,7 +19,7 @@ const options: CreateConnectionOptions = {
|
||||
};
|
||||
|
||||
createConnection(options).then(connection => {
|
||||
let postRepository = connection.getRepository<Post>(Post);
|
||||
let postRepository = connection.getRepository(Post);
|
||||
|
||||
let postCover = new Cover();
|
||||
postCover.url = "http://covers.com/post.jpg";
|
||||
|
||||
@ -23,7 +23,7 @@ createConnection(options).then(connection => {
|
||||
post.text = "Hello how are you?";
|
||||
post.title = "hello";
|
||||
|
||||
let postRepository = connection.getRepository<Post>(Post);
|
||||
let postRepository = connection.getRepository(Post);
|
||||
|
||||
postRepository
|
||||
.persist(post)
|
||||
|
||||
@ -17,4 +17,11 @@ export class CustomNamingStrategy implements NamingStrategyInterface {
|
||||
return _.snakeCase(propertyName);
|
||||
}
|
||||
|
||||
indexName(target: Function, name: string, columns: string[]): string {
|
||||
if (name)
|
||||
return name;
|
||||
|
||||
return "index" + columns.join("_");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
30
sample/sample16-indexes/app.ts
Normal file
30
sample/sample16-indexes/app.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import {createConnection, CreateConnectionOptions} from "../../src/typeorm";
|
||||
import {Post} from "./entity/Post";
|
||||
|
||||
const options: CreateConnectionOptions = {
|
||||
driver: "mysql",
|
||||
connection: {
|
||||
host: "192.168.99.100",
|
||||
port: 3306,
|
||||
username: "root",
|
||||
password: "admin",
|
||||
database: "test",
|
||||
autoSchemaCreate: true
|
||||
},
|
||||
entities: [Post]
|
||||
};
|
||||
|
||||
createConnection(options).then(connection => {
|
||||
|
||||
let post = new Post();
|
||||
post.text = "Hello how are you?";
|
||||
post.title = "hello";
|
||||
post.likesCount = 0;
|
||||
|
||||
let postRepository = connection.getRepository(Post);
|
||||
|
||||
postRepository
|
||||
.persist(post)
|
||||
.then(post => console.log("Post has been saved"));
|
||||
|
||||
}, error => console.log("Cannot connect: ", error));
|
||||
25
sample/sample16-indexes/entity/Post.ts
Normal file
25
sample/sample16-indexes/entity/Post.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import {PrimaryColumn, Column} from "../../../src/columns";
|
||||
import {Table} from "../../../src/tables";
|
||||
import {Index} from "../../../src/decorator/indices/Index";
|
||||
import {CompositeIndex} from "../../../src/decorator/indices/CompositeIndex";
|
||||
|
||||
@Table("sample16_post")
|
||||
@CompositeIndex(["title", "text"])
|
||||
@CompositeIndex(["title", "likesCount"])
|
||||
export class Post {
|
||||
|
||||
@PrimaryColumn("int", { generated: true })
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
@Index()
|
||||
title: string;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
@Column()
|
||||
@Index()
|
||||
likesCount: number;
|
||||
|
||||
}
|
||||
@ -32,7 +32,7 @@ createConnection(options).then(connection => {
|
||||
post.title = "hello";
|
||||
post.details = details;
|
||||
|
||||
let postRepository = connection.getRepository<Post>(Post);
|
||||
let postRepository = connection.getRepository(Post);
|
||||
|
||||
postRepository
|
||||
.persist(post)
|
||||
|
||||
@ -31,7 +31,7 @@ createConnection(options).then(connection => {
|
||||
post.title = "hello";
|
||||
post.details = details;
|
||||
|
||||
let postRepository = connection.getRepository<Post>(Post);
|
||||
let postRepository = connection.getRepository(Post);
|
||||
|
||||
postRepository
|
||||
.persist(post)
|
||||
|
||||
@ -27,7 +27,7 @@ createConnection(options).then(connection => {
|
||||
post.title = "hello";
|
||||
post.details = [details1, details2];
|
||||
|
||||
let postRepository = connection.getRepository<Post>(Post);
|
||||
let postRepository = connection.getRepository(Post);
|
||||
|
||||
postRepository
|
||||
.persist(post)
|
||||
|
||||
16
src/decorator/indices/CompositeIndex.ts
Normal file
16
src/decorator/indices/CompositeIndex.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import {CompositeIndexMetadata} from "../../metadata/CompositeIndexMetadata";
|
||||
import {defaultMetadataStorage} from "../../typeorm";
|
||||
|
||||
/**
|
||||
* Composite indexes must be set on entity classes and must specify fields to be indexed.
|
||||
*/
|
||||
export function CompositeIndex(name: string, fields: string[]): Function;
|
||||
export function CompositeIndex(fields: string[]): Function;
|
||||
export function CompositeIndex(nameOrFields: string|string[], maybeFields?: string[]): Function {
|
||||
const name = typeof nameOrFields === "string" ? nameOrFields : undefined;
|
||||
const fields = typeof nameOrFields === "string" ? <string[]> maybeFields : nameOrFields;
|
||||
|
||||
return function (cls: Function) {
|
||||
defaultMetadataStorage().compositeIndexMetadatas.add(new CompositeIndexMetadata(cls, name, fields));
|
||||
};
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import {CompoundIndexMetadata} from "../../metadata/CompoundIndexMetadata";
|
||||
import {defaultMetadataStorage} from "../../typeorm";
|
||||
|
||||
/**
|
||||
* Compound indexes must be set on entity classes and must specify fields to be indexed.
|
||||
*/
|
||||
export function CompoundIndex(fields: string[]) {
|
||||
return function (cls: Function) {
|
||||
defaultMetadataStorage().compoundIndexMetadatas.add(new CompoundIndexMetadata(cls, fields));
|
||||
};
|
||||
}
|
||||
@ -1,2 +1,2 @@
|
||||
export * from "./decorator/indices/Index";
|
||||
export * from "./decorator/indices/CompoundIndex";
|
||||
export * from "./decorator/indices/CompositeIndex";
|
||||
|
||||
@ -13,6 +13,10 @@ import {UsingJoinColumnOnlyOnOneSideAllowedError} from "./error/UsingJoinColumnO
|
||||
import {MissingJoinColumnError} from "./error/MissingJoinColumnError";
|
||||
import {MissingJoinTableError} from "./error/MissingJoinTableError";
|
||||
import {EntityMetadataValidator} from "./EntityMetadataValidator";
|
||||
import {IndexMetadata} from "../metadata/IndexMetadata";
|
||||
import {CompositeIndexMetadata} from "../metadata/CompositeIndexMetadata";
|
||||
import {PropertyMetadataCollection} from "../metadata/collection/PropertyMetadataCollection";
|
||||
import {TargetMetadataCollection} from "../metadata/collection/TargetMetadataCollection";
|
||||
|
||||
/**
|
||||
* Aggregates all metadata: table, column, relation into one collection grouped by tables for a given set of classes.
|
||||
@ -40,6 +44,17 @@ export class EntityMetadataBuilder {
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private mergeIndicesAndCompositeIndices(indices: PropertyMetadataCollection<IndexMetadata>,
|
||||
compositeIndices: TargetMetadataCollection<CompositeIndexMetadata>) {
|
||||
indices.forEach(index => {
|
||||
const compositeIndex = new CompositeIndexMetadata(index.target, index.name, [index.propertyName]);
|
||||
compositeIndex.namingStrategy = index.namingStrategy;
|
||||
compositeIndices.add(compositeIndex);
|
||||
});
|
||||
|
||||
// later need to check if no duplicate keys in composite indices?
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a complete metadata aggregations for the given entity classes.
|
||||
*/
|
||||
@ -51,15 +66,31 @@ export class EntityMetadataBuilder {
|
||||
const allTableMetadatas = allMetadataStorage.tableMetadatas.filterByClasses(entityClasses);
|
||||
const tableMetadatas = allTableMetadatas.filterByClasses(entityClasses).filter(table => !table.isAbstract);
|
||||
|
||||
// set naming strategy
|
||||
allMetadataStorage.tableMetadatas.forEach(tableMetadata => tableMetadata.namingStrategy = this.namingStrategy);
|
||||
allTableMetadatas.forEach(column => column.namingStrategy = this.namingStrategy);
|
||||
// entityMetadata.relations.forEach(relation => relation.namingStrategy = this.namingStrategy);
|
||||
|
||||
const entityMetadatas = tableMetadatas.map(tableMetadata => {
|
||||
|
||||
const mergedMetadata = allMetadataStorage.mergeWithAbstract(allTableMetadatas, tableMetadata);
|
||||
|
||||
// set naming strategy
|
||||
tableMetadata.namingStrategy = this.namingStrategy;
|
||||
mergedMetadata.columnMetadatas.forEach(column => column.namingStrategy = this.namingStrategy);
|
||||
mergedMetadata.relationMetadatas.forEach(relation => relation.namingStrategy = this.namingStrategy);
|
||||
mergedMetadata.indexMetadatas.forEach(relation => relation.namingStrategy = this.namingStrategy);
|
||||
mergedMetadata.compositeIndexMetadatas.forEach(relation => relation.namingStrategy = this.namingStrategy);
|
||||
|
||||
// merge indices and composite indices because simple indices actually are compose indices with only one column
|
||||
this.mergeIndicesAndCompositeIndices(mergedMetadata.indexMetadatas, mergedMetadata.compositeIndexMetadatas);
|
||||
|
||||
const entityMetadata = new EntityMetadata(
|
||||
tableMetadata,
|
||||
mergedMetadata.columnMetadatas,
|
||||
mergedMetadata.relationMetadatas,
|
||||
mergedMetadata.indexMetadatas,
|
||||
mergedMetadata.compoundIndexMetadatas,
|
||||
// mergedMetadata.indexMetadatas,
|
||||
mergedMetadata.compositeIndexMetadatas,
|
||||
[]
|
||||
);
|
||||
|
||||
@ -76,11 +107,6 @@ export class EntityMetadataBuilder {
|
||||
if (relationJoinColumn)
|
||||
relation.joinColumn = relationJoinColumn;
|
||||
});
|
||||
|
||||
// set naming strategies
|
||||
tableMetadata.namingStrategy = this.namingStrategy;
|
||||
entityMetadata.columns.forEach(column => column.namingStrategy = this.namingStrategy);
|
||||
entityMetadata.relations.forEach(relation => relation.namingStrategy = this.namingStrategy);
|
||||
|
||||
return entityMetadata;
|
||||
});
|
||||
@ -164,7 +190,7 @@ export class EntityMetadataBuilder {
|
||||
new ForeignKeyMetadata(tableMetadata, [columns[0]], metadata.table, [metadata.primaryColumn]),
|
||||
new ForeignKeyMetadata(tableMetadata, [columns[1]], inverseSideMetadata.table, [inverseSideMetadata.primaryColumn]),
|
||||
];
|
||||
const junctionEntityMetadata = new EntityMetadata(tableMetadata, columns, [], [], [], foreignKeys);
|
||||
const junctionEntityMetadata = new EntityMetadata(tableMetadata, columns, [], [], foreignKeys);
|
||||
junctionEntityMetadatas.push(junctionEntityMetadata);
|
||||
relation.junctionEntityMetadata = junctionEntityMetadata;
|
||||
if (relation.inverseRelation)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {TableMetadata} from "../metadata/TableMetadata";
|
||||
import {RelationMetadata} from "../metadata/RelationMetadata";
|
||||
import {IndexMetadata} from "../metadata/IndexMetadata";
|
||||
import {CompoundIndexMetadata} from "../metadata/CompoundIndexMetadata";
|
||||
import {CompositeIndexMetadata} from "../metadata/CompositeIndexMetadata";
|
||||
import {ColumnMetadata} from "../metadata/ColumnMetadata";
|
||||
import {EventSubscriberMetadata} from "../metadata/EventSubscriberMetadata";
|
||||
import {EntityListenerMetadata} from "../metadata/EntityListenerMetadata";
|
||||
@ -29,7 +29,7 @@ export class MetadataStorage {
|
||||
readonly tableMetadatas = new TargetMetadataCollection<TableMetadata>();
|
||||
readonly namingStrategyMetadatas = new TargetMetadataCollection<NamingStrategyMetadata>();
|
||||
readonly eventSubscriberMetadatas = new TargetMetadataCollection<EventSubscriberMetadata>();
|
||||
readonly compoundIndexMetadatas = new TargetMetadataCollection<CompoundIndexMetadata>();
|
||||
readonly compositeIndexMetadatas = new TargetMetadataCollection<CompositeIndexMetadata>();
|
||||
readonly columnMetadatas = new PropertyMetadataCollection<ColumnMetadata>();
|
||||
readonly relationMetadatas = new PropertyMetadataCollection<RelationMetadata>();
|
||||
readonly joinColumnMetadatas = new PropertyMetadataCollection<JoinColumnMetadata>();
|
||||
@ -44,7 +44,7 @@ export class MetadataStorage {
|
||||
constructor(tableMetadatas?: TargetMetadataCollection<TableMetadata>,
|
||||
namingStrategyMetadatas?: TargetMetadataCollection<NamingStrategyMetadata>,
|
||||
eventSubscriberMetadatas?: TargetMetadataCollection<EventSubscriberMetadata>,
|
||||
compoundIndexMetadatas?: TargetMetadataCollection<CompoundIndexMetadata>,
|
||||
compositeIndexMetadatas?: TargetMetadataCollection<CompositeIndexMetadata>,
|
||||
columnMetadatas?: PropertyMetadataCollection<ColumnMetadata>,
|
||||
relationMetadatas?: PropertyMetadataCollection<RelationMetadata>,
|
||||
joinColumnMetadatas?: PropertyMetadataCollection<JoinColumnMetadata>,
|
||||
@ -57,8 +57,8 @@ export class MetadataStorage {
|
||||
this.namingStrategyMetadatas = namingStrategyMetadatas;
|
||||
if (eventSubscriberMetadatas)
|
||||
this.eventSubscriberMetadatas = eventSubscriberMetadatas;
|
||||
if (compoundIndexMetadatas)
|
||||
this.compoundIndexMetadatas = compoundIndexMetadatas;
|
||||
if (compositeIndexMetadatas)
|
||||
this.compositeIndexMetadatas = compositeIndexMetadatas;
|
||||
if (columnMetadatas)
|
||||
this.columnMetadatas = columnMetadatas;
|
||||
if (relationMetadatas)
|
||||
@ -84,7 +84,7 @@ export class MetadataStorage {
|
||||
mergeWithAbstract(allTableMetadatas: TargetMetadataCollection<TableMetadata>,
|
||||
tableMetadata: TableMetadata) {
|
||||
|
||||
const compoundIndexMetadatas = this.compoundIndexMetadatas.filterByClass(tableMetadata.target);
|
||||
const compositeIndexMetadatas = this.compositeIndexMetadatas.filterByClass(tableMetadata.target);
|
||||
const columnMetadatas = this.columnMetadatas.filterByClass(tableMetadata.target);
|
||||
const relationMetadatas = this.relationMetadatas.filterByClass(tableMetadata.target);
|
||||
const joinColumnMetadatas = this.joinColumnMetadatas.filterByClass(tableMetadata.target);
|
||||
@ -106,7 +106,7 @@ export class MetadataStorage {
|
||||
});
|
||||
|
||||
return {
|
||||
compoundIndexMetadatas: compoundIndexMetadatas,
|
||||
compositeIndexMetadatas: compositeIndexMetadatas,
|
||||
columnMetadatas: columnMetadatas,
|
||||
relationMetadatas: relationMetadatas,
|
||||
joinColumnMetadatas: joinColumnMetadatas,
|
||||
|
||||
55
src/metadata/CompositeIndexMetadata.ts
Normal file
55
src/metadata/CompositeIndexMetadata.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import {TargetMetadata} from "./TargetMetadata";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategy";
|
||||
|
||||
/**
|
||||
* This metadata interface contains all information about table's composite index.
|
||||
*/
|
||||
export class CompositeIndexMetadata extends TargetMetadata {
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used to generate and normalize index name.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Readonly Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Columns combination to be used as index.
|
||||
*/
|
||||
readonly columns: string[];
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Private Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Composite index name.
|
||||
*/
|
||||
private readonly _name: string;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(target: Function, name: string|undefined, columns: string[]) {
|
||||
super(target);
|
||||
this.columns = columns;
|
||||
if (name)
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Accessors
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
get name() { // throw exception if naming strategy is not set
|
||||
return this.namingStrategy.indexName(this.target, this._name, this.columns);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
import {TargetMetadata} from "./TargetMetadata";
|
||||
|
||||
/**
|
||||
* This metadata interface contains all information about table's compound index.
|
||||
*/
|
||||
export class CompoundIndexMetadata extends TargetMetadata {
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Readonly Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Fields combination to be used as index.
|
||||
*/
|
||||
readonly fields: string[];
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
constructor(target: Function, fields: string[]) {
|
||||
super(target);
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,7 +2,7 @@ import {TableMetadata} from "./TableMetadata";
|
||||
import {ColumnMetadata} from "./ColumnMetadata";
|
||||
import {RelationMetadata} from "./RelationMetadata";
|
||||
import {IndexMetadata} from "./IndexMetadata";
|
||||
import {CompoundIndexMetadata} from "./CompoundIndexMetadata";
|
||||
import {CompositeIndexMetadata} from "./CompositeIndexMetadata";
|
||||
import {RelationTypes} from "./types/RelationTypes";
|
||||
import {ForeignKeyMetadata} from "./ForeignKeyMetadata";
|
||||
|
||||
@ -18,8 +18,8 @@ export class EntityMetadata {
|
||||
readonly table: TableMetadata;
|
||||
readonly columns: ColumnMetadata[];
|
||||
readonly relations: RelationMetadata[];
|
||||
readonly indices: IndexMetadata[];
|
||||
readonly compoundIndices: CompoundIndexMetadata[];
|
||||
// readonly indices: IndexMetadata[];
|
||||
readonly compositeIndices: CompositeIndexMetadata[];
|
||||
readonly foreignKeys: ForeignKeyMetadata[];
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@ -29,14 +29,14 @@ export class EntityMetadata {
|
||||
constructor(table: TableMetadata,
|
||||
columns: ColumnMetadata[],
|
||||
relations: RelationMetadata[],
|
||||
indices: IndexMetadata[],
|
||||
compoundIndices: CompoundIndexMetadata[],
|
||||
// indices: IndexMetadata[],
|
||||
compositeIndices: CompositeIndexMetadata[],
|
||||
foreignKeys: ForeignKeyMetadata[]) {
|
||||
this.table = table;
|
||||
this.columns = columns;
|
||||
this.relations = relations;
|
||||
this.indices = indices;
|
||||
this.compoundIndices = compoundIndices;
|
||||
// this.indices = indices;
|
||||
this.compositeIndices = compositeIndices;
|
||||
this.foreignKeys = foreignKeys;
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,20 @@
|
||||
import {PropertyMetadata} from "./PropertyMetadata";
|
||||
import {NamingStrategyInterface} from "../naming-strategy/NamingStrategy";
|
||||
|
||||
/**
|
||||
* This metadata interface contains all information about some index on a field.
|
||||
*/
|
||||
export class IndexMetadata extends PropertyMetadata {
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used to generate and normalize index name.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Readonly Properties
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
@ -3,8 +3,6 @@ import {EntityMetadataNotFound} from "../error/EntityMetadataNotFound";
|
||||
|
||||
/**
|
||||
* Array for the entity metadatas.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class EntityMetadataCollection extends Array<EntityMetadata> {
|
||||
|
||||
|
||||
@ -3,6 +3,10 @@ import {PropertyMetadata} from "../PropertyMetadata";
|
||||
|
||||
export class PropertyMetadataCollection<T extends PropertyMetadata> extends TargetMetadataCollection<T> {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
filterRepeatedMetadatas(existsMetadatas: T[]): this {
|
||||
const collection = new (<any> this.constructor)();
|
||||
this
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import {TargetMetadata} from "../TargetMetadata";
|
||||
import {MetadataAlreadyExistsError} from "../../metadata-storage/error/MetadataAlreadyExistsError";
|
||||
|
||||
export class TargetMetadataCollection<T extends TargetMetadata> extends Array<T> {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
filterByClass(cls: Function): this {
|
||||
return this.filterByClasses([cls]);
|
||||
@ -13,17 +18,25 @@ export class TargetMetadataCollection<T extends TargetMetadata> extends Array<T>
|
||||
return collection;
|
||||
}
|
||||
|
||||
|
||||
add(metadata: T) {
|
||||
// if (this.hasWithClass(metadata.target))
|
||||
// throw new MetadataAlreadyExistsError((<any> metadata.constructor).name, metadata.target);
|
||||
// if (metadata.name && this.hasTableMetadataWithName(metadata.name))
|
||||
// throw new MetadataWithSuchNameAlreadyExistsError("Table", metadata.name);
|
||||
add(metadata: T, checkForDuplicateTargets = false) {
|
||||
if (checkForDuplicateTargets && this.hasWithTarget(metadata.target))
|
||||
throw new MetadataAlreadyExistsError((<any> metadata.constructor).name, metadata.target);
|
||||
|
||||
this.push(metadata);
|
||||
}
|
||||
|
||||
private hasWithClass(constructor: Function): boolean {
|
||||
addUniq(metadata: T) {
|
||||
if (this.hasWithTarget(metadata.target))
|
||||
throw new MetadataAlreadyExistsError((<any> metadata.constructor).name, metadata.target);
|
||||
|
||||
this.push(metadata);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private hasWithTarget(constructor: Function): boolean {
|
||||
return !!this.find(metadata => metadata.target === constructor);
|
||||
}
|
||||
|
||||
|
||||
@ -18,4 +18,11 @@ export class DefaultNamingStrategy implements NamingStrategyInterface {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
indexName(target: Function, name: string, columns: string[]): string {
|
||||
if (name)
|
||||
return name;
|
||||
|
||||
return "ind_" + columns.join("_");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -19,4 +19,9 @@ export interface NamingStrategyInterface {
|
||||
*/
|
||||
relationName(propertyName: string): string;
|
||||
|
||||
/**
|
||||
* Gets the name of the index - simple and compose index.
|
||||
*/
|
||||
indexName(target: Function, name: string, columns: string[]): string;
|
||||
|
||||
}
|
||||
@ -77,9 +77,9 @@ export class MysqlSchemaBuilder extends SchemaBuilder {
|
||||
return this.query(sql).then(() => {});
|
||||
}
|
||||
|
||||
getTableForeignQuery(table: TableMetadata): Promise<string[]> {
|
||||
getTableForeignQuery(tableName: string): Promise<string[]> {
|
||||
const sql = `SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = "${this.driver.db}" `
|
||||
+ `AND TABLE_NAME = "${table.name}" AND REFERENCED_COLUMN_NAME IS NOT NULL`;
|
||||
+ `AND TABLE_NAME = "${tableName}" AND REFERENCED_COLUMN_NAME IS NOT NULL`;
|
||||
return this.query<any[]>(sql).then(results => results.map(result => result.CONSTRAINT_NAME));
|
||||
}
|
||||
|
||||
@ -89,6 +89,23 @@ export class MysqlSchemaBuilder extends SchemaBuilder {
|
||||
return this.query<any[]>(sql).then(results => results.map(result => result.CONSTRAINT_NAME));
|
||||
}
|
||||
|
||||
getTableIndicesQuery(tableName: string): Promise<{ key: string, sequence: number, column: string }[]> {
|
||||
const sql = `SHOW INDEX FROM ${tableName}`;
|
||||
return this.query<any[]>(sql).then(results => {
|
||||
// exclude foreign keys
|
||||
return this.getTableForeignQuery(tableName).then(foreignKeys => {
|
||||
|
||||
return results
|
||||
.filter(result => result.Key_name !== "PRIMARY" && foreignKeys.indexOf(result.Key_name) === -1)
|
||||
.map(result => ({
|
||||
key: result.Key_name,
|
||||
sequence: result.Seq_in_index,
|
||||
column: result.Column_name
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getPrimaryConstraintName(tableName: string): Promise<string> {
|
||||
const sql = `SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = "${this.driver.db}"`
|
||||
+ ` AND TABLE_NAME = "${tableName}" AND CONSTRAINT_TYPE = 'PRIMARY KEY'`;
|
||||
@ -100,6 +117,11 @@ export class MysqlSchemaBuilder extends SchemaBuilder {
|
||||
return this.query(sql).then(() => {});
|
||||
}
|
||||
|
||||
createIndex(tableName: string, indexName: string, columns: string[]): Promise<void> {
|
||||
const sql = `CREATE INDEX \`${indexName}\` ON ${tableName}(${columns.join(", ")})`;
|
||||
return this.query(sql).then(() => {});
|
||||
}
|
||||
|
||||
addUniqueKey(tableName: string, columnName: string, keyName: string): Promise<void> {
|
||||
const sql = `ALTER TABLE ${tableName} ADD CONSTRAINT ${keyName} UNIQUE (${columnName})`;
|
||||
return this.query(sql).then(() => {});
|
||||
|
||||
@ -14,10 +14,12 @@ export abstract class SchemaBuilder {
|
||||
abstract addForeignKeyQuery(foreignKey: ForeignKeyMetadata): Promise<void>;
|
||||
abstract dropForeignKeyQuery(foreignKey: ForeignKeyMetadata): Promise<void>;
|
||||
abstract dropForeignKeyQuery(tableName: string, foreignKeyName: string): Promise<void>;
|
||||
abstract getTableForeignQuery(table: TableMetadata): Promise<string[]>;
|
||||
abstract getTableForeignQuery(tableName: string): Promise<string[]>;
|
||||
abstract getTableUniqueKeysQuery(tableName: string): Promise<string[]>;
|
||||
abstract getTableIndicesQuery(tableName: string): Promise<{ key: string, sequence: number, column: string }[]>;
|
||||
abstract getPrimaryConstraintName(tableName: string): Promise<string>;
|
||||
abstract dropIndex(tableName: string, indexName: string): Promise<void>;
|
||||
abstract createIndex(tableName: string, indexName: string, columns: string[]): Promise<void>;
|
||||
abstract addUniqueKey(tableName: string, columnName: string, keyName: string): Promise<void>;
|
||||
abstract getTableColumns(tableName: string): Promise<string[]>;
|
||||
abstract changeColumnQuery(tableName: string, columnName: string, newColumn: ColumnMetadata, skipPrimary?: boolean): Promise<void>;
|
||||
|
||||
@ -4,6 +4,8 @@ import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {SchemaBuilder} from "../schema-builder/SchemaBuilder";
|
||||
import {EntityMetadataCollection} from "../metadata/collection/EntityMetadataCollection";
|
||||
import {IndexMetadata} from "../metadata/IndexMetadata";
|
||||
import {CompositeIndexMetadata} from "../metadata/CompositeIndexMetadata";
|
||||
|
||||
/**
|
||||
* Creates indexes based on the given metadata.
|
||||
@ -38,6 +40,7 @@ export class SchemaCreator {
|
||||
.then(_ => this.updateExistColumnsForAll(metadatas))
|
||||
.then(_ => this.createForeignKeysForAll(metadatas))
|
||||
.then(_ => this.updateUniqueKeysForAll(metadatas))
|
||||
.then(_ => this.createIndicesForAll(metadatas))
|
||||
.then(_ => this.removePrimaryKeyForAll(metadatas))
|
||||
.then(_ => {});
|
||||
}
|
||||
@ -78,6 +81,10 @@ export class SchemaCreator {
|
||||
return Promise.all(metadatas.map(metadata => this.updateUniqueKeys(metadata.table, metadata.columns)));
|
||||
}
|
||||
|
||||
private createIndicesForAll(metadatas: EntityMetadata[]) {
|
||||
return Promise.all(metadatas.map(metadata => this.createIndices(metadata.table, metadata.compositeIndices)));
|
||||
}
|
||||
|
||||
private removePrimaryKeyForAll(metadatas: EntityMetadata[]) {
|
||||
const queries = metadatas
|
||||
.filter(metadata => !metadata.primaryColumn)
|
||||
@ -89,7 +96,7 @@ export class SchemaCreator {
|
||||
* Drops all (old) foreign keys that exist in the table, but does not exist in the metadata.
|
||||
*/
|
||||
private dropForeignKeys(table: TableMetadata, foreignKeys: ForeignKeyMetadata[]) {
|
||||
return this.schemaBuilder.getTableForeignQuery(table).then(dbKeys => {
|
||||
return this.schemaBuilder.getTableForeignQuery(table.name).then(dbKeys => {
|
||||
const dropKeysQueries = dbKeys
|
||||
.filter(dbKey => !foreignKeys.find(foreignKey => foreignKey.name === dbKey))
|
||||
.map(dbKey => this.schemaBuilder.dropForeignKeyQuery(table.name, dbKey));
|
||||
@ -168,7 +175,7 @@ export class SchemaCreator {
|
||||
* Creates foreign keys which does not exist in the table yet.
|
||||
*/
|
||||
private createForeignKeys(table: TableMetadata, foreignKeys: ForeignKeyMetadata[]) {
|
||||
return this.schemaBuilder.getTableForeignQuery(table).then(dbKeys => {
|
||||
return this.schemaBuilder.getTableForeignQuery(table.name).then(dbKeys => {
|
||||
const dropKeysQueries = foreignKeys
|
||||
.filter(foreignKey => dbKeys.indexOf(foreignKey.name) === -1)
|
||||
.map(foreignKey => this.schemaBuilder.addForeignKeyQuery(foreignKey));
|
||||
@ -200,6 +207,27 @@ export class SchemaCreator {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates indices which are missing in db yet, and drops indices which exist in the db,
|
||||
* but does not exist in the metadata anymore.
|
||||
*/
|
||||
private createIndices(table: TableMetadata, compositeIndices: CompositeIndexMetadata[]) {
|
||||
return this.schemaBuilder.getTableIndicesQuery(table.name).then(tableIndices => {
|
||||
|
||||
// drop all indices that exist in the table, but does not exist in the given composite indices
|
||||
const dropQueries = tableIndices
|
||||
.filter(tableIndex => !compositeIndices.find(compositeIndex => compositeIndex.name === tableIndex.key))
|
||||
.map(tableIndex => this.schemaBuilder.dropIndex(table.name, tableIndex.key));
|
||||
|
||||
// then create table indices for all composite indices we have
|
||||
const addQueries = compositeIndices
|
||||
.filter(compositeIndex => !tableIndices.find(i => i.key === compositeIndex.name))
|
||||
.map(compositeIndex => this.schemaBuilder.createIndex(table.name, compositeIndex.name, compositeIndex.columns));
|
||||
|
||||
return Promise.all([dropQueries, addQueries]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes primary key from the table (if it was before and does not exist in the metadata anymore).
|
||||
*/
|
||||
|
||||
@ -6,8 +6,6 @@ import {EntityMetadataCollection} from "../metadata/collection/EntityMetadataCol
|
||||
|
||||
/**
|
||||
* Broadcaster provides a helper methods to broadcast events to the subscribers.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export class Broadcaster {
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user