Propose targettype keyword for associations#8
Propose targettype keyword for associations#8Fannon wants to merge 1 commit intojson-structure:mainfrom
Conversation
Signed-off-by: Simon Heimler <simon.heimler@sap.com>
| `$ref` is NOT permitted in other attributes and MUST NOT be used inside the | ||
| `type` of the root object. | ||
|
|
||
| ### `targettype` Keyword {#targettype-keyword} |
There was a problem hiding this comment.
Other possible keyword names:
| ### `targettype` Keyword {#targettype-keyword} | |
| ### `target` Keyword {#targettype-keyword} |
| ### `targettype` Keyword {#targettype-keyword} | |
| ### `targetobject` Keyword {#targettype-keyword} |
There was a problem hiding this comment.
I agree with the direction of this, but I would make this broader:
For objects and tuples, I would add a new keyword "relations" that is similar to "properties" but specifically defines relationship properties. "relations" and "properties" share a namespace, so they can't define conflicting names.
The "relations" declarations are not modeled as types, because they must be able to cross-reference properties within the same tuple/object. They are similar to properties in that they are represented just like properties in the instances.
A relation is a named object (via the relations map) that has two properties:
- the
targettypedeclares the target type that the relation refers to - the
cardinalitydeclares whether the relationship point to one or more targets
the targettype's identity declaration functions like the tuple keyword for establishing the type of references, meaning that if the target's identity clause references two properties, the reference value in the relation source is a tuple-encoded list of values matching that identity.
An instance of a single relation is an object with the following properties:
"ref" : a JSON pointer to the target object
"identity": a tuple that reflects the target object identity values
A relationship MAY be established either through a direct link (ref) or through an identity match against all known instances of the target type. If both properties are defined, the identity match is performed against all instances of the target type that exists within the scope of the ref json pointer, i.e. are children of the identified node.
the value of relations with multiple cardinality is an array of such objects. the value of relations with single cardinality is a single such object.
{
"$schema": "https://json-structure.org/meta/core/v0/#",
"$id": "https://example.com/library.schema",
"$root": "#/definitions/Library/Document",
"definitions": {
"Library": {
/* 1 ───────── AUTHOR ───────── */
"Author": {
"type": "object",
"name": "Author",
"properties": {
"id": { "type": "uuid" },
"name": { "type": "string" }
},
"required": ["id", "name"],
"identity": ["id"]
},
/* 2 ──────── PUBLISHER ─────── */
"Publisher": {
"type": "object",
"name": "Publisher",
"properties": {
"id": { "type": "uuid" },
"name": { "type": "string" },
"city": { "type": "string" }
},
"required": ["id", "name"],
"identity": ["id"]
},
/* 3 ────────── BOOK ─────────── */
"Book": {
"type": "object",
"name": "Book",
/* intrinsic fields only — reference
properties are implicit via `relations` */
"properties": {
"isbn": { "type": "string" },
"title": { "type": "string" }
},
"required": ["isbn", "title"],
"identity": ["isbn"],
"relations": {
/* many Authors per Book */
"authors": {
"cardinality": "multiple",
"targettype": { "$ref": "#/definitions/Library/Author" }
},
/* exactly one Publisher per Book */
"publisher": {
"cardinality": "single",
"targettype": { "$ref": "#/definitions/Library/Publisher" }
}
}
},
/* 4 ──────── DOCUMENT ROOT ─────── */
"Document": {
"type": "object",
"name": "LibraryDocument",
"properties": {
/* list of all authors present in this file */
"authors": {
"type": "array",
"items": { "$ref": "#/definitions/Library/Author" }
},
/* list of all publishers */
"publishers": {
"type": "array",
"items": { "$ref": "#/definitions/Library/Publisher" }
},
/* list of all books */
"books": {
"type": "array",
"items": { "$ref": "#/definitions/Library/Book" }
}
}
}
}
}
}
Instance:
{
"$schema": "https://example.com/library.schema",
"authors": [
{
"id": "3aad369c-1bfb-11e5-9a21-1697f925ec7b",
"name": "Brian W. Kernighan"
},
{
"id": "b3e0fac1-603f-4960-8646-90b053b6af19",
"name": "Dennis M. Ritchie"
}
],
"publishers": [
{
"id": "9609d302-62fb-4d0a-9e71-86f744e3022c",
"name": "Prentice Hall"
}
],
"books": [
{
"isbn": "978-0131103627",
"title": "The C Programming Language",
"authors": [
{
"ref": "#/authors/0",
"identity": ["3aad369c-1bfb-11e5-9a21-1697f925ec7b"]
},
{
"ref": "#/authors/1",
"identity": ["b3e0fac1-603f-4960-8646-90b053b6af19"]
}
],
"publisher": {
"ref": "#/publishers/0",
"identity": ["9609d302-62fb-4d0a-9e71-86f744e3022c"]
}
}
]
}
I explicitly made the relation instance an object, because that gives us the opportunity to declare a qualifier type in the relation which defines/references a type to qualify the relationship further, equivalent to link properties in a graph.
Example:
qualifier type:
"AuthorRole": {
"type": "string",
"enum": ["Author", "Editor", "Illustrator", "Translator"]
}
extend the relation:
"authors": {
"targettype": { "$ref": "#/definitions/Library/Author" },
"cardinality": "multiple",
/* NEW – the schema that qualifies the link *
* Every relation-instance object MUST carry a `qualifier` *
* that validates against this type. */
"qualifiertype": { "$ref": "#/definitions/Library/AuthorRole" }
}
Instance with qualifiers:
{
"$schema": "https://example.com/library.schema",
"authors": [
{ "id": "3aad369c-1bfb-11e5-9a21-1697f925ec7b", "name": "Brian W. Kernighan" },
{ "id": "b3e0fac1-603f-4960-8646-90b053b6af19", "name": "Dennis M. Ritchie" }
],
"publishers": [
{ "id": "9609d302-62fb-4d0a-9e71-86f744e3022c", "name": "Prentice Hall" }
],
"books": [
{
"isbn": "978-0131103627",
"title": "The C Programming Language",
"authors": [
{
"ref": "#/authors/0",
"identity": ["3aad369c-1bfb-11e5-9a21-1697f925ec7b"],
"qualifier": "Author" // ← validated by AuthorRole enum
},
{
"ref": "#/authors/1",
"identity": ["b3e0fac1-603f-4960-8646-90b053b6af19"],
"qualifier": "Author"
}
],
"publisher": {
"ref": "#/publishers/0",
"identity": ["9609d302-62fb-4d0a-9e71-86f744e3022c"]
/* no qualifier here because the relation *
* lacks a `qualifiertype` in the declaration. */
}
}
]
}
There was a problem hiding this comment.
I like where this is going, thanks for creating this example!
Moving it to its own relation property as an array of object seems like a really good idea.
Where I'm unsure is if we can assume that the instances are structured like you propose. I see how it's nice to make each reference an object and for convenience also adding a "ref", but in many real data cases I've seen the references are just a string property or an array of strings. The concept also needs to work with this. So the "reference" in the schema has to point to the property that carries the ID of the reference, too?
Let me get back on this to you when I have more time to think it through.
| } | ||
| ~~~ | ||
|
|
||
| The `targettype` MUST only be used on object properties. |
There was a problem hiding this comment.
You clarified it in another comment.
| The `targettype` MUST only be used on object properties. | |
| The `targettype` is optional, but only applicable for object or tuple properties. |
|
I think identities and relations would be a good companion spec. |
Relates #4
Builds upon #7
Adds the
targettypekeyword to indicate associations / references / pointers within the model.