Skip to content

Commit 8702c18

Browse files
committed
only allow list one level deep of property type
it is not possible to define property on a node label/ relationship type as a list of list, only simple types and a homogeneous array of the same simple types are allowed. https://neo4j.com/docs/cypher-manual/current/values-and-types/property-structural-constructed/
1 parent 7fa1bf1 commit 8702c18

File tree

8 files changed

+118
-39
lines changed

8 files changed

+118
-39
lines changed

.changeset/fluffy-spoons-camp.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@neo4j/graph-schema-utils": patch
3+
"@neo4j/graph-introspection": patch
4+
---
5+
6+
removed recursive property type

packages/graph-schema-utils/src/formatters/json/extensions.ts

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
Property,
99
PropertyArrayType,
1010
PropertyBaseType,
11-
PropertyTypeRecursive,
11+
PropertyType,
1212
RelationshipObjectType,
1313
RelationshipType,
1414
RelationshipTypeConstraint,
@@ -18,10 +18,16 @@ import {
1818
isNodeLabelIndex,
1919
isRelationshipTypeConstraint,
2020
isRelationshipTypeIndex,
21+
isPropertyTypeList,
22+
isPropertyBaseType,
23+
isPropertyArrayType,
2124
} from "../../model/index.js";
2225
import {
2326
ConstraintJsonStruct,
2427
IndexJsonStruct,
28+
isPrimitivePropertyTypesArrayTypeJsonStruct,
29+
isPrimitivePropertyTypesTypeJsonStruct,
30+
isPropertyTypeListJsonStruct,
2531
LookupIndexJsonStruct,
2632
NodeLabelConstraintJsonStruct,
2733
NodeLabelIndexJsonStruct,
@@ -30,7 +36,7 @@ import {
3036
PrimitivePropertyTypesArrayType,
3137
PrimitivePropertyTypesType,
3238
PropertyJsonStruct,
33-
PropertyTypeJsonStructRecrsive,
39+
PropertyTypeJsonStruct,
3440
RelationshipObjectTypeJsonStruct,
3541
RelationshipTypeConstraintJsonStruct,
3642
RelationshipTypeIndexJsonStruct,
@@ -470,24 +476,36 @@ const property = {
470476
};
471477

472478
const propertyType = {
473-
extract: (pt: PropertyTypeRecursive): PropertyTypeJsonStructRecrsive => {
474-
if (Array.isArray(pt)) {
475-
return pt.map(propertyType.extract);
479+
extract: (pt: PropertyType): PropertyTypeJsonStruct => {
480+
if (isPropertyTypeList(pt)) {
481+
return pt.map((p) => {
482+
if (isPropertyBaseType(p)) {
483+
return propertyBaseType.extract(p);
484+
} else if (isPropertyArrayType(p)) {
485+
return propertyArrayType.extract(p);
486+
}
487+
throw Error(`Unknown property type in list ${p}`);
488+
});
476489
}
477-
if (pt instanceof PropertyBaseType) {
490+
if (isPropertyBaseType(pt)) {
478491
return propertyBaseType.extract(pt);
479-
} else if (pt instanceof PropertyArrayType) {
492+
} else if (isPropertyArrayType(pt)) {
480493
return propertyArrayType.extract(pt);
481494
}
482495
throw new Error(`Unknown property type ${pt}`);
483496
},
484-
create: (
485-
propertyTypeJson: PropertyTypeJsonStructRecrsive
486-
): PropertyTypeRecursive => {
487-
if (Array.isArray(propertyTypeJson)) {
488-
return propertyTypeJson.map((pt) => propertyType.create(pt));
497+
create: (propertyTypeJson: PropertyTypeJsonStruct): PropertyType => {
498+
if (isPropertyTypeListJsonStruct(propertyTypeJson)) {
499+
return propertyTypeJson.map((pt) => {
500+
if (isPrimitivePropertyTypesTypeJsonStruct(pt)) {
501+
return propertyBaseType.create(pt);
502+
} else if (isPrimitivePropertyTypesArrayTypeJsonStruct(pt)) {
503+
return propertyArrayType.create(pt.items.type);
504+
}
505+
throw new Error(`Unknown property type in list ${pt}`);
506+
});
489507
}
490-
if (propertyTypeJson.type === "array") {
508+
if (isPrimitivePropertyTypesArrayTypeJsonStruct(propertyTypeJson)) {
491509
return propertyArrayType.create(propertyTypeJson.items.type);
492510
}
493511
return propertyBaseType.create(propertyTypeJson);

packages/graph-schema-utils/src/formatters/json/parse.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from "path";
33
import { readFile } from "../../../test/fs.utils.js";
44
import { describe, test } from "vitest";
55
import { fromJson } from "./index.js";
6-
import { PropertyTypes } from "../../model/index.js";
6+
import { PropertyArrayType, PropertyBaseType } from "../../model/index.js";
77

88
import { validateSchema } from "../../validation.js";
99

@@ -66,7 +66,11 @@ describe("Parser tests", () => {
6666
"roles"
6767
);
6868
assert.strictEqual(
69-
(parsed.relationshipTypes[0].properties[0].type as PropertyTypes).type,
69+
(
70+
parsed.relationshipTypes[0].properties[0].type as
71+
| PropertyBaseType
72+
| PropertyArrayType
73+
).type,
7074
"array"
7175
);
7276
});

packages/graph-schema-utils/src/formatters/json/types.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export type LookupIndexJsonStruct = IndexJsonStruct & {
105105
export type PropertyJsonStruct = {
106106
$id: string;
107107
token: string;
108-
type: PropertyTypeJsonStructRecrsive;
108+
type: PropertyTypeJsonStruct;
109109
nullable: boolean;
110110
};
111111

@@ -115,11 +115,27 @@ export type PrimitivePropertyTypesArrayType = {
115115
items: { type: PrimitivePropertyTypes };
116116
};
117117

118+
export type PropertyTypeListJsonStruct = (PrimitivePropertyTypesType | PrimitivePropertyTypesArrayType)[]
119+
118120
export type PropertyTypeJsonStruct =
119121
| PrimitivePropertyTypesArrayType
120122
| PrimitivePropertyTypesType
121-
| (PrimitivePropertyTypesType | PrimitivePropertyTypesArrayType)[];
122-
123-
export type PropertyTypeJsonStructRecrsive =
124-
| PropertyTypeJsonStruct
125-
| Array<PropertyTypeJsonStruct | PropertyTypeJsonStructRecrsive[]>;
123+
| PropertyTypeListJsonStruct;
124+
125+
export const isPropertyTypeListJsonStruct = (
126+
propertyType: PropertyTypeJsonStruct
127+
): propertyType is PropertyTypeListJsonStruct => {
128+
return Array.isArray(propertyType);
129+
}
130+
131+
export const isPrimitivePropertyTypesTypeJsonStruct = (
132+
propertyType: PropertyTypeJsonStruct
133+
): propertyType is PrimitivePropertyTypesType => {
134+
return typeof propertyType === "object" && "type" in propertyType && propertyType.type !== "array";
135+
}
136+
137+
export const isPrimitivePropertyTypesArrayTypeJsonStruct = (
138+
propertyType: PropertyTypeJsonStruct
139+
): propertyType is PrimitivePropertyTypesArrayType => {
140+
return typeof propertyType === "object" && "type" in propertyType && propertyType.type === "array" && propertyType.items !== undefined;
141+
}

packages/graph-schema-utils/src/formatters/llm-prompt/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import {
22
GraphSchema,
33
NodeObjectType,
44
PropertyBaseType,
5-
PropertyTypeRecursive,
6-
PropertyTypes,
5+
PropertyType,
76
RelationshipObjectType,
87
} from "../../model/index.js";
98
import { ElementPropertyObject } from "./types.js";
@@ -141,7 +140,7 @@ export function toOskars(schema: GraphSchema): string {
141140
return out.join("\n");
142141
}
143142

144-
function formatPropertyType(type: PropertyTypeRecursive): string {
143+
function formatPropertyType(type: PropertyType): string {
145144
if (Array.isArray(type)) {
146145
return type.map(formatPropertyType).join("|");
147146
}

packages/graph-schema-utils/src/model/index.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,20 +295,35 @@ export type IndexType =
295295

296296
export type EntityType = "node" | "relationship";
297297

298-
export type PropertyTypes = PropertyBaseType | PropertyArrayType;
299-
export type PropertyTypeRecursive =
300-
| PropertyTypes
301-
| Array<PropertyTypes | PropertyTypeRecursive[]>;
298+
export type PropertyType =
299+
| PropertyBaseType
300+
| PropertyArrayType
301+
| PropertyTypeList;
302+
303+
export type PropertyTypeList = (PropertyBaseType | PropertyArrayType)[];
304+
305+
export const isPropertyBaseType = (p: PropertyType): p is PropertyBaseType => {
306+
return p instanceof PropertyBaseType;
307+
}
308+
309+
export const isPropertyArrayType = (p: PropertyType): p is PropertyArrayType => {
310+
return p instanceof PropertyArrayType;
311+
}
312+
313+
export const isPropertyTypeList = (p: PropertyType): p is PropertyTypeList => {
314+
return Array.isArray(p);
315+
}
316+
302317
export class Property {
303318
$id: string;
304319
token: string;
305-
type: PropertyTypeRecursive;
320+
type: PropertyType;
306321
nullable: boolean;
307322

308323
constructor(
309324
$id: string,
310325
token: string,
311-
type: PropertyTypeRecursive,
326+
type: PropertyType,
312327
nullable: boolean
313328
) {
314329
this.$id = $id;

packages/graph-schema-utils/test/model/programatical.test.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import { strict as assert } from "node:assert";
22
import { describe, expect, test } from "vitest";
33
import { model } from "../../src/index.js";
44
import {
5-
PropertyTypes,
5+
PropertyArrayType,
6+
PropertyBaseType,
7+
PropertyType,
68
isLookupIndex,
79
isNodeLabelConstraint,
810
isNodeLabelIndex,
@@ -147,8 +149,9 @@ describe("Programatic model tests", () => {
147149
);
148150
assert.strictEqual(
149151
(
150-
graphSchema.relationshipObjectTypes[0].type.properties[0]
151-
.type as PropertyTypes
152+
graphSchema.relationshipObjectTypes[0].type.properties[0].type as
153+
| PropertyBaseType
154+
| PropertyArrayType
152155
).type,
153156
"array"
154157
);
@@ -165,7 +168,11 @@ describe("Programatic model tests", () => {
165168
assert.strictEqual(graphSchema.constraints[0].nodeLabel, labels[0]);
166169
}
167170
assert.strictEqual(
168-
(graphSchema.constraints[0].properties[0].type as PropertyTypes).type,
171+
(
172+
graphSchema.constraints[0].properties[0].type as
173+
| PropertyBaseType
174+
| PropertyArrayType
175+
).type,
169176
"string"
170177
);
171178
assert.strictEqual(graphSchema.constraints[1].name, "existence");
@@ -187,7 +194,11 @@ describe("Programatic model tests", () => {
187194
assert.strictEqual(graphSchema.indexes[0].nodeLabel, labels[0]);
188195
assert.strictEqual(graphSchema.indexes[0].properties.length, 1);
189196
assert.strictEqual(
190-
(graphSchema.indexes[0].properties[0].type as PropertyTypes).type,
197+
(
198+
graphSchema.indexes[0].properties[0].type as
199+
| PropertyBaseType
200+
| PropertyArrayType
201+
).type,
191202
"string"
192203
);
193204
}
@@ -204,7 +215,11 @@ describe("Programatic model tests", () => {
204215
);
205216
assert.strictEqual(graphSchema.indexes[1].properties.length, 1);
206217
assert.strictEqual(
207-
(graphSchema.indexes[1].properties[0].type as PropertyTypes).type,
218+
(
219+
graphSchema.indexes[1].properties[0].type as
220+
| PropertyBaseType
221+
| PropertyArrayType
222+
).type,
208223
"array"
209224
);
210225
}

packages/introspection/src/index.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,16 @@ async function introspectNodes(
6161
Neo4jPropertyType | Neo4jPropertyArrayType
6262
>;
6363

64-
const types = neo4jTypes.map(createPropertyInstance);
64+
const types: (model.PropertyBaseType | model.PropertyArrayType)[] =
65+
neo4jTypes.map(createPropertyInstance);
66+
67+
const propertyType: model.PropertyType =
68+
types.length === 1 ? types.pop() : types;
6569

6670
return new model.Property(
6771
`${nl}_${p.propertyName}`,
6872
p.propertyName,
69-
types.length > 1 ? types : types.pop(),
73+
propertyType,
7074
!p.mandatory
7175
);
7276
});
@@ -125,11 +129,13 @@ async function introspectRelationships(
125129
>;
126130

127131
const types = neo4jTypes.map(createPropertyInstance);
132+
const propertyType: model.PropertyType =
133+
types.length === 1 ? types.pop() : types;
128134

129135
return new model.Property(
130136
`${relType}_${p.propertyName}`,
131137
p.propertyName,
132-
types.length > 1 ? types : types.pop(),
138+
propertyType,
133139
!p.mandatory
134140
);
135141
});

0 commit comments

Comments
 (0)