Linked Data Patch Format (LD Patch) defines a language for expressing a sequence of operations to apply to a RDF Graph; it is suitable for use with the HTTP PATCH method. The "text/ldpatch" media type is used to identify such Patch documents.
@@You need to have a custom SotD paragraph. Maybe give a succinct description of your spec's status.@@
Linked Data "describes a method of publishing structured data so that it can be interlinked and become more useful. It builds upon standard Web technologies such as HTTP, RDF and URIs, but rather than using them to serve web pages for human readers, it extends them to share information in a way that can be read automatically by computers. This enables data from different sources to be connected and queried." (source Wikipedia).
This document defines the Linked Data Patch Format (LD Patch), a format for describing changes to apply to Linked Data. It is suitable for use with HTTP PATCH [@@RFC5789], a method to perform partial modifications to Web resources. The "text/ldpatch" media type is used to identify such LD Patch documents.
An instance of the LD Patch language (or LD Patch document) defines a list of operations to be performed against an RDF Graph, namely the addition or removal of RDF triples in this graph.
The following RDF Graph will be used as an example through this specification. It describes the relation between a person named Tim Berners-Lee (denoted by <http://example.org/timbl#>
) and two events he attended.
@prefix schema: <http://schema.org/> . @prefix profile: <http://ogp.me/ns/profile#> . @prefix ex: <http://example.org/vocab#> . <http://example.org/timbl#> a schema:Person ; schema:alternateName "TimBL" ; profile:first_name "Tim" ; profile:last_name "Berners-Lee" ; schema:workLocation [ schema:name "W3C/MIT" ] ; schema:attendee _:b1, _:b2 ; ex:preferredLanguages ( "en" "fr" ). _:b1 ; schema:name "F2F5 - Linked Data Platform" ; schema:url <https://www.w3.org/2012/ldp/wiki/F2F5> . _:b2 a schema:Event ; schema:name "TED 2009" ; schema:startDate "2009-02-04" . schema:url <http://conferences.ted.com/TED2009/> .
The following is an example HTTP Patch request, conveying an LD Patch document:
PATCH /timbl HTTP/1.1 Host: example.org Content-Length: 478 Content-Type: text/ldpatch If-Match: "abc123" @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . @prefix schema: <http://schema.org> . @prefix profile: <http://ogp.me/ns/profile#> . @prefix ex: <http://example.org/vocab#> . Delete <#> profile:first_name "Tim" . Add <#> profile:first_name "Timothy" . UpdateList <#> ex:preferredLanguages 1>2 ( "fr-CH" ) . Bind ?event <#> /schema:attendee[/schema:url = <http://conferences.ted.com/TED2009/>] . Add ?event rdf:type schema:Event . Bind ?ted <http://conferences.ted.com/TED2009/> /-schema:url! . Delete ?ted schema:startDate "2009-02-04". Add ?ted schema:location _:loc . Add _:loc schema:name "Long Beach, California" . Add _:loc schema:geo _:geo . Add _:geo schema:latitude "33.7817" . Add _:geo schema:longitude "-118.2054" .
This example introduces most features of the LD Patch format: @prefix and prefixed names, the
The following is the resulting (patched) document.
@prefix schema: <http://schema.org/> . @prefix profile: <http://ogp.me/ns/profile#> . @prefix ex: <http://example.org/vocab#> . <http://example.org/timbl#> a schema:Person ; schema:alternateName "TimBL" ; profile:first_name "Timothy" ; profile:last_name "Berners-Lee" ; schema:workLocation [ schema:name "W3C/MIT" ] ; schema:attendee _:b1, _:b2 ; ex:preferredLanguages ( "en" "fr-CH" ). _:b1 ; schema:name "F2F5 - Linked Data Platform" ; schema:url <https://www.w3.org/2012/ldp/wiki/F2F5> . _:b2 a schema:Event ; schema:name "TED 2009" ; schema:url <http://conferences.ted.com/TED2009/> ; schema:location [ schema:name "Long Beach, California"; schema:geo [ schema:latitude "33.7817" ; schema:longitude "-118.2054" ] . ] .
A LD Patch document is made of a prologue and a list of statements, where the order is relevant. The prologue declares a numver of
LD Patch offers the possibility to abbreviate URIs by using Turtle's @prefix directive that allows declaring a short prefix name for a long prefix of repeated URIs. This is useful for many RDF vocabularies that are all defined in nearby namespace URIs, possibly using XML's namespace mechanism that works in a similar fashion.
Once a prefix such as @prefix foo: <http://example.org/ns#>
is defined, any mention of a URI later in the document may use a foo:
to stand for the longer URI. So for example, the foo:bar
is a shorthand for the URI <http://example.org/ns#bar>
.
LD Patch borrows much of its syntax to Turtle (@@@ref) and SPARQL (@@@ref) for describing nodes. IRIs (either abbreviated or not) and literals represent the corresponding node in the graph being patched. Blank nodes, on the other hand, pose a problem, as they have no global identifier. Indeed, blank node identifiers have their scope limited to the document in which they appear. As a consequence, whenever a blank node identifiers appears in an LD Patch document, it is understood to denote a fresh blank node, that needs to be created in the patched RDF graph. (@@@ remark that this is different from RDF-Patch)
In order to be able to address blank nodes already present in the graph, LD Patch has two mechanisms:
LD Patch uses path expressions to describe possible routes through a graph between two graph nodes. The main goal is to allow addressing a blank node by “walking” the arcs of the graph from an already identified node. A path is composed by a number of steps, which can be of three kinds:
rdf:rest
arcs and one rdf:first
arc in order to reach the corresponding member of an RDF collection.Note that each step can, in general, result in several matching nodes, as a given node can have several (incoming or outgoing) arcs with the same predicate.
A Path may also contains
The path below (taken from our running example) will look for all the events attended by the starting node, and keep only those having <http://conferences.ted.com/TED2009/>
as their URL.
/schema:attendee[/schema:url = <http://conferences.ted.com/TED2009/>]
The Bind operation is used to create a new variable by binding or assigning an RDF Term to the variable. The bound variable has a global scope. Use of a given variable name anywhere in an LD Patch document identifies the same variable (@@@ but a variable _can_ be redefined). Following the example above, the Bind operation creates a new variable called event
, starting from the RDF Term <#>
and following the path expression /schema:attendee[/schema:url = <http://conferences.ted.com/TED2009/>]
in order to identify the RDF Term to which this variable will point -- i.e. _:b2
.
... Bind ?event <#> /schema:attendee[/schema:url = <http://conferences.ted.com/TED2009/>] . ...
The Bind operation is defined by three components:
The Add operation is used to add or append new RDF triples to the existing graph. To add new RDF triple, the operation requires a
... Add <#> profile:first_name "Timothy" . ... Add ?event rdf:type schema:Event . ...When the third component is an
The Delete operation is used to remove a single RDF triple from the existing graph. The syntax for the Delete operation requires a
... Delete <#> profile:first_name "Tim" . ... Delete ?ted schema:startDate "2009-02-04". ...
The UpdateList operation is used to update the members of an RDF collection (@@@ref). That collection is supposed to be the object of a triple, specified by its
... UpdateList <#> ex:preferredLanguages 1>2 ( "fr-CH" ) . ...
UpdateList works in a similar way to slicing in Python or similar languages. In general, it replaces a part (“slice”) of a list by another list. To remove members, one can replace them by an empty list. To insert new values between two members, one can set a non empty list to the empty slice comprised between those two members.
In LD Patch, a slice is described by two positive integers separated by ">". The second integer can be omitted, in that case it denotes the end of the list. If both integers are omitted (i.e. ">" alone), this denotes by convention the empty slice at the end of the list. @@@TODO eplain that indexes start at 0 @@@TODO give some examples here.
LDPatch ::= Prologue Statement*Prologue ::=prefixID *Statement ::=Bind |Add |Delete |UpdateList Bind ::= ("Bind" | "B")Var Value Path ? "."Add ::= ("Add" | "A")Subject Predicate (Object |List ) "."Delete ::= ("Delete" | "D")Subject Predicate Object "."UpdateList ::= ("UpdateList" | "UL")Subject Predicate Slice List "."Subject ::=iri |BlankNode |Var Predicate ::=iri Object ::=iri |BlankNode |literal |Var Value ::=iri |literal |Var List ::= '('Object * ')'Path ::= (Step |Constraint )*Step ::= '/' ( '-'iri |iri |INDEX )Constraint ::= '['Path ( '='Value )? ']' | '!'Slice ::=INDEX ? '>'INDEX ?INDEX ::= [0-9]+ // copied from SPARQL // supposed to be `Var ::= VAR1 | VAR2` but here only VAR1Var ::= '?'VARNAME VARNAME ::= (PN_CHARS_U | [0-9] ) (PN_CHARS_U | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040] )* // copied from TurtleprefixID ::= "@prefix" PNAME_NS IRIREF "."Literal ::=RDFLiteral |NumericLiteral |BooleanLiteral NumericLiteral ::=INTEGER |DECIMAL |DOUBLE RDFLiteral ::=String (LANGTAG | '^^'iri )?BooleanLiteral ::= 'true' | 'false'String ::=STRING_LITERAL_QUOTE |STRING_LITERAL_SINGLE_QUOTE |STRING_LITERAL_LONG_SINGLE_QUOTE |STRING_LITERAL_LONG_QUOTE iri ::=IRIREF |PrefixedName PrefixedName ::=PNAME_LN |PNAME_NS BlankNode ::=BLANK_NODE_LABEL |ANON IRIREF ::= '<' ([^#x00-#x20<>"{}|^`\] |UCHAR )* '>' /* #x00=NULL #01-#x1F=control codes #x20=space */PNAME_NS ::=PN_PREFIX ? ':'PNAME_LN ::=PNAME_NS PN_LOCAL BLANK_NODE_LABEL ::= '_:' (PN_CHARS_U | [0-9]) ((PN_CHARS | '.')*PN_CHARS )?LANGTAG ::= '@' [a-zA-Z]+ ('-' [a-zA-Z0-9]+)*INTEGER ::= [+-]? [0-9]+DECIMAL ::= [+-]? [0-9]* '.' [0-9]+DOUBLE ::= [+-]? ([0-9]+ '.' [0-9]*EXPONENT | '.' [0-9]+EXPONENT | [0-9]+EXPONENT )EXPONENT ::= [eE] [+-]? [0-9]+STRING_LITERAL_QUOTE ::= '"' ([^#x22#x5C#xA#xD] |ECHAR |UCHAR )* '"' /* #x22=" #x5C=\ #xA=new line #xD=carriage return */STRING_LITERAL_SINGLE_QUOTE ::= "'" ([^#x27#x5C#xA#xD] |ECHAR |UCHAR )* "'" /* #x27=' #x5C=\ #xA=new line #xD=carriage return */STRING_LITERAL_LONG_SINGLE_QUOTE ::= "'''" (("'" | "''")? ([^'\] |ECHAR |UCHAR ))* "'''"STRING_LITERAL_LONG_QUOTE ::= '"""' (('"' | '""')? ([^"\] |ECHAR |UCHAR ))* '"""'UCHAR ::= '\\u'HEX HEX HEX HEX | '\\U'HEX HEX HEX HEX HEX HEX HEX HEX ECHAR ::= '\' [tbnrf"'\]WS ::= #x20 | #x9 | #xD | #xA /* #x20=space #x9=character tabulation #xD=carriage return #xA=new line */ANON ::= '['WS * ']'PN_CHARS_BASE ::= [A-Z] | [a-z] | [#x00C0-#x00D6] | [#x00D8-#x00F6] | [#x00F8-#x02FF] | [#x0370-#x037D] | [#x037F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]PN_CHARS_U ::=PN_CHARS_BASE | '_'PN_CHARS ::=PN_CHARS_U | '-' | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040]PN_PREFIX ::=PN_CHARS_BASE ((PN_CHARS | '.')*PN_CHARS )?PN_LOCAL ::= (PN_CHARS_U | ':' | [0-9] |PLX ) ((PN_CHARS | '.' | ':' |PLX )* (PN_CHARS | ':' |PLX ))?PLX ::=PERCENT |PN_LOCAL_ESC PERCENT ::= '%'HEX HEX HEX ::= [0-9] | [A-F] | [a-f]PN_LOCAL_ESC ::= '\' ('_' | '~' | '.' | '-' | '!' | '$' | '&' | "'" | '(' | ')' | '*' | '+' | ',' | ';' | '=' | '/' | '?' | '#' | '@' | '%')
The LD Patch data model makes use of the commonly defined Abstract Data Types Set, List and Option, used here as type constructors. For example, Set(A)
denotes the type for the sets of elements of type A
. We assume that they come with their common operations, such as the function size : Set → Int
.
LDPatch ::=List (Statement )Statement ::=Add |AddList |Delete |Bind |UpdateList Add ::= (Subject ,Predicate ,Object )AddList ::= (Subject ,Predicate ,List (Object ))Delete ::= (Subject ,Predicate ,Object )Bind ::= (Var ,Value ,Path )UpdateList ::= (Subject ,Predicate ,Slice ,List (Object ))Path ::=List (PathElement )PathElement ::=Step |Constraint Step ::=StepForward |StepBackward |StepAt Constraint ::=Filter | UNICITY_CONSTRAINTStepForward ::=IRI StepBackward ::=IRI StepAt ::=Integer Filter ::= (Path , Option(Value ))Slice ::=Range |EverythingAfter | ENDRange ::= (Integer ,Integer )EverythingAfter ::=Integer Subject ::=IRI |BlankNode |Var Predicate ::=IRI Object ::=IRI |BlankNode |Literal |Var Value ::=IRI |Literal |Var Var ::=String IRI ::= RDF URI-reference http://www.w3.org/TR/2014/REC-rdf11-concepts-20140225/#section-IRIs as subsequently restricted by SPARQL http://www.w3.org/TR/rdf-sparql-query/#docTerminologyBlankNode ::= RDF blank node http://www.w3.org/TR/2014/REC-rdf11-concepts-20140225/#section-blank-nodesLiteral ::= RDF Literal http://www.w3.org/TR/2014/REC-rdf11-concepts-20140225/#section-Graph-LiteralString ::= a Unicode String
The denotational RDF semantics makes use of the set-builder notation for building the RDF sets.
@@TODO@@
LD Patch abides to the semantics of the HTTP PATCH method, in that the server MUST apply the entire set of changes atomically and never provide (e.g., in response to a GET during this operation) a partially modified representation. If the entire patch document cannot be successfully applied (e.g., one of the instructions has failed), then the server MUST NOT apply any of the changes.