Skip to content

Commit

Permalink
feat(Relationships): Reduce relationship lines by combining relations…
Browse files Browse the repository at this point in the history
…hip names to and from end model, then skipping end model. Fix issue with misrepresented one/one ot zero/one relationships (#237)

Known issue with many to X relationships.
  • Loading branch information
keonik committed Oct 9, 2023
1 parent 3c921f1 commit f1aa362
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 16 deletions.
2 changes: 1 addition & 1 deletion ERD.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion __tests__/issues/124.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ test('Zero to many relationship', async () => {
// User has zero to many posts
expect(svgContent).toContain('author');
expect(svgContent).toContain('marker-start="url(#ZERO_OR_MORE_START)"');
expect(svgContent).toContain('marker-end="url(#ZERO_OR_ONE_END)"');
expect(svgContent).toContain('marker-end="url(#ZERO_OR_MORE_END)"');
});
2 changes: 1 addition & 1 deletion __tests__/issues/138.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ test('id not override before key name', async () => {
/id="text-entity-UserSetting([^\><]*)-attr-\d-name"([^<\>]*)\>user_id<\/text\>/
);
// UserSetting has a relation to User
expect(svgContent).toMatch(/<text([^><]*)\>user<\/text\>/);
expect(svgContent).toMatch(/<text([^><]*)\>userSettings \/ user<\/text\>/);
// expect(svgContent).toContain('>user</text>');
});
25 changes: 25 additions & 0 deletions prisma/issues/219.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
generator erd {
provider = "node ./dist/index.js"
output = "../../__tests__/219.svg"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model DailySchedule {
id Int @id @default(autoincrement())
day String
startTime String
endTime String
schedule Schedule @relation(fields: [scheduleId], references: [id])
scheduleId Int
}

model Schedule {
id Int @id @default(autoincrement())
name String
timezone String
dailySchedules DailySchedule[]
}
63 changes: 50 additions & 13 deletions src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,19 +135,37 @@ ${
.join('\n\n');

let relationships = '';
let skipModels: string[] = [];
// https://mermaid.js.org/syntax/entityRelationshipDiagram.html#relationship-syntax
/*
Value (left) Value (right) Meaning
|o o| Zero or one
|| || Exactly one
}o o{ Zero or more (no upper limit)
}| |{ One or more (no upper limit)
*/
for (const model of modellikes) {
for (const field of model.fields) {
const isEnum = field.kind === 'enum';
if (isEnum && (tableOnly || ignoreEnums)) {
continue;
}

const relationshipName = `${isEnum ? 'enum:' : ''}${field.name}`;
const thisSide = `"${model.dbName || model.name}"`;
const otherSide = `"${
if (field.relationName) {
// skip models that have already been processed
if (skipModels.includes(field.relationName)) {
continue;
} else if (field.relationName) {
// add to skip list
skipModels.push(field.relationName);
}
}

const thisSide = model.dbName || model.name;
const otherSide =
modellikes.find((ml) => ml.name === field.type)?.dbName ||
field.type
}"`;
field.type;
const relationshipName = `${isEnum ? 'enum:' : ''}${field.name}`;
// normal relations
if (
(field.relationFromFields &&
Expand Down Expand Up @@ -175,20 +193,37 @@ ${
} else if (!otherField?.isRequired) {
thisSideMultiplicity = 'o|';
}

relationships += ` ${thisSide} ${thisSideMultiplicity}--${otherSideMultiplicity} ${
const otherSideRelationName = otherField?.name
? ` / ${otherField.name}`
: '';
relationships += ` "${thisSide}" ${thisSideMultiplicity}--${otherSideMultiplicity} "${
otherModel?.dbName || otherSide
} : "${relationshipName}"\n`;
}" : "${relationshipName + otherSideRelationName}"\n`;
}
// many to many
else if (
modellikes.find(
(m) => m.name === field.type || m.dbName === field.type
) &&
field.relationFromFields?.length === 0
// && field.relationToFields?.length
) {
relationships += ` ${thisSide} o{--}o ${otherSide} : "${field.name}"\n`;
const otherSideRelationName =
modellikes
.find(
(m) =>
m.name === field.type || m.dbName === field.type
)
?.fields.find((f) => f.type === model.name)?.name ?? '';
console.log(
'many to many relationship',
field.name,
otherSideRelationName
);
relationships += ` ${thisSide} o{--}o ${otherSide} : "${
field.name
}${
otherSideRelationName ? ' / ' + otherSideRelationName : ''
}"\n`;
}
// composite types
else if (field.kind == 'object') {
Expand All @@ -198,7 +233,7 @@ ${
.replace(/^_/, 'z_') // replace leading underscores
.replace(/\s/g, '') // remove spaces === otherSide
);
console.log(otherSide, otherSideCompositeType);

if (otherSideCompositeType) {
// most logic here is a copy/paste from the normal relation logic
// TODO extract and reuse
Expand All @@ -220,10 +255,12 @@ ${
} else if (!otherField?.isRequired) {
thisSideMultiplicity = 'o|';
}

const otherSideRelationName = otherField?.name
? ` / ${otherField.name}`
: '';
relationships += ` ${thisSide} ${thisSideMultiplicity}--${otherSideMultiplicity} ${
otherSideCompositeType.dbName || otherSide
} : "${relationshipName}"\n`;
} : "${relationshipName}${otherSideRelationName}"\n`;
}
}
}
Expand Down

0 comments on commit f1aa362

Please sign in to comment.