Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(er): add multiple key constraints #4030

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions demos/er.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,44 @@
</pre>
<hr />

<pre class="mermaid">
erDiagram
"HOSPITAL" {
int id PK
int doctor_id PK, FK
string address UK
string name
string phone_number
string fax_number
}
</pre
>
<hr />

<pre class="mermaid">
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string registrationNumber PK
string make
string model
string[] parts
}
PERSON ||--o{ NAMED-DRIVER : is
PERSON {
string driversLicense PK "The license #"
string(99) firstName "Only 99 characters are allowed"
string lastName
string phone UK
int age
}
NAMED-DRIVER {
string carRegistrationNumber PK, FK
string driverLicence PK,FK
}
MANUFACTURER only one to zero or more CAR : makes
</pre>

<script src="./mermaid.js"></script>
<script type="module">
mermaid.initialize({
Expand Down
22 changes: 15 additions & 7 deletions docs/syntax/entityRelationshipDiagram.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,13 @@ The `type` and `name` values must begin with an alphabetic character and may con

#### Attribute Keys and Comments

Attributes may also have a `key` or comment defined. Keys can be "PK", "FK" or "UK", for Primary Key, Foreign Key or Unique Key. And a `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them.
Attributes may also have a `key` or comment defined. Keys can be `PK`, `FK` or `UK`, for Primary Key, Foreign Key or Unique Key. To specify multiple key constraints on a single attribute, separate them with a comma (e.g., `PK, FK`).. A `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them.

```mermaid-example
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string allowedDriver FK "The license of the allowed driver"
string registrationNumber UK
string registrationNumber PK
string make
string model
string[] parts
Expand All @@ -217,17 +216,21 @@ erDiagram
string driversLicense PK "The license #"
string(99) firstName "Only 99 characters are allowed"
string lastName
string phone UK
int age
}
MANUFACTURER only one to zero or more CAR
NAMED-DRIVER {
string carRegistrationNumber PK, FK
string driverLicence PK, FK
}
MANUFACTURER only one to zero or more CAR : makes
```

```mermaid
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string allowedDriver FK "The license of the allowed driver"
string registrationNumber UK
string registrationNumber PK
string make
string model
string[] parts
Expand All @@ -237,9 +240,14 @@ erDiagram
string driversLicense PK "The license #"
string(99) firstName "Only 99 characters are allowed"
string lastName
string phone UK
int age
}
MANUFACTURER only one to zero or more CAR
NAMED-DRIVER {
string carRegistrationNumber PK, FK
string driverLicence PK, FK
}
MANUFACTURER only one to zero or more CAR : makes
```

### Other Things
Expand Down
7 changes: 5 additions & 2 deletions packages/mermaid/src/diagrams/er/erRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {

// Check to see if any of the attributes has a key or a comment
attributes.forEach((item) => {
if (item.attributeKeyType !== undefined) {
if (item.attributeKeyTypeList !== undefined && item.attributeKeyTypeList.length > 0) {
hasKeyType = true;
}

Expand Down Expand Up @@ -112,6 +112,9 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
nodeHeight = Math.max(typeBBox.height, nameBBox.height);

if (hasKeyType) {
const keyTypeNodeText =
item.attributeKeyTypeList !== undefined ? item.attributeKeyTypeList.join(',') : '';

const keyTypeNode = groupNode
.append('text')
.classed('er entityLabel', true)
Expand All @@ -122,7 +125,7 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
.style('text-anchor', 'left')
.style('font-family', getConfig().fontFamily)
.style('font-size', attrFontSize + 'px')
.text(item.attributeKeyType || '');
.text(keyTypeNodeText);

attributeNode.kn = keyTypeNode;
const keyTypeBBox = keyTypeNode.node().getBBox();
Expand Down
11 changes: 9 additions & 2 deletions packages/mermaid/src/diagrams/er/parser/erDiagram.jison
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
\"[^"]*\" return 'WORD';
"erDiagram" return 'ER_DIAGRAM';
"{" { this.begin("block"); return 'BLOCK_START'; }
<block>"," return 'COMMA';
<block>\s+ /* skip whitespace in block */
<block>\b((?:PK)|(?:FK)|(?:UK))\b return 'ATTRIBUTE_KEY'
<block>(.*?)[~](.*?)*[~] return 'ATTRIBUTE_WORD';
Expand Down Expand Up @@ -131,11 +132,12 @@ attributes

attribute
: attributeType attributeName { $$ = { attributeType: $1, attributeName: $2 }; }
| attributeType attributeName attributeKeyType { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3 }; }
| attributeType attributeName attributeKeyTypeList { $$ = { attributeType: $1, attributeName: $2, attributeKeyTypeList: $3 }; }
| attributeType attributeName attributeComment { $$ = { attributeType: $1, attributeName: $2, attributeComment: $3 }; }
| attributeType attributeName attributeKeyType attributeComment { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3, attributeComment: $4 }; }
| attributeType attributeName attributeKeyTypeList attributeComment { $$ = { attributeType: $1, attributeName: $2, attributeKeyTypeList: $3, attributeComment: $4 }; }
;


attributeType
: ATTRIBUTE_WORD { $$=$1; }
;
Expand All @@ -144,6 +146,11 @@ attributeName
: ATTRIBUTE_WORD { $$=$1; }
;

attributeKeyTypeList
: attributeKeyType { $$ = [$1]; }
| attributeKeyTypeList COMMA attributeKeyType { $1.push($3); $$ = $1; }
;

attributeKeyType
: ATTRIBUTE_KEY { $$=$1; }
;
Expand Down
22 changes: 22 additions & 0 deletions packages/mermaid/src/diagrams/er/parser/erDiagram.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,28 @@ describe('when parsing ER diagram it...', function () {
expect(entities[entity].attributes.length).toBe(4);
});

it('should allow an entity with attributes that have many constraints and comments', function () {
const entity = 'CUSTOMER';
const attribute1 = 'int customer_number PK, FK "comment1"';
const attribute2 = 'datetime customer_status_start_datetime PK,UK, FK';
const attribute3 = 'datetime customer_status_end_datetime PK , UK "comment3"';
const attribute4 = 'string customer_firstname';
const attribute5 = 'string customer_lastname "comment5"';

erDiagram.parser.parse(
`erDiagram\n${entity} {\n${attribute1}\n${attribute2}\n${attribute3}\n${attribute4}\n${attribute5}\n}`
);
const entities = erDb.getEntities();
expect(entities[entity].attributes[0].attributeKeyTypeList).toEqual(['PK', 'FK']);
expect(entities[entity].attributes[0].attributeComment).toBe('comment1');
expect(entities[entity].attributes[1].attributeKeyTypeList).toEqual(['PK', 'UK', 'FK']);
expect(entities[entity].attributes[2].attributeKeyTypeList).toEqual(['PK', 'UK']);
expect(entities[entity].attributes[2].attributeComment).toBe('comment3');
expect(entities[entity].attributes[3].attributeKeyTypeList).toBeUndefined();
expect(entities[entity].attributes[4].attributeKeyTypeList).toBeUndefined();
expect(entities[entity].attributes[4].attributeComment).toBe('comment5');
});

it('should allow an entity with attribute that has a generic type', function () {
const entity = 'BOOK';
const attribute1 = 'type~T~ type';
Expand Down
12 changes: 8 additions & 4 deletions packages/mermaid/src/docs/syntax/entityRelationshipDiagram.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,13 @@ The `type` and `name` values must begin with an alphabetic character and may con

#### Attribute Keys and Comments

Attributes may also have a `key` or comment defined. Keys can be "PK", "FK" or "UK", for Primary Key, Foreign Key or Unique Key. And a `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them.
Attributes may also have a `key` or comment defined. Keys can be `PK`, `FK` or `UK`, for Primary Key, Foreign Key or Unique Key. To specify multiple key constraints on a single attribute, separate them with a comma (e.g., `PK, FK`).. A `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them.

```mermaid-example
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string allowedDriver FK "The license of the allowed driver"
string registrationNumber UK
string registrationNumber PK
string make
string model
string[] parts
Expand All @@ -163,9 +162,14 @@ erDiagram
string driversLicense PK "The license #"
string(99) firstName "Only 99 characters are allowed"
string lastName
string phone UK
int age
}
MANUFACTURER only one to zero or more CAR
NAMED-DRIVER {
string carRegistrationNumber PK, FK
string driverLicence PK, FK
}
MANUFACTURER only one to zero or more CAR : makes
```

### Other Things
Expand Down