-
Notifications
You must be signed in to change notification settings - Fork 8
Proposal: Named Node Expressions
The named node expression proposal is a proposal made to extend the Turtle language in order to make certain Turtle expressions more compact and referenceable. It is an alternative that is intended to address several critical use cases currently under consideration by the RDF-Star Working Group. It should have no impact upon the underlying RDF.
An anonymous node is a node in a graph that is created via notational means (e.g., part of a brace expression "[]", linked list expression "()", reification expression "<<>>" or graph expression "{}"). Anonymous nodes are not directly referenceable even locally, and while they are usually rendered as blank node in RDF, in Turtle there is no way to explicitly name such nodes without losing the appropriate advantages of the notation.
A named blank node is a node in a graph that uses bnode notation to define a name that only has scope within a given Turtle file depiction of a graph, and that typically assigned as a bnode URI by the parser when the Turtle file is parsed. In this respect, named blank nodes are locally scoped. An anonymous node is not synonymous with a named blank node, though most anonymous nodes in Turtle are representable as blank nodes in RDF. An anonymous node is not referenceable at all within Turtle from outside the data structure, while a named blank node can be, so long as it is within the scope of a graph when encoded in Turtle.
A named node identifier is either a URI or blank node used by a named node expression to create a referenceable name for an anonymous node.
A named node expression is a mechanism in Turtle to assign a named node identifier to an anonymous node, where the name can be either a fully qualified IRI or a named blank node, when the Turtle is parsed.
## BNF Notation
<namedNodeExpression> ::= <op> <namedNode> "=>" <partialExpression> <endOp>
<op> ::= "[" | "(" | "<<" | "{"
<namedNode> ::= <iri> | <blankNode>
; :partialExpression is an expression dependent upon <op>, see below
<endOp> ::= "]" | ")" | ">>" | "}"
An expansion is a translation from a Turtle-star form to an RDF form, in which all anonymous nodes have either been named or assigned node identifiers by the system if un-named (the default behaviour in the absence of named node identifier).
An anonymous brace expression is an expression that creates an anonymous node as a blank node, then references this blank node in subsequent expressions.
A typical anonymous brace expression will look like the following:
Person:JaneDoe Person:hasName [ a Name: ; Name:firstName "Jane" ; Name:lastName "Doe"].
This anonymous expression has the following expansion form:
Person:JaneDoe Person:hasName _:a1234 .
_:a1234 a Name: .
_:a1234 Name:firstName "Jane" .
_:a1234 Name:lastName "Doe" .
where _:a1234 is a system-assigned blank node that represents an anonymous node.
A named brace expression is a brace expression in which the anonymous node is replaced with a named node identifier that can be referenced in subsequent expressions.
A *named brace expression resolves as a named node identifier that take the corresponding internal predicates and objects. For instance,
Person:JaneDoe Person:hasName [Name:JaneDoeBirthName => a Name: ; Name:firstName "Jane" ; Name:lastName "Doe" ].
This expands to the following RDF:
Person:JaneDoe Person:hasName Name:JaneDoeBirthName.
Name:JaneDoeBirthName a Name: .
Name:JaneDoeBirthName Name:firstName "Jane" .
Name:JaneDoeBirthName Name:lastName "Doe" .
where Name:JaneDoeBirthName is a named node identifier that is also an IRI.
Similarly,
Person:JaneDoe Person:hasName [_:JaneDoeBirthName => Name:firstName "Jane" ; Name:lastName "Doe" ; a Name: ].
resolves (expands) to
Person:JaneDoe Person:hasName _:JaneDoeBirthName .
_:JaneDoeBirthName Name:firstName "Jane" .
_:JaneDoeBirthName Name:lastName "Doe" .
_:JaneDoeBirthName a Name: .
where _:JaneDoeBirthName is a named node identifier that is also a blank node.
While the behavior of such named node expressions is the same, their effect are best shown if they are given as objects, predicates and subjects.
:s :p [:namedNode => :p1 :o1 ; :p2 o2 ; ...] .
This evaluates to
:s :p :namedNode .
:namedNode :p1 :o1 .
:namedNode ;p2 :o2 .
...
As given above, the following named brace expression can be written as follows:
Person:JaneDoe Person:hasName [Name:JaneDoeName => Name:firstName "Jane" ; Name:lastName "Doe"].
Name:JaneDoeName a Name; Entity:dateStart "1998-03-07" ; Entity:dateEnd "2021-11-05".
This creates a named node identifier Name:JaneDoeName
bound with the first name and last name. This is then referenced in another set of triples indicating a range. This expands to the RDF as:
Person:JaneDoe Person:hasName Name:JaneDoeName .
Name:JaneDoeName Name:firstName "Jane" ; Name:lastName "Doe".
Name:JaneDoeName a Name; Entity:dateStart "1998-03-07" ; Entity:dateEnd "2021-11-05".
[:namedNode => :p1 :o1 ; :p2 o2 ; ...] :p :o .
This evaluates to
:namedNode :p :o .
:namedNode :p1 :o1 .
:namedNode ;p2 :o2 .
...
Note: in the case of a subject replacement of a named node expression, this defaults to the expression:
:namedNode :p1 :o1 ; :p2 o2 ; ... ; :p :o .
:s [:namedNode => :p1 :o1 ; :p2 o2 ; ...] :o .
This evaluates to
:s :namedNode :o .
:namedNode :p1 :o1 .
:namedNode ;p2 :o2 .
...
Person:JaneDoe [_:hasBigCoIdentifier => rdfs:subPropertyOf Person:hasEmployeeIdentifier;
Identifier:hasAuthority Org:BigCo] "JD12345"^^xsd:string.
This expands to:
Person:JaneDoe _:hasBigCoIdentifier "JD12345"^^xsd:string .
_:hasBigCoIdentifier rdfs:subPropertyOf Person:hasEmployeeIdentifier .
_:hasBigCoIdentifier Identifier:hasAuthority Org:BigCo .
Note: This syntactical form makes it possible to define local properties, which moves Turtle-Star into alignment with OpenCypher Create Constructs. See the Use Cases section below for more details.
A named node expression can be composed within another named expression. For instance,
Actor:ElijahWood Actor:playedCharacter [ Character:Frodo => Character:inMovieSeries
[MovieSeries:LordOfTheRings => MovieSeries:directedBy Director:PeterJackson]].
This expands to the RDF:
Actor:ElijahWood Actor:playedCharacter Character:Frodo .
Character:Frodo Character:inMovieSeries MovieSeries:LordOfTheRings .
MovieSeries:LordOfTheRings MovieSeries:directedBy Director:PeterJackson .
Note: This notation is similar to OpenCypher CREATE notation.
A Named Parenthetical Expression is a way to name a linked list structure, frequently used for actions such as passing positional parameters in Turtle.
:s :p ( :key1 => :item1, :key2 => :item2, :key3 => :item3 ...) .
This expands to:
:s :p :key1 .
:key1 rdf:first :item1 .
:key1 rdf:rest :key2 .
:key2 rdf:first :item2 .
:key2 rdf:rest :key3 .
:key3 rdf:first :item3 .
:key3 rdf:rest rdf:nil .
This creates a sequential linked list, with referenceable items. :key1 in this case also becomes the name of the linked list structure in toto.
Book:TheFellowshipOfTheRing Book:hasChapters (
:FOTRChapter1 => "A Long-expected Party",
:FOTRChapter2 => "The Shadow of the Past",
:FOTRChapter3 => "Three is Company",
:FOTRChapter4 => "A Short Cut to Mushrooms")
This expands to
Book:TheFellowshipOfTheRing Book:hasChapters :FOTRChapter1 .
:FOTRChapter1 rdf:first "A Long-expected Party".
:FOTRChapter1 rdf:rest :FOTRChapter2 .
:FOTRChapter2 rdf:first "The Shadow of the Past".
:FOTRChapter2 rdf:rest :FOTRChapter3 .
:FOTRChapter3 rdf:first "Three is Company",
:FOTRChapter3 rdf:rest :FOTRChapter4 .
:FOTRChapter4 rdf:first "A Short Cut to Mushrooms",
:FOTRChapter4 rdf:rest :rdf:nil .
The most significant point to note here is that this is a linked list structure with named node identifiers, not a set. This means that it follows the same traversal pattern of any linked list.
Additionally, the objects of the rdf:first statement could themselves be bracket expressions:
Book:TheFellowshipOfTheRing Book:hasChapters (
:FOTRChapter1 => [rdfs:label "A Long-expected Party"],
:FOTRChapter2 => [rdfs:label "The Shadow of the Past"],
:FOTRChapter3 => [rdfs:label "Three is Company"],
:FOTRChapter4 => [rdfs:label "A Short Cut to Mushrooms"])
Equally, the older anonymous parenthetical expressions could be used, with bracketed expressions containing the identifier:
Book:TheFellowshipOfTheRing Book:hasChapters (
[:FOTRChapter1 => rdfs:label "A Long-expected Party"]
[:FOTRChapter1 => rdfs:label "The Shadow of the Past"]
[:FOTRChapter1 => rdfs:label "Three is Company"]
[:FOTRChapter1 => rdfs:label "A Short Cut to Mushrooms"])
Editor's Note: I'm uncertain about whether a comma, semi-colon, or space should be used to divide named parenthetical expressions. I'm leaning towards commas, because it makes the expressions look more like JSON, but arguments can be made for any of them.
A Named Set Expression is a way to name a set structure. Sets are somewhat invisible in RDF, but they have significant utility in Turtle. It uses the (* and *) notation,
:s :p (* namedNode => :item1,:item2,:item,... *)
This expands to:
:s :p :namedNode .
:namedNode rdf:member :item1,:item2,:item3 .
:JaneDoe :hiredBand (* :Beatles => :John,:Paul,:George,:Ringo *).
expands to:
:JaneDoe :engagedBand :Beatles .
:Beatles :rdf:member :John, :Paul, :George :Ringo .
Note: This scenario also illustrates how you can use composition to pass through an identifier:
:JaneDoe :hiredBand [(* :Beatles => :John,:Paul,:George,:Ringo *) => a RockBand:].
which expands as:
:JaneDoe :hiredBand :Beatles.
:Beatles rdf:member :John,:Paul,:George,:Ringo .
:Beatles a RockBand: .
Here, The named node :Beatles
from the Named Set Expression then gets passed back in as the named node for the brace expression.
A Named Ordered Set Expression is similar to a named set expression, except that instead of using the predicate rdf:member, it uses the predicates rdf:1,rdf:2,rdf:3, etc, where rdf:N is a subproperty of rdf:member. This is frequently an easier way of creating an ordered set, if there's no explicit need for insertion into the list.
:s :p (% namedNode => :item1,:item2,:item3 %)
This expands to:
:s :p :namedNode .
:namedNode rdf:1 :item1 ;
rdf:2 :item2 ;
rdf:3 :item3 .
Book:TheFellowshipOfTheRing Book:hasChapters (% :FOTRChapters =>
"A Long-expected Party",
"The Shadow of the Past",
"Three is Company",
"A Short Cut to Mushrooms" %).
This expands to:
Book:TheFellowshipOfTheRing Book:hasChapters :FOTRChapters .
:FOTRChapters
rdf:1 "A Long-expected Party" ;
rdf:2 "The Shadow of the Past" ;
rdf:3 "Three is Company" ;
rdf:4 "A Short Cut to Mushrooms" .
Note: This is especially useful for work with SPARQL, as it's easy to extract the number portion of each property, then sort it with the ORDER by property. This is often considerably more efficient than trying to walk a linked list. THe following will list the chapters in order:
SELECT ?chapterTitles where {
values {?book Book:FellowshipOfTheRing}
?book Book:hasChapters ?chapter .
?chapter ?prop ?chapterTitle .
bind(xs:integer(strafter(?prop,str(rdfs:))) as ?index.
} order by ?index.
A reification is a data structure that gives the subject, predicate, object, and (optionally) graph of a given triple. A reification is not, itself, a triple.
:s :p << :s1 :p1 :o1 :g>> #similarly for subject and predicate positions
which expands to:
:s :p _:a1234 .
_:a1234 rdf:s :s1 .
_:a1234 rdf:p :p1 .
_:a1234 rdf:o :o1 .
_:a1234 rdf:g :g .
A named graph expression provides the same named node nomenclature to graph collections.
:s :p {:graphName => :s1 :p1 :o1. :s2 :p2 :o2. ...}
#similarly for subject and predicate positions
This maps to Trig (Turtle + Graphs)
:s :p :graphName .
:graphName {
:s1 :p1 :o1 .
:s2 :p2 :o2.
... }
Note: This is something of an edge case, but is useful when graphs are used extensively and you are looking to annotate each graph entry.
The following give various use cases and their resolution using Named Node Expressions
Liz married several times over the years, including one person (Richard) twice.
:liz
:born "1945"; :died: "2014";
:hasMarriages [(% :LizMarriages =>
[:LizJohnMarriage1 => :marriedTo :John; :from "1965"; :to "1974"; :endedBy :Divorce;
:hasAnnotation [ :LizJohnQuestion => :hasComment "They separated in 1973, but the divorce
wasn't finalized until much later."]],
[:LizRichardMarriage1 => :marriedTo :Richard; :from "1975"; :to "1984"; :endedBy :Divorce ;
:hasAnnotation [:RichardAndLiz => :title "Richard and Liz"; :by :HeddyLaHopper :published "1996"]],
[:LizThomasMarriage1 => :marriedTo :Thomas; :from "1985"; :to "1994"; :endedBy :SpouseDeath],
[:LizRichardMarriage1 => :marriedTo :Richard; :from "1995"; :to "2004"; :endedBy :SpouseDeath ;
:hasAnnotation :RichardAndLiz],
[:LizJamesMarriage1 => :marriedTo :James; :from "2005"; :to "2014"; :endedBy :Death;]%) =>
:hasAnnotations
[ :TheMerryMenOfLiz => :title "The Merry Men of Liz"; :by :VirginiaDeWolfe; :published "2015" ] .
This expands to the following RDF:
:Liz :hasMarriages :LizMarriages.
:lizMarriages rdf:1 :LizJohnMarriage1 .
:lizMarriages rdf:2 :LizRichardMarriage1 .
:lizMarriages rdf:3 :LizThomasMarriage1.
:lizMarriages rdf:4 :LizRichardMarriage2.
:lizMarriages rdf:5 :LizJamesdMarriage1.
:LizJohnMarriage1 :marriedTo :John; :from "1965"; :to "1974"; :endedBy :Divorce .
:LizRichardMarriage1 :marriedTo :Richard; :from "1975"; :to "1984"; :endedBy :Divorce ;
:hasAnnotation :RichardAndLiz .
:LizThomasMarriage1 :marriedTo :Thomas; :from "1985"; :to "1994"; :endedBy :SpouseDeath .
:LizRichardMarriage2 :marriedTo :Richard; :from "1995"; :to "2004"; :endedBy :SpouseDeath;
:hasAnnotation :RichardAndLiz .
:LizJamesMarriage1 :marriedTo :James; :from "2005"; :to "2014"; :endedBy :Death.
:LisMarriages :hasAnnotation :theMerryMenOfLiz .
:theMerryMenOfLiz :title "The Merry Men of Liz"; :by :VirginiaDeWolfe; :published "2015".
:RichardAndLiz "title "Richard and Liz"; :by :HeddyLaHopper :published "1996".
This has become one of the testbed use cases for the RDF-Star working group, as it shows a number of different issues - How do you model a sequence of events, how do you annotate specific entries, and what are the links between reifications and assertions.
Annotations are worth discussing first. An annotation is usually an external description or clarification of a given object. There are three annotation references in the above example, one talking about the set of all marriages of Liz, the second talking specifically about the marriages of Liz and Richard, and third raising a question about a date. In general, annotations apply to objects or much more rarely properties, not literal values, as an annotation is usually qualifying an event during which a state change occurred.
The use cases for reification here are considerably fewer than for using named node expressions and a generalized annotation ontology, and usually have to do with deep-level modelling.
Data modelling matters as well, here. There are a number of different ways that this could be modelled, but not all of them are unambiguous.
OpenCypher is the largest competitor to the RDF tool stack.
[:ElijahWood => a Actor:] :playedCharacter [ [:Frodo => a Character:] => :inMovieSeries
[[:LordOfTheRings => a MovieSeries: ] => :directedBy [ :PeterJackson => a Director:]]]]]
:born "1981-01-28".
The notation that OpenCypher uses for CREATE statements (roughly the analog to Turtle in the RDF stack), makes extensive use of a similar named node expression format, though more limited in some respects. The composition aspect is doable without named nodes, but the named nodes often make it much easier to create effective labels on data structures.
One other key distinction, The primary difference between the RDF and OpenCyper model has to do with way that labels are specified. Each edge in OpenCypher has an internal unique identifier, with the "label" of the link being a separate property. This way, edges are able to hold literal properties as well as have common "labels", because they store the subject and object node as identifiers in their model. Thus, in Open Cypher, Liz Marries Richard can be entered twice to refer to two distinct marriages, because "Marries" is just a label on a graph where you have two distinct edges. With named nodes, this can be expressed as follows:
:Liz [_:lizRichardMarriage1 => :label "Marries" ; :from "1975"; :to "1984"; :endedBy :Divorce] :Richard.
:Liz [_:lizRichardMarriage2 => :label "Marries" ; :from "1995"; :to "2004"; :endedBy :SpouseDeath] :Richard.
When this gets parsed, _:lizRichardMarriage1
and _:lizRichardMarriage2
get resolved to system assigned URIs, but because they still have a common property label, they can still be queried from sparql as if they had the "same" property. This provides equivalence between the two models.
Statements may exist without a guarantee that they are valid, and as such should not be with the graph, but should be available for reference.
<<:SkyIsGreen => :EarthSky :hasColor :Green>> :statedBy :john.
<<:SkyIsBlue => :EarthSky :hasColor :Blue>> :statedBy :john.
:SkyIsGreen :validatedAs false .
:SkyIsBlue :validatedAs true .
These statements are equivalent to:
:SkyIsGreen rdf:s :EarthSky .
:SkyIsGreen rdf:p :hasColor .
:SkyIsGreen rdf:o :Green .
:SkyIsGreen a rdf:Reification.
:SkyIsGreen :validatedAs false .
:SkyIsBlue rdf:s :EarthSky .
:SkyIsBlue rdf:p :hasColor .
:SkyIsBlue rdf:o :Blue.
:SkyIsBlue :validatedAs true .
:SkyIsBlue a rdf:Reification.
The validation statements may actually be added later, after some kind of process determines whether the statements are true or false. SPARQL Update can then be used to insert the statements AS triples, but only if they are properly validated:
insert {
?s ?p o.
} WHERE {
?r a rdf:Reification .
?r rdf:s ?s .
?r rdf:p ?p .
?r rdf:o ?o .
?r rdf:validatedAs ?isValid.
filter(?isValue)
}
This will then create the triple
:EarthSky :hasColor :Blue .
but not
:EarthSky :hasColor :Green .
### Comments
This is a simple use case but an important one because many knowledge graphs pull information from multiple data streams, and have to be validated before they are entered into a canonical graph. Reification represents one way of performing such validation.
Summary of the RDF-star WG wiki.
- Editor's guide
- Meeting minutes
- RDF terminology
- Scribes
- Use Cases collection
- RDF-star syntax and semantics:
- RDF-star "alternative baseline" (VOTED 2024.11.14 - frozen)
- RDF-star "liberal baseline" (current working version)
- RDF-star "minimal baseline" (VOTED 2024.07.18 - frozen - superseded by vote 2024.11.14 - deprecated)
- RDF-star "working baseline" (working version - deprecated)
- RDF‐star baseline examples
- RDF-star and LPGs
- Extending the baseline with "asserted" stuff
- systems and acronyms
- Task forces
- Text Direction considerations
- Text Direction Proposal
- Triple‐Edge-subgroup-proposals