chore(database): use naming functions for identifiers in metadata relations

This commit is contained in:
Ben Irvin 2024-02-26 12:27:58 +01:00 committed by GitHub
parent c29dcf0999
commit 3cea9ffa5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 88 additions and 53 deletions

View File

@ -1,9 +1,8 @@
import _ from 'lodash/fp'; import _ from 'lodash/fp';
import * as identifiers from '../utils/identifiers';
import * as types from '../utils/types'; import * as types from '../utils/types';
import { import {
createRelation, createRelation,
getJoinTableName,
isPolymorphic, isPolymorphic,
isBidirectional, isBidirectional,
isAnyToOne, isAnyToOne,
@ -17,7 +16,6 @@ import type { Attribute, Model } from '../types';
export type { Metadata, Meta }; export type { Metadata, Meta };
export { export {
getJoinTableName,
isPolymorphic, isPolymorphic,
isBidirectional, isBidirectional,
isAnyToOne, isAnyToOne,
@ -87,6 +85,6 @@ export const createMetadata = (models: Model[] = []): Metadata => {
}; };
const createAttribute = (attributeName: string, attribute: Attribute) => { const createAttribute = (attributeName: string, attribute: Attribute) => {
const columnName = _.snakeCase(attributeName); const columnName = identifiers.getColumnName(attributeName);
Object.assign(attribute, { columnName }); Object.assign(attribute, { columnName });
}; };

View File

@ -1,5 +1,6 @@
import _ from 'lodash/fp'; import _ from 'lodash/fp';
import * as identifiers from '../utils/identifiers';
import type { Meta, Metadata } from './metadata'; import type { Meta, Metadata } from './metadata';
import type { RelationalAttribute, Relation, MorphJoinTable } from '../types'; import type { RelationalAttribute, Relation, MorphJoinTable } from '../types';
@ -15,6 +16,10 @@ interface JoinTableOptions {
meta: Meta; meta: Meta;
} }
const ID = identifiers.ID_COLUMN;
const ORDER = identifiers.ORDER_COLUMN;
const FIELD = identifiers.FIELD_COLUMN;
const hasInversedBy = ( const hasInversedBy = (
attr: RelationalAttribute attr: RelationalAttribute
): attr is RelationalAttribute & { inversedBy: boolean } => 'inversedBy' in attr; ): attr is RelationalAttribute & { inversedBy: boolean } => 'inversedBy' in attr;
@ -57,9 +62,6 @@ const isOwner = (
const shouldUseJoinTable = (attribute: RelationalAttribute) => const shouldUseJoinTable = (attribute: RelationalAttribute) =>
!('useJoinTable' in attribute) || attribute.useJoinTable !== false; !('useJoinTable' in attribute) || attribute.useJoinTable !== false;
export const getJoinTableName = (tableName: string, attributeName: string) =>
_.snakeCase(`${tableName}_${attributeName}_links`);
export const hasOrderColumn = (attribute: RelationalAttribute) => isAnyToMany(attribute); export const hasOrderColumn = (attribute: RelationalAttribute) => isAnyToMany(attribute);
export const hasInverseOrderColumn = (attribute: RelationalAttribute) => export const hasInverseOrderColumn = (attribute: RelationalAttribute) =>
isBidirectional(attribute) && isManyToAny(attribute); isBidirectional(attribute) && isManyToAny(attribute);
@ -209,8 +211,8 @@ const createManyToMany = (
* set info in the traget * set info in the traget
*/ */
const createMorphToOne = (attributeName: string, attribute: Relation.MorphToOne) => { const createMorphToOne = (attributeName: string, attribute: Relation.MorphToOne) => {
const idColumnName = 'target_id'; const idColumnName = identifiers.getJoinColumnAttributeIdName('target');
const typeColumnName = 'target_type'; const typeColumnName = identifiers.getMorphColumnTypeName('target');
if ('morphColumn' in attribute && attribute.morphColumn) { if ('morphColumn' in attribute && attribute.morphColumn) {
return; return;
@ -225,7 +227,7 @@ const createMorphToOne = (attributeName: string, attribute: Relation.MorphToOne)
}, },
idColumn: { idColumn: {
name: idColumnName, name: idColumnName,
referencedColumn: 'id', referencedColumn: ID,
}, },
}, },
}); });
@ -246,19 +248,19 @@ const createMorphToMany = (
return; return;
} }
const joinTableName = _.snakeCase(`${meta.tableName}_${attributeName}_morphs`); const joinTableName = identifiers.getMorphTableName(meta.tableName, attributeName);
const joinColumnName = identifiers.getMorphColumnJoinTableIdName(meta.singularName);
const idColumnName = identifiers.getMorphColumnAttributeIdName(attributeName);
const typeColumnName = identifiers.getMorphColumnTypeName(attributeName);
const joinColumnName = _.snakeCase(`${meta.singularName}_id`); const fkIndexName = identifiers.getFkIndexName(joinTableName);
const morphColumnName = _.snakeCase(`${attributeName}`);
const idColumnName = `${morphColumnName}_id`;
const typeColumnName = `${morphColumnName}_type`;
metadata.add({ metadata.add({
singularName: joinTableName, singularName: joinTableName,
uid: joinTableName, uid: joinTableName,
tableName: joinTableName, tableName: joinTableName,
attributes: { attributes: {
id: { [ID]: {
type: 'increments', type: 'increments',
}, },
[joinColumnName]: { [joinColumnName]: {
@ -276,10 +278,10 @@ const createMorphToMany = (
[typeColumnName]: { [typeColumnName]: {
type: 'string', type: 'string',
}, },
field: { [FIELD]: {
type: 'string', type: 'string',
}, },
order: { [ORDER]: {
type: 'float', type: 'float',
column: { column: {
unsigned: true, unsigned: true,
@ -288,12 +290,12 @@ const createMorphToMany = (
}, },
indexes: [ indexes: [
{ {
name: `${joinTableName}_fk`, name: fkIndexName,
columns: [joinColumnName], columns: [joinColumnName],
}, },
{ {
name: `${joinTableName}_order_index`, name: `${joinTableName}_order_index`,
columns: ['order'], columns: [ORDER],
}, },
{ {
name: `${joinTableName}_id_column_index`, name: `${joinTableName}_id_column_index`,
@ -302,10 +304,10 @@ const createMorphToMany = (
], ],
foreignKeys: [ foreignKeys: [
{ {
name: `${joinTableName}_fk`, name: fkIndexName,
columns: [joinColumnName], columns: [joinColumnName],
referencedColumns: ['id'], referencedColumns: [ID],
referencedTable: meta.tableName, referencedTable: identifiers.getTableName(meta.tableName),
onDelete: 'CASCADE', onDelete: 'CASCADE',
}, },
], ],
@ -317,7 +319,7 @@ const createMorphToMany = (
name: joinTableName, name: joinTableName,
joinColumn: { joinColumn: {
name: joinColumnName, name: joinColumnName,
referencedColumn: 'id', referencedColumn: ID,
}, },
morphColumn: { morphColumn: {
typeColumn: { typeColumn: {
@ -325,7 +327,7 @@ const createMorphToMany = (
}, },
idColumn: { idColumn: {
name: idColumnName, name: idColumnName,
referencedColumn: 'id', referencedColumn: ID,
}, },
}, },
orderBy: { orderBy: {
@ -390,7 +392,7 @@ const createJoinColum = (metadata: Metadata, { attribute, attributeName }: JoinC
const joinColumnName = _.snakeCase(`${attributeName}_id`); const joinColumnName = _.snakeCase(`${attributeName}_id`);
const joinColumn = { const joinColumn = {
name: joinColumnName, name: joinColumnName,
referencedColumn: 'id', referencedColumn: ID,
referencedTable: targetMeta.tableName, referencedTable: targetMeta.tableName,
}; };
@ -430,30 +432,34 @@ const createJoinTable = (
return; return;
} }
const joinTableName = getJoinTableName(meta.tableName, attributeName); const joinTableName = identifiers.getJoinTableName(meta.tableName, attributeName);
const joinColumnName = _.snakeCase(`${meta.singularName}_id`); const joinColumnName = identifiers.getJoinColumnIdName(meta.singularName);
let inverseJoinColumnName = _.snakeCase(`${targetMeta.singularName}_id`); let inverseJoinColumnName = identifiers.getJoinColumnIdName(targetMeta.singularName);
// if relation is self referencing // if relation is self referencing
if (joinColumnName === inverseJoinColumnName) { if (joinColumnName === inverseJoinColumnName) {
inverseJoinColumnName = `inv_${inverseJoinColumnName}`; inverseJoinColumnName = identifiers.getInverseJoinColumnIdName(targetMeta.singularName);
} }
const orderColumnName = _.snakeCase(`${targetMeta.singularName}_order`); const orderColumnName = identifiers.getOrderColumnName(targetMeta.singularName);
let inverseOrderColumnName = _.snakeCase(`${meta.singularName}_order`); // TODO: should this plus the conditional below be rolled into one method?
let inverseOrderColumnName = identifiers.getOrderColumnName(meta.singularName);
// if relation is self referencing // if relation is self referencing
if (attribute.relation === 'manyToMany' && orderColumnName === inverseOrderColumnName) { if (attribute.relation === 'manyToMany' && orderColumnName === inverseOrderColumnName) {
inverseOrderColumnName = `inv_${inverseOrderColumnName}`; inverseOrderColumnName = identifiers.getInverseOrderColumnName(meta.singularName);
} }
const fkIndexName = identifiers.getFkIndexName(joinTableName); // TODO: joinTableName is multipart, can we re-use those parts instead of shortening the combined string?
const invFkIndexName = identifiers.getInverseFkIndexName(joinTableName);
const metadataSchema: Meta = { const metadataSchema: Meta = {
singularName: joinTableName, singularName: joinTableName,
uid: joinTableName, uid: joinTableName,
tableName: joinTableName, tableName: joinTableName,
attributes: { attributes: {
id: { [ID]: {
type: 'increments', type: 'increments',
}, },
[joinColumnName]: { [joinColumnName]: {
@ -472,32 +478,32 @@ const createJoinTable = (
}, },
indexes: [ indexes: [
{ {
name: `${joinTableName}_fk`, name: fkIndexName,
columns: [joinColumnName], columns: [joinColumnName],
}, },
{ {
name: `${joinTableName}_inv_fk`, name: invFkIndexName,
columns: [inverseJoinColumnName], columns: [inverseJoinColumnName],
}, },
{ {
name: `${joinTableName}_unique`, name: identifiers.getUniqueIndexName(joinTableName),
columns: [joinColumnName, inverseJoinColumnName], columns: [joinColumnName, inverseJoinColumnName],
type: 'unique', type: 'unique',
}, },
], ],
foreignKeys: [ foreignKeys: [
{ {
name: `${joinTableName}_fk`, name: fkIndexName,
columns: [joinColumnName], columns: [joinColumnName],
referencedColumns: ['id'], referencedColumns: [ID],
referencedTable: meta.tableName, referencedTable: meta.tableName, // TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
onDelete: 'CASCADE', onDelete: 'CASCADE',
}, },
{ {
name: `${joinTableName}_inv_fk`, name: invFkIndexName,
columns: [inverseJoinColumnName], columns: [inverseJoinColumnName],
referencedColumns: ['id'], referencedColumns: [ID],
referencedTable: targetMeta.tableName, referencedTable: targetMeta.tableName, // TODO: does this need to be wrapped or do we trust meta.tableName to be the right shortened version?
onDelete: 'CASCADE', onDelete: 'CASCADE',
}, },
], ],
@ -509,11 +515,11 @@ const createJoinTable = (
name: joinTableName, name: joinTableName,
joinColumn: { joinColumn: {
name: joinColumnName, name: joinColumnName,
referencedColumn: 'id', referencedColumn: ID,
}, },
inverseJoinColumn: { inverseJoinColumn: {
name: inverseJoinColumnName, name: inverseJoinColumnName,
referencedColumn: 'id', referencedColumn: ID,
}, },
pivotColumns: [joinColumnName, inverseJoinColumnName], pivotColumns: [joinColumnName, inverseJoinColumnName],
} as any; } as any;
@ -528,7 +534,7 @@ const createJoinTable = (
}, },
}; };
metadataSchema.indexes.push({ metadataSchema.indexes.push({
name: `${joinTableName}_order_fk`, name: identifiers.getOrderFkIndexName(joinTableName), // TODO: should we send joinTableName as parts?
columns: [orderColumnName], columns: [orderColumnName],
}); });
joinTable.orderColumnName = orderColumnName; joinTable.orderColumnName = orderColumnName;
@ -546,7 +552,7 @@ const createJoinTable = (
}; };
metadataSchema.indexes.push({ metadataSchema.indexes.push({
name: `${joinTableName}_order_inv_fk`, name: identifiers.getOrderInverseFkIndexName(joinTableName),
columns: [inverseOrderColumnName], columns: [inverseOrderColumnName],
}); });

View File

@ -38,7 +38,7 @@ export const getNameFromTokens = (tokens: NameToken[], max: number = MAX_DB_IDEN
}; };
// Generic name handler used by all helper functions // Generic name handler used by all helper functions
export const getName = (names: NameInput, options: NameOptions) => { export const getName = (names: NameInput, options: NameOptions = {}) => {
const tokens = _.castArray(names).map((name) => { const tokens = _.castArray(names).map((name) => {
return { return {
name, name,
@ -61,7 +61,6 @@ export const getName = (names: NameInput, options: NameOptions) => {
* TABLES * TABLES
*/ */
// Get a base table name for a model
export const getTableName = (name: string, options?: NameOptions) => { export const getTableName = (name: string, options?: NameOptions) => {
const tokens = [ const tokens = [
{ {
@ -93,7 +92,10 @@ export const getMorphTableName = (collectionName: string, attributeName: string)
* COLUMNS * COLUMNS
*/ */
// Regular relation join tables export const getColumnName = (attributeName: string) => {
return getName(attributeName);
};
export const getJoinColumnEntityIdName = () => { export const getJoinColumnEntityIdName = () => {
return getName(ENTITY, { suffix: 'id' }); return getName(ENTITY, { suffix: 'id' });
}; };
@ -102,7 +104,25 @@ export const getJoinColumnAttributeIdName = (attributeName: string) => {
return getName(attributeName, { suffix: 'id' }); return getName(attributeName, { suffix: 'id' });
}; };
// Morph Join Tables export const getJoinColumnIdName = (singularName: string) => {
return getName(singularName, { suffix: 'id' });
};
export const getInverseJoinColumnIdName = (singularName: string) => {
return getName(singularName, { suffix: 'id', prefix: 'inv' });
};
export const getOrderColumnName = (singularName: string) => {
return getName(singularName, { suffix: 'order' });
};
export const getInverseOrderColumnName = (singularName: string) => {
return getName(singularName, { suffix: 'order', prefix: 'inv' });
};
/**
* Morph Join Tables
*/
export const getMorphColumnJoinTableIdName = (singularName: string) => { export const getMorphColumnJoinTableIdName = (singularName: string) => {
return getName(singularName, { suffix: 'id' }); return getName(singularName, { suffix: 'id' });
}; };
@ -127,6 +147,18 @@ export const getFkIndexName = (names: NameInput) => {
return getName(names, { suffix: 'fk' }); return getName(names, { suffix: 'fk' });
}; };
export const getInverseFkIndexName = (names: NameInput) => {
return getName(names, { suffix: 'inv_fk' });
};
export const getOrderFkIndexName = (names: NameInput) => {
return getName(names, { suffix: 'order_fk' });
};
export const getOrderInverseFkIndexName = (names: NameInput) => {
return getName(names, { suffix: 'order_inv_fk' });
};
export const getUniqueIndexName = (names: NameInput) => { export const getUniqueIndexName = (names: NameInput) => {
return getName(names, { suffix: 'unique' }); return getName(names, { suffix: 'unique' });
}; };

View File

@ -1,7 +1,6 @@
import { getJoinTableName } from '../../metadata';
import type { Database } from '../..'; import type { Database } from '../..';
import type { Relation } from '../../types'; import type { Relation } from '../../types';
import { getJoinTableName } from '../../utils/identifiers';
type Link = { type Link = {
relation: Relation.Bidirectional & { inversedBy: string }; relation: Relation.Bidirectional & { inversedBy: string };