Useful data needs consistent structure:
Detect and correct errors:
@prefix : <http://www.w3.org/2012/12/rdf-val/SOTA-ex#> . @prefix foaf: <http://xmlns.com/foaf/0.1/'> . @prefix xsd: <http://www.w3.org/2001/XMLSchema#> . <issue7> a :Issue , :SecurityIssue ; :state :unassigned ; :reportedBy <user6> , <user2> ; # cardinality 1 :reportedOn "2012-12-31T23:57:00"^^xsd:dateTime ; :assignedTo <user2>, <user1> ; :assignedOn "2012-11-31T23:57:00"^^xsd:dateTime ; # reproduced before being reported :related <issue4>, <issue3>, <issue2> . # referenced issues not included <issue4> # a ??? missing type arc :state :unsinged ; # misspelled # :reportedBy ??? - missing :reportedOn "2012-12-31T23:57:00"^^xsd:dateTime . <user2> a foaf:Person ; foaf:givenName "Alice" ; foaf:familyName "Smith" ; foaf:phone <tel:+1.555.222.2222> ; foaf:mbox <mailto:alice@example.com> . <user6> a foaf:Agent ; # should be foaf:Person foaf:givenName "Bob" ; # foaf:familyName "???" - missing foaf:phone <tel:+.555.222.2222> ; # malformed tel: URL foaf:mbox <mailto:alice@example.com> .
What do users get elsewhere?
SQL | XML | |
---|---|---|
missing properties | reportedBy UNSIGNED INT NOT NULL | element reportedBy { User }, |
missing/bad type arcs | N/A | N/A |
missing referents | FOREIGN KEY (reportedBy) REFERENCES Users(ID) | <keyref refer="UserID"> |
inconsistent state | CHECK(assignedOn>reportedOn) | [schematron] |
value set violations | ENUM('unasigned', 'assigned') | attribute state { "unassigned" | "assigned" } |
in order of specificity:
ASK { { SELECT ?S (COUNT(*) AS ?S_c0) { ?S foaf:givenName ?o . } GROUP BY ?S} { SELECT ?S (COUNT(*) AS ?S_c1) { ?S foaf:givenName ?o . } GROUP BY ?S} FILTER (?S_c0 = ?S_c1 && ?S_c0 = 1) { SELECT ?S (COUNT(*) AS ?S_c2) { ?S ex:state ?o . } GROUP BY ?S HAVING (COUNT(*)=1)} { SELECT ?S (COUNT(*) AS ?S_c3) { ?S ex:state ?o . FILTER ((?o = ex:unassigned || ?o = ex:assigned)) } GROUP BY ?S HAVING (COUNT(*)=1)} FILTER (?S_c2 = ?S_c3 && (?S_c0 = 0 || ?S_c0 = 1)) }
{ "@context": { … }, "constraints": [{ "context": "ex:status", "constraint": "ASK { ?s ex:assignee ?o }", "severity": "warning", "message": "a status of assigned requires an assignee" }] }
ASK { { SELECT ?this (COUNT(*) AS ?this_c0) { ?this foaf:givenName ?o . } GROUP BY ?this} { SELECT ?this (COUNT(*) AS ?this_c1) { ?this foaf:givenName ?o . } GROUP BY ?this} FILTER (?this_c0 = ?this_c1 && ?this_c0 = 1) { SELECT ?this (COUNT(*) AS ?this_c2) { ?this ex:state ?o . } GROUP BY ?this HAVING (COUNT(*)=1)} { SELECT ?this (COUNT(*) AS ?this_c3) { ?this ex:state ?o . FILTER ((?o = ex:unassigned || ?o = ex:assigned)) } GROUP BY ?this HAVING (COUNT(*)=1)} FILTER (?this_c2 = ?this_c3 && (?this_c0 = 0 || ?this_c0 = 1)) }
:Issue a owl:Class ; rdfs:subClassOf owl:Thing ; spin:constraint [ a spl:ObjectCountPropertyConstraint ; arg:property ex:name ; arg:minCount 1 ; arg:maxCount 1 ; ] ; spin:constraint [ a spl:ObjectCountPropertyConstraint ; arg:property ex:state ; arg:minCount 0 ; arg:maxCount 1 ; ] ; spin:constraint [ a spl:UntypedObjectPropertyConstraint ; arg:property ex:state ; ] . ex:name a owl:DatatypeProperty ; rdfs:domain my:name-status ; rdfs:range xsd:string . :ValidState a owl:Class ; rdfs:label "Valid state" ; rdfs:subClassOf owl:Thing ; . :state a owl:ObjectProperty ; rdfs:domain my:name-status ; rdfs:range ex:ValidState . :unassigned a ex:ValidState . :assigned a ex:ValidState .
Combine OWL with a premise type associated with data.
Datatype: rdfs:Literal DataProperty: ex:name ObjectProperty: ex:status Class: ex:name-status SubClassOf: ex:name exactly 1 rdfs:Literal , ex:status max 1 ({ ex:assigned , ex:unassigned }) , ex:status min 0 owl:Thing Individual: ex:assigned Individual: ex:unassigned
Not a real proposal; instead used with a different interpretation…
OWL with unique name assumption and closed world
Datatype: rdfs:Literal DataProperty: ex:name ObjectProperty: ex:status Class: ex:name-status SubClassOf: ex:name exactly 1 rdfs:Literal , ex:status max 1 ({ ex:assigned , ex:unassigned }) , ex:status min 0 owl:Thing Individual: ex:assigned Individual: ex:unassigned
my:name-status a rs:ResourceShape ; rs:property [ rs:name "name" ; rs:propertyDefinition foaf:name ; rs:valueType xsd:string ; rs:occurs rs:Exactly-one ; ] ; rs:property [ rs:name "state" ; rs:propertyDefinition ex:state ; rs:allowedValue ex:unassigned> , ex:assigned ; rs:occurs rs:Zero-or-one ; ] .
my:name-status { ex:name xsd:string , ex:status ( ex:unassigned ex:assigned )? }
<Foo> { foaf:parent NONLITERAL{2} }
<X> foaf:parent [ foaf:mbox <mailto:a@example.com> ], [ foaf:mbox <mailto:a@example.com> ].
rdf:type
<(?:[^<>]++|(?1))*>
What is Shape Expressions?
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX myco: <http://myco.example/#> start = <IssueShape> <IssueShape> { issue:status (issue:unassigned issue:assigned), issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime, ( issue:assignedTo @<EmployeeShape>, issue:assignedOn xsd:dateTime )? } <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI } <EmployeeShape> { foaf:page (myco:Employee~), foaf:givenName LITERAL+, foaf:familyName LITERAL, foaf:phone IRI*, foaf:mbox IRI }
What's my user profile look like?
PREFIX foaf: <http://xmlns.com/foaf/> <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
What's my user profile look like?
PREFIX foaf: <http://xmlns.com/foaf/> <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
What's my user profile look like?
PREFIX foaf: <http://xmlns.com/foaf/> <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
compare with other schema languages...
(element foaf:name { xsd:string } | (element foaf:givenName { xsd:string }+, element foaf:familyName { xsd:string })), element foaf:mbox { xsd:anyURI }
(N|(G+F))M
NM
GFM
GGGFM
<xs:complexType name="UserContent"> <xs:sequence> <xs:choice> <xs:element name="name" type="xs:string"/> <xs:sequence> <xs:element maxOccurs="unbounded" name="givenName" type="xs:string"/> <xs:element name="familyName" type="xs:string"/> </xs:sequence> </xs:choice> <xs:element name="mbox" type="xs:anyURI"/> </xs:sequence> </xs:complexType>
Trying it out
PREFIX foaf: <http://xmlns.com/foaf/> start = <UserShape> <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
Try it at ericP's indulgent interface
or Jose's more correct interface:
<Alice>,
<Bob>,
<Gene>,
<Pat>,
<Eve> (passes, OR is non-exclusive)
PREFIX foaf: <http://xmlns.com/foaf/> <Alice> foaf:name "Alice" ; foaf:mbox <mailto:alice@example.org> . <Bob> foaf:givenName "Robert", "Edward" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> . <Gene> foaf:givenname "Gene" ; foaf:familyName "Smith" ; # misspelling foaf:mbox <mailto:gene@example.org> . <Pat> foaf:givenName "Patricia" ; # no foaf:familyName foaf:mbox <mailto:pat@example.org> . <Eve> foaf:name "Eve" ; # both name and givenName/familyName foaf:givenName "Eve" ; foaf:familyName "石川" ; foaf:mbox <mailto:eve@example.org> .
Fix Gene, Pat and Eve
PREFIX foaf: <http://xmlns.com/foaf/> start = <UserShape> <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
PREFIX foaf: <http://xmlns.com/foaf/> <Gene> foaf:givenname "Gene" ; foaf:familyName "Smith" ; foaf:mbox <mailto:gene@example.org> . <Pat> foaf:givenName "Patricia" ; foaf:mbox <mailto:pat@example.org> . <Eve> foaf:name "Eve" ; foaf:givenName "Eve" ; foaf:familyName "石川" ; foaf:mbox <mailto:eve@example.org> .
Now break it;
it's more fun to demonstrate what validators whine about than what they ignore.
Fixed Gene, Pat and Eve
PREFIX foaf: <http://xmlns.com/foaf/> start = <UserShape> <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
PREFIX foaf: <http://xmlns.com/foaf/> <Gene> foaf:givenName "Gene" ; foaf:familyName "Smith" ; foaf:mbox <mailto:gene@example.org> . <Pat> foaf:givenName "Patricia" ; foaf:familyName "Gordon" ; foaf:mbox <mailto:pat@example.org> . <Eve1> foaf:name "Eve" ; foaf:mbox <mailto:eve@example.org> . <Eve2> foaf:givenName "Eve" ; foaf:familyName "石川" ; foaf:mbox <mailto:eve@example.org> .
my:name-status a rs:ResourceShape ; rs:property [ rs:name "name" ; rs:propertyDefinition ex:name ; rs:valueType xsd:string ; rs:occurs rs:Exactly-one ; ] ; rs:property [ rs:name "status" ; rs:propertyDefinition ex:status ; rs:allowedValue ex:unassigned , ex:assigned ; rs:occurs rs:Zero-or-one ; ] .
equivalent to
my:name-status { ex:name xsd:string , ex:status ( ex:unassigned ex:assigned )? }
universal, non-qualified constraints
SELECT ?S { { SELECT ?S (COUNT(*) AS ?S_c0) { ?S ex:name ?o . } GROUP BY ?S} { SELECT ?S (COUNT(*) AS ?S_c1) { ?S ex:name ?o . FILTER(datatype(?o) = xsd:string) } GROUP BY ?S} FILTER (?S_c0 = ?S_c1 && ?S_c0 = 1) { SELECT ?S (COUNT(*) AS ?S_c2) { ?S ex:status ?o . } GROUP BY ?S} { SELECT ?S (COUNT(*) AS ?S_c3) { ?S ex:status ?o . FILTER ((?o = ex:unassigned || ?o = ex:assigned)) } GROUP BY ?S} FILTER (?S_c2 = ?S_c3 && (?S_c0 = 0 || ?S_c0 = 1)) }
Datatype: xsd:string DataProperty: ex:name ObjectProperty: ex:status Class: ex:name-status SubClassOf: ex:name exactly 1 rdfs:Literal , ex:status only ({ ex:assigned , ex:unassigned }) , ex:status max 1 , ex:status min 0 Individual: ex:assigned Individual: ex:unassigned
What's that look like in SPARQL?
PREFIX foaf: <http://xmlns.com/foaf/> <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
PREFIX foaf: <http://xmlns.com/foaf/> ASK { { SELECT ?UserShape WHERE { { { SELECT ?UserShape { ?UserShape foaf:name ?o . } GROUP BY ?UserShape HAVING (COUNT(*)=1)} { SELECT ?UserShape { ?UserShape foaf:name ?o . FILTER (isLiteral(?o)) } GROUP BY ?UserShape HAVING (COUNT(*)=1)} } UNION { { SELECT ?UserShape (COUNT(*) AS ?UserShape_c0) { ?UserShape foaf:givenName ?o . } GROUP BY ?UserShape HAVING (COUNT(*)>=1)} { SELECT ?UserShape (COUNT(*) AS ?UserShape_c1) { ?UserShape foaf:givenName ?o . FILTER (isLiteral(?o)) } GROUP BY ?UserShape HAVING (COUNT(*)>=1)} FILTER (?UserShape_c0 = ?UserShape_c1) { SELECT ?UserShape { ?UserShape foaf:familyName ?o . } GROUP BY ?UserShape HAVING (COUNT(*)=1)} { SELECT ?UserShape { ?UserShape foaf:familyName ?o . FILTER (isLiteral(?o)) } GROUP BY ?UserShape HAVING (COUNT(*)=1)} } } GROUP BY ?UserShape HAVING (COUNT(*) = 1)} { SELECT ?UserShape { ?UserShape foaf:mbox ?o . } GROUP BY ?UserShape HAVING (COUNT(*)=1)} { SELECT ?UserShape { ?UserShape foaf:mbox ?o . FILTER (isIRI(?o)) } GROUP BY ?UserShape HAVING (COUNT(*)=1)} }
ShExC is a BNF for RDF:
Issue: reportedOn reportedBy ( assignedOn assignedTo )? reportedBy: User assignedTo: User User: ( name | givenName+ familyName ) mbox
example instance
<Issue1> issue:reportedBy <Bob> ; issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime . <Bob> foaf:givenName "Robert", "Edward" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> .
ShExC is a BNF for RDF:
start = <IssueShape> <IssueShape> { issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime, ( issue:assignedTo @<UserShape>, issue:assignedOn xsd:dateTime )? } <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
example instance
<Issue1> issue:reportedBy <Bob> ; issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime . <Bob> foaf:givenName "Robert", "Edward" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> .
Different rules for Users from Employees.
Adjust previous example:
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> start = <IssueShape> <IssueShape> { issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime, ( issue:assignedTo @<UserShape>, issue:assignedOn xsd:dateTime )? } <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI }
Create a <EmployeeShape> distinct from an <UserShape>.
example instance:
PREFIX ex: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> ex:reportedBy <User2> ; ex:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ; ex:assignedTo <Thompson.J> ; ex:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime . <User2> foaf:givenName "Bob" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> . <Thompson.J> foaf:givenName "Joe", "Joseph" ; foaf:familyName "Thompson" ; foaf:phone <tel:+456> ; foaf:mbox <mailto:joe@example.org> .
Different rules for Users from Employees.
A solution:
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> start = <IssueShape> <IssueShape> { issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime, ( issue:assignedTo @<EmployeeShape>, issue:assignedOn xsd:dateTime )? } <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI } <EmployeeShape> { foaf:givenName LITERAL+, foaf:familyName LITERAL, foaf:phone IRI*, foaf:mbox IRI }
Create a <EmployeeShape> distinct from an <UserShape>.
example instance:
PREFIX ex: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> ex:reportedBy <User2> ; ex:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ; ex:assignedTo <Thompson.J> ; ex:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime . <User2> foaf:givenName "Bob" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> . <Thompson.J> foaf:givenName "Joe", "Joseph" ; foaf:familyName "Thompson" ; foaf:phone <tel:+456> ; foaf:mbox <mailto:joe@example.org> .
A value set is a set of possible values.
issue:assigned~
matches
issue:assignedBy
,
issue:assignedTo
,
issue:assignedOn
.
ex:mood ("happy" "sad" "indigo")
ex:mood (mood:happy mood:sad mood:indigo)
ex:mood (mood:~)
<http://myco.example/#Employee>
.Extend previous example:
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> start = <IssueShape> <IssueShape> { issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime, ( issue:assignedTo @<EmployeeShape>, issue:assignedOn xsd:dateTime )? } <UserShape> { (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI } <EmployeeShape> { foaf:givenName LITERAL+, foaf:familyName LITERAL, foaf:phone IRI*, foaf:mbox IRI }
example instance:
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX myco: <http://myco.example/#> <Issue1> issue:status issue:assigned ; issue:reportedBy <User2> ; issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ; issue:assignedTo <Thompson.J> ; issue:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime . <User2> a foaf:Person ; foaf:givenName "Bob" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> . <Thompson.J> a foaf:Person ; foaf:page myco:Employee7 ; foaf:givenName "Joe", "Joseph" ; foaf:familyName "Thompson" ; foaf:phone <tel:+456> ; foaf:mbox <mailto:joe@example.org> .
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX myco: <http://myco.example/#> start = <IssueShape> <IssueShape> { issue:status (issue:unassigned issue:assigned issue:resolved), issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime, ( issue:assignedTo @<EmployeeShape>, issue:assignedOn xsd:dateTime )? } <UserShape> { a (foaf:Person), (foaf:name LITERAL | foaf:givenName LITERAL+, foaf:familyName LITERAL), foaf:mbox IRI } <EmployeeShape> { a (foaf:Person), foaf:page (myco:Employee~), foaf:givenName LITERAL+, foaf:familyName LITERAL, foaf:phone IRI*, foaf:mbox IRI }
example instance:
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX myco: <http://myco.example/#> <Issue1> issue:status issue:assigned ; issue:reportedBy <User2> ; issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ; issue:assignedTo <Thompson.J> ; issue:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime . <User2> a foaf:Person ; foaf:givenName "Bob" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> . <Thompson.J> a foaf:Person ; foaf:page myco:Employee7 ; foaf:givenName "Joe", "Joseph" ; foaf:familyName "Thompson" ; foaf:phone <tel:+456> ; foaf:mbox <mailto:joe@example.org> .
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> start = <IssueShape> <IssueShape> { issue:status (issue:unassigned issue:assigned issue:resolved), issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime, ( issue:assignedTo @<EmployeeShape>, issue:assignedOn xsd:dateTime )? }
What's wrong with this picture?
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> issue:status issue:unassigned ; issue:reportedBy <User2> ; issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ; issue:assignedTo <Thompson.J> ; issue:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime . <User2> a foaf:Person ; foaf:givenName "Bob" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> . <Thompson.J> a foaf:Person ; foaf:page myco:Employee7 ; foaf:givenName "Joe", "Joseph" ; foaf:familyName "Thompson" ; foaf:phone <tel:+456> ; foaf:mbox <mailto:joe@example.org> .
How much can we capture?
Not a lot, but we can improve the free-for-all in the previous example
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> start = <IssueShape> <IssueShape> { issue:status (issue:unassigned issue:assigned issue:resolved), issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime, ( issue:assignedTo @<UserShape>, issue:assignedOn xsd:dateTime )? } <UserShape> { foaf:givenName LITERAL+, foaf:familyName LITERAL, foaf:mbox IRI }
example instance:
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> issue:status issue:assigned ; issue:reportedBy <User2> ; issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ; issue:assignedTo <Thompson.J> ; issue:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime . <User2> foaf:givenName "Bob" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> . <Thompson.J> foaf:givenName "Joe", "Joseph" ; foaf:familyName "Thompson" ; foaf:mbox <mailto:joe@example.org> .
You will need to use '!' to negate a rule
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> start = <IssueShape> <IssueShape> { issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime, ( issue:status (issue:unassigned), !issue:assignedTo ., !issue:assignedOn . | issue:status (issue:assigned issue:resolved), issue:assignedTo @<UserShape>, issue:assignedOn xsd:dateTime ) } <UserShape> { foaf:givenName LITERAL+, foaf:familyName LITERAL, foaf:mbox IRI }
example instance:
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> issue:status issue:assigned ; issue:reportedBy <User2> ; issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ; issue:assignedTo <Thompson.J> ; issue:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime . <User2> foaf:givenName "Bob" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> . <Thompson.J> foaf:givenName "Joe", "Joseph" ; foaf:familyName "Thompson" ; foaf:mbox <mailto:joe@example.org> .
IRIs in predicates and value sets can have:
PREFIX annot: http://www.w3.org/annotea/ns#> PREFIX dc: <http://purl.org/dc/elements/1.1/> start={ annot:context LITERAL, dc:~ - dc:author - dc:creator .* }
PREFIX annot: http://www.w3.org/annotea/ns#> PREFIX dc: <http://purl.org/dc/elements/1.1/> <Annot1> annot:context "xpath..."; dc:abstract "stuff" ; dc:audience "9606" ; dc:description """some long description""" .
Test arcs coming into an object.
PREFIX issue: <http://ex.example/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> start = <IssueShape> <IssueShape> { issue:status (issue:assigned issue:resolved ), issue:reportedOn xsd:dateTime, issue:assignedOn xsd:dateTime, issue:related @<RefdIssueShape> } <RefdIssueShape> { issue:name LITERAL, ^issue:related <IssueShape> }
PREFIX issue: <http://ex.example/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> issue:status issue:assigned ; issue:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ; issue:assignedOn "2013-01-23T11:00:00"^^xsd:dateTime ; issue:related <Issue3> . <Issue3> issue:name "smokey" .
Use case: storing application data in a
"If I tell you X, will you understand it?"
What about re-used nodes?
Closed shapes:
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> start = <IssueShape> <IssueShape> { issue:reportedBy @<UserShape>, issue:assignedTo @<EmployeeShape>? } <UserShape> { foaf:name LITERAL foaf:mbox IRI } <EmployeeShape> { foaf:givenName LITERAL+, foaf:familyName LITERAL, foaf:mbox IRI }
<User2> fits multiple shapes:
PREFIX ex: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> ex:reportedBy <User2> ; ex:assignedTo <User2> ; <User2> foaf:name "Bob Smith" ; foaf:givenName "Bob" ; foaf:familyName "Smith" ; foaf:mbox <mailto:bob@example.org> .
For extensibility:
For actions:
Provides purpose-fit expressivity with minimal implementation cost.
Can count on order.
Javascript API actions embedded in %js{ … %}.
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> start = <IssueShape> <IssueShape> { issue:state (issue:unassigned issue:assigned), issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime %js{ report = _.o; return true; %}, (issue:reproducedBy @<EmployeeShape>, issue:reproducedOn xsd:dateTime %js{ return _.o.lex > report.lex; %} ), issue:related @<IssueShape>* } <UserShape> { (foaf:name xsd:string | foaf:givenName xsd:string+, foaf:familyName xsd:string), foaf:mbox IRI } <EmployeeShape> { foaf:givenName xsd:string+, foaf:familyName xsd:string, foaf:phone IRI*, foaf:mbox IRI }
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> ex:state ex:unassigned ; ex:reportedBy <User2> ; ex:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ; ex:reproducedBy <Thompson.J> ; ex:reproducedOn "2013-01-23T10:00:00"^^xsd:dateTime . <User2> foaf:givenName "Bob" ; foaf:familyName "Smith" ; foaf:mbox <mail:bob@example.org> . <Thompson.J> foaf:givenName "Joe", "Joseph" ; foaf:familyName "Thompson" ; foaf:phone <tel:+456> ; foaf:mbox <mail:joe@example.org> .
Isn't there an RDF query language?
%sparql{ … %}
can complement our %js{ … %} actions.
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> start = <IssueShape> <IssueShape> { issue:state (issue:unassigned issue:assigned), issue:reportedBy @<UserShape>, issue:reportedOn xsd:dateTime %js{ report = _.o; return true; %}, (issue:reproducedBy @<EmployeeShape>, issue:reproducedOn xsd:dateTime %js{ return _.o.lex > report.lex; %} %sparql{ ?s issue:reportedOn ?rpt . FILTER (?o > ?rpt) %} ), issue:related @<IssueShape>* } <UserShape> { (foaf:name xsd:string | foaf:givenName xsd:string+, foaf:familyName xsd:string), foaf:mbox IRI } <EmployeeShape> { foaf:givenName xsd:string+, foaf:familyName xsd:string, foaf:phone IRI*, foaf:mbox IRI }
PREFIX issue: <http://ex.example/> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> ex:state ex:unassigned ; ex:reportedBy <User2> ; ex:reportedOn "2013-01-23T10:18:00"^^xsd:dateTime ; ex:reproducedBy <Thompson.J> ; ex:reproducedOn "2013-01-23T10:00:00"^^xsd:dateTime . <User2> foaf:givenName "Bob" ; foaf:familyName "Smith" ; foaf:mbox <mail:bob@example.org> . <Thompson.J> foaf:givenName "Joe", "Joseph" ; foaf:familyName "Thompson" ; foaf:phone <tel:+456> ; foaf:mbox <mail:joe@example.org> .
Semantic actions can have side-effects.
PREFIX ex: <http://ex.example/#> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> %js{ action.arguments[1].message("in init"); doc = document.implementation.createDocument ("http://ex.example/xml", "Issue", undefined); issue = doc.childNodes[0]; %} start = <IssueShape> <IssueShape> { ex:state (ex:unassigned ex:assigned) %js{ issue.setAttribute("id", _.s.lex); issue.setAttribute("state", _.o.lex.substr(19)); person = user = doc.createElement("Reported"); %}, ex:reportedBy @<Person> %js{{ issue.appendChild(user); %}, ex:reportedOn xsd:dateTime %js{{ user.setAttribute("reportedOn", _.o.lex); person = reproducer = doc.createElement("Reproduced"); %}, (ex:reproducedBy @<EmployeeShape> %js{{ issue.appendChild(person);%}, ex:reproducedOn xsd:dateTime %js{{ reproducer.setAttribute("reportedOn", _.o.lex); %})?, ex:related @<IssueShape>* } %js{ window.open("data:application/xml;charset=utf-8;base64," + Base64.encode((new XMLSerializer()).serializeToString(doc)), "toy"); %} VIRTUAL <Person> { (foaf:name xsd:string | foaf:givenName xsd:string+ %js{ gn = doc.createElement("GivenName"); gn.appendChild(doc.createTextNode(_.o.lex)); person.appendChild(gn); %}, foaf:familyName xsd:string %js{ fn = doc.createElement("FamilyName"); fn.appendChild(doc.createTextNode(_.o.lex)); person.appendChild(fn); %} ), foaf:mbox IRI %js{ fn = doc.createElement("Email-address"); fn.appendChild(doc.createTextNode(_.o.lex)); person.appendChild(fn); %} } <UserShape> & <Person> { } <EmployeeShape> { & <Person>, foaf:phone IRI+ %js{ fn = doc.createElement("Telephone-Number"); fn.appendChild(doc.createTextNode(_.o.lex)); person.appendChild(fn); %} }
<Issue xmlns="http://ex.example/xml" id="Issue1" state="unassigned"> <Reported reportedOn="2013-01-23T10:18:00"> <GivenName>Bob</GivenName> <FamilyName>Smith</FamilyName> <Email-address>mailto:bob@example.org</Email-address> </Reported> <Reproduced reportedOn="2013-01-23T11:00:00"> <GivenName>Joe</GivenName> <GivenName>Joseph</GivenName> <FamilyName>Thompson</FamilyName> <Email-address>mailto:joe@example.org</Email-address> <Telephone-Number>tel:+456</Telephone-Number> </Reproduced> </Issue>
GenX details.
=<function>?
, $<namespace>
, !debugger
PREFIX ex: <http://ex.example/#> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> %GenX{ issue $http://ex.example/xml %} start = <IssueShape> <IssueShape> { ex:state (ex:unassigned ex:assigned) %GenX{ @state =substr(19) %}, ex:reportedBy @<Person> %GenX{ reported = %}, ex:reportedOn xsd:dateTime %GenX{ [-1]@date %}, (ex:reproducedBy @<EmployeeShape> , ex:reproducedOn xsd:dateTime %GenX{ @date %} )? %GenX{ reproduced = %}, ex:related @<IssueShape>* } %GenX{ @id %} VIRTUAL <Person> { (foaf:name xsd:string %GenX{ full-name %} | foaf:givenName xsd:string+ %GenX{ given-name %}, foaf:familyName xsd:string %GenX{ family-name %} ), foaf:mbox IRI %GenX{ email-addr %} } <UserShape> & <Person> { } <EmployeeShape> { & <Person>, foaf:phone IRI+ %GenX{ tel-number %} }
<Issue xmlns="http://ex.example/xml" id="Issue1" state="unassigned"> <Reported reportedOn="2013-01-23T10:18:00"> <GivenName>Bob</GivenName> <FamilyName>Smith</FamilyName> <Email-address>mailto:bob@example.org</Email-address> </Reported> <Reproduced reportedOn="2013-01-23T11:00:00"> <GivenName>Joe</GivenName> <GivenName>Joseph</GivenName> <FamilyName>Thompson</FamilyName> <Email-address>mailto:joe@example.org</Email-address> <Telephone-Number>tel:+456</Telephone-Number> </Reproduced> </Issue>
GenJ is GenX's JSON counterpart.
PREFIX ex: <http://ex.example/#> PREFIX foaf: <http://xmlns.com/foaf/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> %GenJ{ @id %} start = <IssueShape> <IssueShape> { ex:state (ex:unassigned ex:assigned) %GenJ{ state %}, ex:reportedBy @<User> %GenJ{ rprtBy @id %}, ex:reportedOn xsd:dateTime %GenJ{ rprtOn %}, (ex:reproducedBy @<User> %GenJ{ rpdcBy @id %}, ex:reproducedOn xsd:dateTime %GenJ{ rpdcOn %} )?, ex:related @<IssueShape>* } <User> { (foaf:name xsd:string %GenJ{ name %} | foaf:givenName xsd:string+ %GenJ{ fname %}, foaf:familyName xsd:string %GenJ{ lname %} ), foaf:mbox IRI %GenJ{ email %}, foaf:phone IRI? %GenJ{ tel %} }
{"@context": {"ns0":"http://ex.example/#", "state":"ns0:state", "rprtBy":"ns0:reportedBy", "ns1":"http://xmlns.com/foaf/", "fname":"ns1:givenName", "lname":"ns1:familyName", "email":"ns1:mbox", "ns2":"http://www.w3.org/2001/XMLSchema#", "rprtOn":{ "@id":"ns0:reportedOn", "@type":"ns2:dateTime" }, "rpdcBy":"ns0:reproducedBy", "tel":"ns1:phone", "rpdcOn":{ "@id":"ns0:reproducedOn", "@type":"ns2:dateTime" } }, "@id":"Issue1", "state":"http://ex.example/#unassigned", "rprtBy":{ "@id":"User2", "fname":"Bob", "lname":"Smith", "email":"mailto:bob@example.org" }, "rprtOn":"2013-01-23T10:18:00", "rpdcBy":{ "@id":"Thompson.J", "fname":["Joe","Joseph"], "lname":"Thompson", "email":"mailto:joe@example.org", "tel":"tel:+456" }, "rpdcOn":"2013-01-23T11:00:00" }
GenR is GenJ's RDF counterpart.
Generate RDF from RDF?!?
PREFIX ex: <http://ex.example/#> PREFIX my: <http://my.example/#> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> %GenR{ %} start = <IssueShape> <IssueShape> { ex:state (ex:unassigned ex:assigned ) %GenR{ ISSUE = IRI(CONCAT(STR(s), "/myMark")) . STATE = SUBSTR(STR(o), 19) . ISSUE my:status STATE %}, ex:reportedOn xsd:dateTime %GenR{ ON = BNODE("on") . ISSUE my:on ON . DATE=CONCAT(YEAR(o),'-'MONTH(o),'-',DAY(o)) . TIME=CONCAT(HOURS(o),':'MINUTES(o),':',SECONDS(o),TZ(o)) . ON ex:date STRDT(DATE, xsd:date) . ON ex:time STRDT(TIME, xsd:time) %} }
PREFIX ex: <http://ex.example/#> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> <Issue1> ex:state ex:unassigned ; ex:reportedOn "2013-01-23T10:18:12Z"^^xsd:dateTime .
<Issue1/myMark> my:status "unassigned" ; my:on [ my:date "2013-01-23"^^xsd:date ; my:time "10:18:12Z"^^xsd:time ] .
Normalize RDF.
Limited use:
%GenN{ %}
$my:id