Linked Data Templates (LDT) is a uniform protocol for read-write Linked Data. This document defines an abstract syntax (a data model) of Linked Data applications with SPARQL backends, and semantics of CRUD interactions against their resources. [[LINKED-DATA]][[sparql11-overview]]
LDT can be used to design ontology-driven Web application programming interfaces (APIs). It provides facilities to define interactions with application resources declaratively using SPARQL commands. It also provides a standard method to evaluate requests into responses over application ontology and dataset.
Linked Data is a method for publishing structured data, including relationships between data, on the Web using a common data model (RDF). The purpose of Linked Data Templates is to provide means to define read-write Linked Data APIs declaratively using SPARQL and specify a uniform interaction protocol for them.
LDT reconciles ontology-driven Semantic Web, read-write Linked Data and the SPARQL query language. LDT architecture decouples software from domain- or application-specific operations, enabling reuse of machine-processable operation descriptions (for composition, reasoning etc.) as well as generic implementations of both server and client software. It builds on existing standards; no changes or additions to other specifications are required.
Ontology as a declarative representation of the application's operations, dependency on SPARQL, formal valuation of interactions and the support for HATEOAS hypermedia are the main features that distinguish LDT from other Linked Data specifications such as Linked Data Platform [[LDP]]. Rather than prescribing a single interaction model as does LDP, LDT specifies ontology structure and processing rules that allow applications to define custom interaction models.
This document defines a precise semantics for SPARQL-backed Linked Data applications, providing a specification of how Append, Read, Update, Delete (CRUD) interactions change the state of their resources. It does so by establishing the following:
The main LDT concepts are explained below.
An LDT application represents a data space identified by its base URI, in which application resource URIs are usually (but not necessarily) relative to the base URI. The only interface an application provides is RESTful Linked Data: application produces RDF representations when resource URIs are dereferenced, and consumes RDF representations when requested to change resource state.
A service is a datasource from which an LDT application retrieves, and in which it stores RDF data with resource representations. The only interface a service MUST provide is SPARQL 1.1 Protocol [[[sparql11-protocol]]]. That way an application is agnostic to the service implementation details: it can be a native RDF triplestore, an RDBMS or XML database with an RDF wrapper, or any other compliant datasource.
An ontology is an efficient way to define application structure declaratively as a set of instructions for resource representation processing. LDT application uses a single RDF ontology to define operations specific to that application. Ontologies can be exposed and dereferenced, as well as imported and reused by other LDT applications or third-party software.
An operation maps a certain URI pattern to a certain SPARQL command. During request processing, an LDT processor matches request URI against operations and selects the best match (if such exists) that will drive the RDF CRUD processing. Operations are defined using the LDT vocabulary, while the SPARQL commands are defined using SPIN RDF syntax and use a "magic" variable ?this which is bound to the request URI value.
Processes such as content negotiation, URI resolution, constraint validation etc. are separate from operations and out of scope of this document.
Hypermedia, used here in terms of Hypermedia As The Engine Of Application State (HATEOAS), is the final constraint of REST. It enables loose server/client coupling by requiring the client to navigate by transitioning between server-provided application states. It implies that those states need to be URI-identified and provided in response body. LDT addresses this by providing operation parameters that are used to validate and describe requested application states.
This section defines the data model of LDT applications.
In this section we define the abstract syntax REpresentational State Transfer. It sufficient to model a RESTful API independently of the transport protocol.
Request := Method IRI | Method IRI MediaType Body
Response := Status MediaType Body
In this section, we specialize the REST abstract syntax by constraining the message body to RDF serializations and read-write interactions to CRUD.
LDRequest := CRUDMethod IRI | CRUDMethod IRI RDFMediaType Dataset
LDResponse := CRUDStatus RDFMediaType Dataset
CRUDMethod := Append
| Read
| Delete
| Update
CRUDStatus := NotFound
| NoContent
| OK
We use denotational semantics as the formalism for Linked Data semantics.
text/turtle
, text/trig
, application/n-triples
, application/n-quads
, application/ld+json
, application/rdf+xml
, … } Writing RDF graphs [[rdf11-primer]]Append
, Read
, Delete
, Update
}NotFound
, NoContent
, OK
}?
") = absolutePath ∈ IRIabs [[!xpath-functions-31]]?
") = queryString ∈ String [[!xpath-functions-31]]NoType
}NoMatch
}NoMatch
[] topMatch((operation1, precedence1), …, (operationi, precedencei), …, (operationn, precedencen)) = operationi : precedencei ≥ precedencej for all i, jNoMatch
}NoMatch
}=
"))), decodeUri(head(tail(tokenize(head(strings), "=
"))))), argumentPairs(tail(strings)))Evaluation of response to request ∈ LDRequest:
LDResponse : LDRequest → LDResponse × Dataset
NoMatch
→ NotFound
[]Delete
→ NoContent
[]OK
,Read
→ merge(readData(request, app), state(iri(request), paramListArgs(parameters(reqMatch(request, app)), argumentPairs(tokenize(queryString(iri(request))))))) []Read
→ dataset(app) []Append
→ merge(body(request), dataset(app)) []Update
→ merge(body(request), deleteData(request, app)) []Delete
→ deleteData(request, app))HTTPRequest := HTTPMethod IRI | HTTPMethod IRI MediaType Body
HTTPResponse := HTTPStatus MediaType Body
HTTPMethod := GET
| POST
| PUT
| DELETE
HTTPStatus := 200 OK
| 204 No Content
| 404 Not Found
GET
, POST
, PUT
, DELETE
} ⊂ Request Methods [[!HTTP11]]200 OK
, 204 No Content
, 404 Not Found
} ⊂ Response Status Codes [[!HTTP11]]POST
, Append
), (GET
, Read
), (PUT
, Update
), (DELETE
, Delete
))NotFound
, 404 Not Found
), (NoContent
, 204 No Content
), (OK
, 200 OK
))Evaluation of response to request ∈ HTTPRequest:
HTTPResponse : HTTPRequest → HTTPResponse × Dataset
We will use the following set members for all the subsequent response valuation examples:
app
∈ App<https://linkeddatahub.com/>
, ontology
, dataset
)graphParam
∈ Parameter"g"
, <http://www.w3.org/2000/01/rdf-schema#Resource>
, true
)match
∈ Operation"/people/{familyName}"
, describeWithTopic
, deleteWithTopic
, (graphParam
))describeWithTopic
∈ DescribePREFIX foaf: <http://xmlns.com/foaf/0.1/>
DESCRIBE ?this ?primaryTopic
WHERE
{ GRAPH ?g
{ ?this ?p ?o
OPTIONAL
{ ?this foaf:primaryTopic ?primaryTopic .
?primaryTopic
?primaryTopicP ?primaryTopicO
}
}
}
deleteWithTopic
∈ DeletePREFIX foaf: <http://xmlns.com/foaf/0.1/>
DELETE {
GRAPH ?g {
?this ?p ?o .
?primaryTopic ?primaryTopicP ?primaryTopicO .
}
}
WHERE
{ GRAPH ?g
{ ?this ?p ?o
OPTIONAL
{ ?this foaf:primaryTopic ?primaryTopic .
?primaryTopic ?primaryTopicP ?primaryTopicO
}
}
}
The following example shows the ontology of our example application. Operations used in response valuation are encoded as RDF terms using LDT vocabulary and SPIN vocabulary:
@prefix : <https://linkeddatahub.com/ns#> . @prefix ldt: <https://www.w3.org/ns/ldt#> . @prefix owl: <http://www.w3.org/2002/07/owl#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . @prefix sp: <http://spinrdf.org/sp#> . @prefix spl: <http://spinrdf.org/spl#> . : a ldt:Ontology ; owl:imports ldt:, sp: ; rdfs:label "Example ontology" . :PersonItem a ldt:Template ; rdfs:label "Person item" ; ldt:match "/people/{familyName}" ; ldt:param :GraphParam ; ldt:query :DescribeWithTopic ; ldt:update :DeleteWithTopic ; rdfs:isDefinedBy : . :GraphParam a ldt:Parameter ; rdfs:label "Graph parameter" ; spl:predicate :g ; spl:valueType rdfs:Resource ; spl:optional true ; rdfs:isDefinedBy : . :DescribeWithTopic a ldt:Query, sp:Describe ; rdfs:label "Describe with topic" ; sp:text """PREFIX foaf: <http://xmlns.com/foaf/0.1/> DESCRIBE ?this ?primaryTopic WHERE { GRAPH ?g { ?this ?p ?o OPTIONAL { ?this foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }""" ; rdfs:isDefinedBy : . :DeleteWithTopic a ldt:Update, sp:Modify ; rdfs:label "Delete with topic" ; sp:text """PREFIX foaf: <http://xmlns.com/foaf/0.1/> DELETE { GRAPH ?g { ?this ?p ?o . ?primaryTopic ?primaryTopicP ?primaryTopicO . } } WHERE { GRAPH ?g { ?this ?p ?o OPTIONAL { ?this foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }""" ; rdfs:isDefinedBy : .
The following example shows how an HTTP PUT
request with an RDF dataset as its body evaluates as Linked Data Update
request on an LDT application. The evaluation result is a successful HTTP response and operations on the application's dataset.
SPARQL update execution removes the existing description of the requested resource in the dataset. It is followed by a merge of the request body into the dataset. The updated application dataset is a side-effect of the interaction.
HTTP PUT request putRequest
with request body putBody
PUT /people/Berners-Lee HTTP/1.1 Host: linkeddatahub.com Accept: text/turtle, text/trig Content-Type: text/trig @base <https://linkeddatahub.com/people/Berners-Lee> . @prefix c: <https://www.w3.org/ns/ldt/core/domain#> . @prefix foaf: <http://xmlns.com/foaf/0.1/> . @prefix owl: <http://www.w3.org/2002/07/owl#> . <../graphs/c5f34fe9-0456-48e8-a371-04be71529762> { <> a c:Document ; foaf:primaryTopic <#this> . <#this> a foaf:Person ; foaf:isPrimaryTopicOf <> ; owl:sameAs <https://www.w3.org/People/Berners-Lee/card#i> . }
Valuation of putRequest
HTTPResponse [[HTTPStatus MediaType Body]] (putRequest) = (crudStatusToHTTPStatus(ldResponse↓1, crudStatusToHTTPStatusMapping), ldResponse↓2, ldResponse↓3) : ldResponse = (LDResponse [[CRUDStatus RDFMediaType Dataset]] ((httpMethodToCRUDMethod(method(putRequest), httpMethodToCRUDMethodMapping), iri(putRequest), mediaType(putRequest), body(putRequest))))↓1 ldResponse = (LDResponse [[CRUDStatus RDFMediaType Dataset]] ((httpMethodToCRUDMethod(method(putRequest), httpMethodToCRUDMethodMapping), iri(putRequest), mediaType(putRequest), body(putRequest))))↓1 = (LDResponse [[CRUDStatus RDFMediaType Dataset]] ((httpMethodToCRUDMethod(PUT, httpMethodToCRUDMethodMapping), <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)))↓1 = (LDResponse [[CRUDStatus RDFMediaType Dataset]] ((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)))↓1 = (LDResponse [[CRUDStatus RDFMediaType Dataset]] (app(iri((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)))) ((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)))↓1 = (LDResponse [[CRUDStatus RDFMediaType Dataset]] (app(<https://linkeddatahub.com/people/Berners-Lee>)) ((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)))↓1 = (LDResponse [[CRUDStatus RDFMediaType Dataset]] ((<https://linkeddatahub.com/>, ontology, dataset)) ((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)))↓1 = ((reqMatch((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody), (<https://linkeddatahub.com/>, ontology, dataset)) = NoMatch → NotFound [] method((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)) = Delete → NoContent [] OK, conNeg((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)), method((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)) = Read → merge(readData((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody), (<https://linkeddatahub.com/>, ontology, dataset)), state(iri((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)), paramListArgs(parameters(reqMatch((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody), (<https://linkeddatahub.com/>, ontology, dataset))), argumentPairs(tokenize(queryString(iri((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)))))))) [] method((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)) = Append → empty [] method((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)) = Update → empty [] method((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)) = Delete → empty), method((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)) = Read → dataset((<https://linkeddatahub.com/>, ontology, dataset)) [] method((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)) = Append → merge(body((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)), dataset((<https://linkeddatahub.com/>, ontology, dataset))) [] method((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)) = Update → merge(body((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)), deleteData((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody), (<https://linkeddatahub.com/>, ontology, dataset))) [] method((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)) = Delete → deleteData((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody), (<https://linkeddatahub.com/>, ontology, dataset)))↓1 = ((match = NoMatch → NotFound [] Update = Delete → NoContent [] OK, text/turtle, Update = Read → merge(readData((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody), (<https://linkeddatahub.com/>, ontology, dataset)), state(<https://linkeddatahub.com/people/Berners-Lee>, paramListArgs(parameters(match), argumentPairs(tokenize(queryString(<https://linkeddatahub.com/people/Berners-Lee>)))))) [] Update = Append → empty [] Update = Update → empty [] Update = Delete → empty), Update = Read → dataset((<https://linkeddatahub.com/>, ontology, dataset)) [] Update = Append → merge(putBody, dataset((<https://linkeddatahub.com/>, ontology, dataset))) [] Update = Update → merge(putBody, deleteData((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody), (<https://linkeddatahub.com/>, ontology, dataset))) [] Update = Delete → deleteData((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody), (<https://linkeddatahub.com/>, ontology, dataset)))↓1 = ((OK, text/turtle, empty), merge(putBody, deleteData((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody), (<https://linkeddatahub.com/>, ontology, dataset))))↓1 = ((OK, text/turtle, empty), merge(putBody, execDelete(apply(delete(reqMatch((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody), (<https://linkeddatahub.com/>, ontology, dataset))), cons((variable(this), absolutePath(iri((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody)))), argBindings(paramListArgs(parameters(reqMatch((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody), (<https://linkeddatahub.com/>, ontology, dataset))), argumentPairs(tokenize(queryString(iri((Update, <https://linkeddatahub.com/people/Berners-Lee>, text/trig, putBody))))))))), dataset((<https://linkeddatahub.com/>, ontology, dataset)))))↓1 = ((OK, text/turtle, empty), merge(putBody, execDelete(apply(delete(match), cons((variable(this), absolutePath(<https://linkeddatahub.com/people/Berners-Lee>)), argBindings(paramListArgs(parameters(match), argumentPairs(tokenize(queryString(<https://linkeddatahub.com/people/Berners-Lee>))))))), dataset)))↓1 = ((OK, text/turtle, empty), merge(putBody, execDelete(apply(deleteWithTopic, cons((?this, <https://linkeddatahub.com/people/Berners-Lee>), argBindings(paramListArgs((graphParam), argumentPairs(tokenize("")))))), dataset)))↓1 = ((OK, text/turtle, empty), merge(putBody, execDelete(apply("PREFIX foaf: <http://xmlns.com/foaf/0.1/> DELETE { GRAPH ?g { ?this ?p ?o . ?primaryTopic ?primaryTopicP ?primaryTopicO . } } WHERE { GRAPH ?g { ?this ?p ?o OPTIONAL { ?this foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }", cons((?this, <https://linkeddatahub.com/people/Berners-Lee>), argBindings(paramListArgs((graphParam), argumentPairs(()))))), dataset)))↓1 = ((OK, text/turtle, empty), merge(putBody, execDelete(apply("PREFIX foaf: <http://xmlns.com/foaf/0.1/> DELETE { GRAPH ?g { ?this ?p ?o . ?primaryTopic ?primaryTopicP ?primaryTopicO . } } WHERE { GRAPH ?g { ?this ?p ?o OPTIONAL { ?this foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }", cons((?this, <https://linkeddatahub.com/people/Berners-Lee>), argBindings(paramListArgs((graphParam), ())))), dataset)))↓1 = ((OK, text/turtle, empty), merge(putBody, execDelete(apply("PREFIX foaf: <http://xmlns.com/foaf/0.1/> DELETE { GRAPH ?g { ?this ?p ?o . ?primaryTopic ?primaryTopicP ?primaryTopicO . } } WHERE { GRAPH ?g { ?this ?p ?o OPTIONAL { ?this foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }", cons((?this, <https://linkeddatahub.com/people/Berners-Lee>), argBindings(()))), dataset)))↓1 = ((OK, text/turtle, empty), merge(putBody, execDelete(apply("PREFIX foaf: <http://xmlns.com/foaf/0.1/> DELETE { GRAPH ?g { ?this ?p ?o . ?primaryTopic ?primaryTopicP ?primaryTopicO . } } WHERE { GRAPH ?g { ?this ?p ?o OPTIONAL { ?this foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }", cons((?this, <https://linkeddatahub.com/people/Berners-Lee>), ())), dataset)))↓1 = ((OK, text/turtle, empty), merge(putBody, execDelete(apply("PREFIX foaf: <http://xmlns.com/foaf/0.1/> DELETE { GRAPH ?g { ?this ?p ?o . ?primaryTopic ?primaryTopicP ?primaryTopicO . } } WHERE { GRAPH ?g { ?this ?p ?o OPTIONAL { ?this foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }", ((?this, <https://linkeddatahub.com/people/Berners-Lee>))), dataset)))↓1 = ((OK, text/turtle, empty), merge(putBody, execDelete("PREFIX foaf: <http://xmlns.com/foaf/0.1/> DELETE { GRAPH ?g { <https://linkeddatahub.com/people/Berners-Lee> ?p ?o . ?primaryTopic ?primaryTopicP ?primaryTopicO . } } WHERE { GRAPH ?g { <https://linkeddatahub.com/people/Berners-Lee> ?p ?o OPTIONAL { <https://linkeddatahub.com/people/Berners-Lee> foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }", dataset)))↓1 (crudStatusToHTTPStatus(ldResponse↓1, crudStatusToHTTPStatusMapping), ldResponse↓2, ldResponse↓3) = (200 OK, text/turtle, empty)
HTTP response
HTTP/1.1 200 OK
The following example shows how an HTTP GET
evaluates as Linked Data Read
request on an LDT application. The evaluation result is a successful HTTP response with a body that is an RDF dataset. The dataset is a result of a SPARQL query execution on the application dataset, augmented with hypermedia arguments.
The request dereferences the IRI and retrieves the same representation that was stored by the previous example, to which the hypermedia state is appended.
HTTP GET request getRequest
with query string arguments
GET /people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762 HTTP/1.1 Host: linkeddatahub.com Accept: text/turtle, text/trig
Valuation of getRequest
HTTPResponse [[HTTPStatus MediaType Body]] (getRequest) = (crudStatusToHTTPStatus(ldResponse↓1, crudStatusToHTTPStatusMapping), ldResponse↓2, ldResponse↓3) : ldResponse = (LDResponse [[CRUDStatus RDFMediaType Dataset]] ((httpMethodToCRUDMethod(method(getRequest), httpMethodToCRUDMethodMapping), iri(getRequest), mediaType(getRequest), body(getRequest))))↓1 ldResponse = (LDResponse [[CRUDStatus RDFMediaType Dataset]] ((httpMethodToCRUDMethod(method(getRequest), httpMethodToCRUDMethodMapping), iri(getRequest), mediaType(getRequest), body(getRequest))))↓1 = (LDResponse [[CRUDStatus RDFMediaType Dataset]] ((httpMethodToCRUDMethod(GET, httpMethodToCRUDMethodMapping), <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)))↓1 = (LDResponse [[CRUDStatus RDFMediaType Dataset]] ((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)))↓1 = (LDResponse [[CRUDStatus RDFMediaType Dataset]] ((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)))↓1 = (LDResponse [[CRUDStatus RDFMediaType Dataset]] (app(iri((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)))) ((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)))↓1 = (LDResponse [[CRUDStatus RDFMediaType Dataset]] (app(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>)) ((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)))↓1 = (LDResponse [[CRUDStatus RDFMediaType Dataset]] ((<https://linkeddatahub.com/>, ontology, dataset)) ((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)))↓1 = ((reqMatch((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty), app) = NoMatch → NotFound [] method((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)) = Delete → NoContent [] OK, conNeg((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)), method((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)) = Read → merge(readData((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty), (<https://linkeddatahub.com/>, ontology, dataset)), state(iri((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)), paramListArgs(parameters(reqMatch((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty), (<https://linkeddatahub.com/>, ontology, dataset))), argumentPairs(tokenize(queryString(iri((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)))))))) [] method((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)) = Append → empty [] method((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)) = Update → empty [] method((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)) = Delete → empty), method((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)) = Read → dataset((<https://linkeddatahub.com/>, ontology, dataset)) [] method((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)) = Append → merge(body((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)), dataset((<https://linkeddatahub.com/>, ontology, dataset))) [] method((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)) = Update → merge(body((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)), deleteData((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty), (<https://linkeddatahub.com/>, ontology, dataset))) [] method((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)) = Delete → deleteData((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty), (<https://linkeddatahub.com/>, ontology, dataset)))↓1 = ((match = NoMatch → NotFound [] Read = Delete → NoContent [] OK, text/trig, Read = Read → merge(readData((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty), (<https://linkeddatahub.com/>, ontology, dataset)), state(iri((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)), paramListArgs(parameters(match), argumentPairs(tokenize(queryString(iri((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)))))))) [] Read = Append → empty [] Read = Update → empty [] Read = Delete → empty), Read = Read → dataset((<https://linkeddatahub.com/>, ontology, dataset)) [] Read = Append → merge(body((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)), dataset((<https://linkeddatahub.com/>, ontology, dataset))) [] Read = Update → merge(body((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)), deleteData((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty), (<https://linkeddatahub.com/>, ontology, dataset))) [] Read = Delete → deleteData((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty), (<https://linkeddatahub.com/>, ontology, dataset)))↓1 = ((OK, text/trig, merge(readData((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty), (<https://linkeddatahub.com/>, ontology, dataset)), state(iri((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)), paramListArgs(parameters(match), argumentPairs(tokenize(queryString(iri((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty))))))))), dataset((<https://linkeddatahub.com/>, ontology, dataset)))↓1 = ((OK, text/trig, merge(execQuery(apply(query(reqMatch((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty), (<https://linkeddatahub.com/>, ontology, dataset))), cons((variable(this), absolutePath(iri((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)))), argBindings(paramListArgs(parameters(reqMatch((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty), (<https://linkeddatahub.com/>, ontology, dataset))), argumentPairs(tokenize(queryString(iri((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty))))))))), dataset((<https://linkeddatahub.com/>, ontology, dataset))), state(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, paramListArgs(parameters(match), argumentPairs(tokenize(queryString(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>))))))), dataset)↓1 = ((OK, text/trig, merge(execQuery(apply(query(match), cons((variable(this), absolutePath(iri((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty)))), argBindings(paramListArgs(parameters(match), argumentPairs(tokenize(queryString(iri((Read, <https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, NoType, empty))))))))), dataset((<https://linkeddatahub.com/>, ontology, dataset))), state(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, paramListArgs(parameters(match), argumentPairs(tokenize(queryString(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>))))))), dataset)↓1 = ((OK, text/trig, merge(execQuery(apply(describeWithTopic, cons((?this, absolutePath(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>)), argBindings(paramListArgs((graphParam), argumentPairs(tokenize(queryString(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>))))))), dataset), state(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, paramListArgs((graphParam), argumentPairs(tokenize("g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762")))))), dataset)↓1 = ((OK, text/trig, merge(execQuery(apply("PREFIX foaf: <http://xmlns.com/foaf/0.1/> DESCRIBE ?this ?primaryTopic WHERE { GRAPH ?g { ?this ?p ?o OPTIONAL { ?this foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }", cons((?this, <https://linkeddatahub.com/people/Berners-Lee>), argBindings(paramListArgs((graphParam), argumentPairs(tokenize("g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762")))))), dataset), state(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, paramListArgs((graphParam), (("g", "http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762")))))), dataset)↓1 = ((OK, text/trig, merge(execQuery(apply("PREFIX foaf: <http://xmlns.com/foaf/0.1/> DESCRIBE ?this ?primaryTopic WHERE { GRAPH ?g { ?this ?p ?o OPTIONAL { ?this foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }", cons((?this, <https://linkeddatahub.com/people/Berners-Lee>), argBindings(paramListArgs((graphParam), argumentPairs(("g", "http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762")))))), dataset), state(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, ((graphParam, >http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762<))))), dataset)↓1 = ((OK, text/trig, merge(execQuery(apply("PREFIX foaf: <http://xmlns.com/foaf/0.1/> DESCRIBE ?this ?primaryTopic WHERE { GRAPH ?g { ?this ?p ?o OPTIONAL { ?this foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }", cons((?this, <https://linkeddatahub.com/people/Berners-Lee>), argBindings(((graphParam, >http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762<))))), dataset), state(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, ((graphParam, >http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762<))))), dataset)↓1 = ((OK, text/trig, merge(execQuery(apply("PREFIX foaf: <http://xmlns.com/foaf/0.1/> DESCRIBE ?this ?primaryTopic WHERE { GRAPH ?g { ?this ?p ?o OPTIONAL { ?this foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }", cons((?this, <https://linkeddatahub.com/people/Berners-Lee>), ((?g, >http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762<)))), dataset), state(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, ((graphParam, >http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762<))))), dataset)↓1 = ((OK, text/trig, merge(execQuery(apply("PREFIX foaf: <http://xmlns.com/foaf/0.1/> DESCRIBE ?this ?primaryTopic WHERE { GRAPH ?g { ?this ?p ?o OPTIONAL { ?this foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }", ((?this, <https://linkeddatahub.com/people/Berners-Lee>), ((?g, >http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762<)))), dataset), state(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, ((graphParam, >http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762<))))), dataset)↓1 = ((OK, text/trig, merge(execQuery("PREFIX foaf: <http://xmlns.com/foaf/0.1/> DESCRIBE <https://linkeddatahub.com/people/Berners-Lee> ?primaryTopic WHERE { GRAPH >http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762< { <https://linkeddatahub.com/people/Berners-Lee> ?p ?o OPTIONAL { <https://linkeddatahub.com/people/Berners-Lee> foaf:primaryTopic ?primaryTopic . ?primaryTopic ?primaryTopicP ?primaryTopicO } } }", dataset), state(<https://linkeddatahub.com/people/Berners-Lee?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762>, ((graphParam, >http://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762<))))), dataset)↓1
HTTP response
HTTP/1.1 200 OK Content-Type: text/trig @base <https://linkeddatahub.com/people/Berners-Lee> . @prefix ldt: <https://www.w3.org/ns/ldt#> . @prefix c: <https://www.w3.org/ns/ldt/core/domain#> . @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . @prefix foaf: <http://xmlns.com/foaf/0.1/> . @prefix owl: <http://www.w3.org/2002/07/owl#> . <../graphs/c5f34fe9-0456-48e8-a371-04be71529762> { <> a c:Document ; foaf:primaryTopic <#this> . <#this> a foaf:Person ; foaf:isPrimaryTopicOf <> ; owl:sameAs <https://www.w3.org/People/Berners-Lee/card#i> . } <?g=http%3A%2F%2Flinkeddatahub.com%2Fgraphs%2Fc5f34fe9-0456-48e8-a371-04be71529762> ldt:arg [ a <https://linkeddatahub.com/ns#GraphParam> ; ldt:paramName "g" ; rdf:value <https://linkeddatahub.com/graphs/c5f34fe9-0456-48e8-a371-04be71529762> ] .
Prefix | Namespace URI |
---|---|
ldt:
|
https://www.w3.org/ns/ldt#
|
rdf:
|
http://www.w3.org/1999/02/22-rdf-syntax-ns#
|
rdfs:
|
http://www.w3.org/2000/01/rdf-schema#
|
xsd:
|
http://www.w3.org/2001/XMLSchema#
|
sd:
|
http://www.w3.org/ns/sparql-service-description#
|
Applications are orthogonal to operations. Does it make sense to put them under the same namespace?
Domain | Property | Range | Cardinality | ||
---|---|---|---|---|---|
App | ldt:Application | ldt:base | rdfs:Resource | IRI | + |
App | ldt:Application | ldt:ontology | ldt:Ontology | Ontology | 1 |
App | ldt:Application | ldt:service | sd:Service | ???? | 1 |
Domain | Property | Range | Cardinality | ||
---|---|---|---|---|---|
Operation | ldt:Template | ldt:match | xsd:string | IRIPattern | 1 |
Operation | ldt:Template | ldt:query | ldt:Query | Query | 1 |
Operation | ldt:Template | rdfs:isDefinedBy | ldt:Ontology | Ontology | 1 |
Operation | ldt:Template | ldt:update | ldt:Update | Delete | ? |
Operation | ldt:Template | ldt:param | ldt:Parameter | Parameter | * |
Operation | ldt:Template | ldt:extends | ldt:Template | Operation | 1 |
Should we align set names with RDF terms? I.e. ldt:Operation
instead of ldt:Template
?
The range of the rdfs:
properties is not accurate here in terms of RDFS.
Domain | Property | Range | Cardinality | |
---|---|---|---|---|
Operation | ldt:Template | ldt:lang | rdf:List(xsd:string) | ? |
Operation | ldt:Template | ldt:cacheControl | xsd:string | ? |
The HTTP properties are not part of LDT over HTTP yet. We need to introduce HTTP headers.
Also core and HTTP probably should not be under the same namespace.
Domain | Property | Range | Cardinality | ||
---|---|---|---|---|---|
IRI | rdfs:Resource | ldt:arg | Argument | * |