Issue-34 - Aggregation: simple proposal
Contents
1 Overview
1.1 Vocabulary
The two main classes of object here are ldp:Containers and ldp:Aggregations, For detailed definitions see: Ontology sections It is argued there that ldp:Containers do not have any members in common with ldp:Aggregations.
1.2 Example Summary
We start with the following:
- existing server:
http://localhost:9000
- existing container:
http://localhost:9000/2013/
We show how we can use LDP as it is with no changes to create an aggregation container. By LDP as it is we mean the LDP with a strict notion of containership, ie a DELETE of a container deletes all the LDPRs it contains. We do this by:
- creating an resource that is a member of the container and that will be named http://localhost:9000/2013/aggReg
- <aggReg> LDPR defines an ldp:Aggregation http://localhost:9000/2013/aggReg#ion
- editing the aggregate collection using either PUT or PATCH
- deleting the aggregate collection using either PUT or PATCH
In both cases PATCH should just be thought of as an efficiency improvement over the equivalent method using PUT.
1.3 Known differences from an LDPC
- An Aggregation container is not an LDPC. It is a resource defined inside or as an LDPR: :Aggregation owl:disjointWith :Aggregation .
- To create an Aggregation container one creates an LDPR, by POSTing the graph that describes the aggregation container to the LDPC
- Deleting an Aggregation container does NOT delete the resources it contains, deleting an LDPC deletes the resources it contains.
Note that all of this ( except for the efficiency improvements using PATCH ) work with LDP as it is currently defined.
2 Example Interaction Scenario
Let us start with an empty collection http://localhost:9000/2013/
.
2.1 Create resources
First we create a foaf:PersonalProfileDocument in the collection at /2013/. This can be done on the command line with curl like this:
$ curl -i -X POST -H "Expect:" -H "Content-Type: text/turtle" -H "Slug: card" http://localhost:9000/2013/ --data-binary @card.ttl HTTP/1.1 201 Created Location: card Content-Length: 0
The Location header shows that the resulting url is http://localhost:9000/2013/card
( note that, here I use a Slug: card
Header to name the resource as proposed in Issue 43, to get a nice
human readable name )
2.2 Create a new aggregation inside the container
Create an Aggregation container named aggReg inside the same collection. You can do this by using the telnet program. Connect to the remote server.
$ telnet localhost 9000
Then paste in the HTTP header & body. (Getting the Content-Length right is sometimes tricky)
POST /2013/ HTTP/1.1 Host: localhost:9000 Accept: */* User-Agent: curl/7.27.0 Content-Length: 200 Slug: aggReg Content-Type: text/turtle @prefix : <http://www.w3.org/ns/ldp#>. @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . <> a :Document; :primaryTopic <#ion> . <#ion> a :Aggregation; rdfs:member <card>, <http://www.w3.org/People/Berners-Lee/card> .
This will return a response from the server saying the http://localhost:9000/2013/aggReg resource was created.
HTTP/1.1 201 Created Location: aggReg Content-Length: 0
This resource defines the aggregate collection http://localhost:9000/2013/aggReg#ion, which contains the resource created by the collection, and a remote resource, namely Tim Berner's Lee's foaf profile. Tim Berner's Lee's Profile is of course not under the control of this localhost server.
Currently I distinguish between the LDPR <http://localhost:9000/2013/aggReg> and the LDP Aggregation http://localhost:9000/2013/aggReg#ion just to help with clarity. It may be that there is some overlap between the ldp:Aggregation and an LDPR but it would be I don't want to settle the issue immediately.
So at present we have 2 Collections:
First the initial LDP Container:
$ curl -i -X GET -H "Accept: text/turtle" http://localhost:9000/2013/ HTTP/1.1 200 OK Content-Type: text/turtle Content-Length: 97 @prefix : <http://www.w3.org/ns/ldp#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . <> a :Container; rdfs:member <aggReg> , <card> .
This Container contains two LDPRs <aggReg> and <card> .
And secondly the Collection defined inside the LDPR </2013/aggReg> and named </2013/aggReg#ion>.
2.3 Edit the Aggregation Collection
This can be done in a number of ways.
2.3.1 Alternative 1: Using HTTP PUT to replace the content
This would work as the spec is written now. Imagine you wish to add a new person to that aggregation, then you can take the previous content with the previous change and PUT the new representation in place of the old one.
PUT /2013/aggReg HTTP/1.1 Host: localhost:9000 User-Agent: curl/7.27.0 Content-Length: 200 Content-Type: text/turtle @prefix : <http://www.w3.org/ns/ldp#> . <> a :Document; :primaryTopic <#ion> . <#ion> a :Aggregation; rdfs:member <card>, <http://www.w3.org/People/Berners-Lee/card>, <http://b4mad.net/FOAF/goern.rdf> .
If the server responds with a 200 then the </2013/aggReg#cnt> collection contains the new member <http://b4mad.net/FOAF/goern.rdf> .
2.3.2 Alternative 2: Using PATCH
Of course the above is a clumsy and inefficient. With a patching mechanism - which the group is commited to come up with - the append could be expressed a lot more easily, like this:
PATCH /2013/aggReg HTTP/1.1 Host: localhost:9000 User-Agent: curl/7.27.0 Content-Length: 90 Content-Type: text/some-patch-language @prefix : <http://www.w3.org/ns/ldp#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . INSERT { <#ion> rdfs:member <http://b4mad.net/FOAF/goern.rdf> . }
Deletion could work the same way, and combining them one gets a full update:
PATCH /2013/aggReg HTTP/1.1 Host: localhost:9000 User-Agent: curl/7.27.0 Content-Length: 90 Content-Type: text/turtle @prefix ldp: <http://www.w3.org/ns/ldp#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . DELETE { <#ion> rdfs:member <http://b4mad.net/FOAF/goern.rdf> . } INSERT { <#ion> rdfs:member <http://novaspivack.typepad.com/foaf.rdf> . }
here we remove <http://b4mad.net/FOAF/goern.rdf> and then add <http://novaspivack.typepad.com/foaf.rdf>, in the same connection.
2.4 Delete your aggregation
There are two ways one can do that.
2.4.1 Alternative 1: Delete the resource containing the collection
The simplest way is just to delete the resource in which the aggregated collection
Request:
curl -i -X DELETE -H "Expect:" http://localhost:9000/2013/aggReg HTTP/1.1 200 OK Content-Length: 0
This deletes the /2013/aggReg resource
$ curl -i -H "Expect:" http://localhost:9000/2013/aggReg HTTP/1.1 404 Not Found
but not the members of the collection
$ curl -i -H "Expect:" http://localhost:9000/2013/ HTTP/1.1 200 Ok @prefix : <http://www.w3.org/ns/ldp#> . <> a :Container; rdfs:member <card> .
Ie an HTTP GET on card will still succeed.
2.4.2 Alternative 2: Remove with a PATCH
If for some reason there are other things inside the collection that should be kept, then one can just remove the collection definition itself
PATCH /2013/aggReg HTTP/1.1 Host: localhost:9000 User-Agent: curl/7.27.0 Content-Length: 90 Content-Type: text/turtle @prefix : <http://www.w3.org/ns/ldp#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . DELETE { <#ion> ldp:member ?q . ?s ?r <#ion> . }
Here the aggReg document still exists
$ curl http://localhost:9000/2013/aggReg @prefix : <http://www.w3.org/ns/ldp#> . <> a :Document .
But it no longer contains the primary topic. The collection still contains all the members.
$ curl -i -H "Accept: text/turtle" http://localhost:9000/2013/ HTTP/1.1 200 OK Content-Type: text/turtle Content-Length: 97 @prefix : <http://www.w3.org/ns/ldp#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . <> a :Container; rdfs:member <aggReg> , <card> .
3 Further Open Questions
3.1 LDPAs and LDPRs
3.1.1 can LDPAs be LDPRs?
It seems very reasonable to have LDPRs be LDPAs.
{ [] owl:intersectionOf ( ldpx:Resource ldpx:Aggregation ) owl:differentFrom owl:Nothing . } defendedBy [ = g5; foaf:member <http://bblfish.net/people/henry/card#me> ]; = :lemma5 .
That is compatible with my example. It is just that in the example I tried not to take the special case. Essentally it would allow
<http://localhost:9000/2013/aggReg> to contain { <> a ldpx:Aggregation . }
3.1.2 Must LDPAs be LDPRs ?
Others may want to claim that
{ ldp:Aggregation rdfs:subClassOf ldp:Resource . } defendedBy g6; owl:sameAs :c6 .
3.2 Inclusion of LDPC contents in an LDPA ?
It may be useful to be able to state in one line that all the members of an LDPC are members of an LDPA. (use case?) If so then a membership inclusion relation could do that by reference.
4 Notes
4.1 Representing containment and/or aggregation links
- paraphrased email Steve Battle: I think we should avoid overloading existing aggregation properties because we may want the option to add existing (i.e. unmanaged) resources to a container via aggregation (e.g. using rdfs:member or its ilk). Indeed, I see this as the function of the ldp:membershipPredicate; as a convenience to create a structured aggregation/collection over and above the container structure.
4.2 Email threads that have been incorporated above
- 2012-12 Threads:
- Andy's
- Henry's Simple Proposal with detailed answers to questions
- 2013-01 threads: