Advanced document reading

In this guide, we’ll use different scenarios to go through the REST API methods (detailing the different headers and basic request bodies) you can use to perform more complex SPARQL queries that might aide you when retrieving documents from Carbon LDP™.

We have created a Postman Collection with all the examples featured in this documentation. The examples for this section are contained in the advanced-document-reading folder.

Introduction

We have already covered how you can retrieve a Carbon LDP™ resource with a simple GET request. Still, applications normally need more than just retrieving a single document. Therefore, we will cover the basics of how to retrieve specific data from your platform using Carbon LDP™’s REST API and SPARQL.

The more you know…

SPARQL is the standard query language and protocol for retrieving Linked Data from the web or RDF triple stores. Basically, if your information follows the RDF standard of being composed by a subject, predicate, and object, which it does when it is stored in Carbon LDP™,
it can be queried by SPARQL.

 

This section of the documentation assumes that you already know how to execute SPARQL queries in Carbon LDP™. If you don’t recall how to do this, please read our documentation on SPARQL querying.

In order to best explain the different ways in which you can use SPARQL in your favor for different application requirements, we initially generated certain data to work with.

First, we generated two containers at the application’s root level, one for books and one for authors. You should end up with something like this:

<http://localhost:8083/books/>

<http://localhost:8083/authors/>

Then, we filled the containers with documents of type ex:Book and ex:Author respectively. These documents were defined with the following properties:

  • ex:Book
    • ex:name: String that contains the book’s name.
    • ex:author: ex:Author that wrote the book.
    • ex:price: Integer that represents the cost of the book.
  • ex:Author
    • ex:name: String that contains the author’s name.
    • ex:citizenship: String that contains the author’s citizenship.

Having these data models in mind, let’s review how you could use different SPARQL functionalities to aid your retrieval of information.

Retrieving only specific properties

It is fairly common for applications to require only certain properties of a document. This can happen, for example, when rendering a list of objects and just displaying their name.

To retrieve only specific properties of a document, you will need to execute a CONSTRUCT SPARQL query. In this case, we will try to retrieve all the documents stored in our book’s container to render a list of their names.

Create the POST request

Create the following HTTP request to execute a SPARQL query on the platform.

POST http://localhost:8083/

HTTP Header Value Required/Optional
Content-Type application/sparql-query Required
Prefer include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts Optional (strongly recommended)
Prefer include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums Optional (strongly recommended)
Accept application/ld+json Optional (defaults to text/turtle)
PREFIX ex: <http://example.org/ns#>
PREFIX ldp: <http://www.w3.org/ns/ldp#>

CONSTRUCT {
    ?book
    ex:name ?bookName .
}
WHERE {
    <http://localhost:8083/books/> ldp:contains ?book .
    OPTIONAL { ?book ex:name ?bookName } .
}

Review the POST request

It is important that before issuing this request, you understand all its parts. Next, you’ll get more information about each part in the request.

Content-Type

Since the request method is POST it requires a body. Therefore, the Content-Type HTTP header tells the platform what kind of content to expect in the body of the request. In the examples we use application/sparql-query (SPARQL) because we are describing a SPARQL query that we need the platform to execute.

Note that it is essential that you include this header with the appropriate value. Otherwise, if you include the header with a valid value other than application/sparql-query your request might be accepted by the platform and a new document can be accidentally created, instead of running a SPARQL query.

Accept

Because of the different results that can be obtained from an SPARQL query, this header will vary depending on the query form that you are executing.

Since we are executing a CONSTRUCT query, we will receive RDF graphs as a result. Hence, the platform’s response can include different RDF Dataset Languages supported by Carbon LDP. These include:

  • CONSTRUCT: RDF Dataset Languages like:
    • JSON-LD: application/ld+json
    • TriG: application/trig
    • Turtle: application/turtle
    • RDF XML: application/rdf+xml
Prefer

In this case, we strongly recommend the usage of the following preference headers:

  • include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts
    • Ensures that every property retrieved via a CONSTRUCT query is contained within a context.
  • include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums
    • Includes the checksums of the resources you are retrieving in the c:checksum property.

It is important to stand out that CONSTRUCT queries don’t return contexts. This is an issue if you’re trying to work with only certain properties of a Carbon LDP document. In Carbon LDP, all properties live within the context of the document that contains them. If a property is not a part of the context in which Carbon LDP stores it, you could be building content that does not exist in your database. To avoid this, we recommend you always include the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts header.

Furthermore, we also recommend including the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header. Having the resource’s checksums will help you ensure that the data you are modifying is accurate before changing anything within your document; this is necessary to maintain data consistency.

Bear in mind that the property c:checksum is not contained within your database, its only generated when requested. What is more, it acts as a System Managed Property, which means you will only be able to read it, but not modify it.

Also, note that including the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header without the include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts header will not allow you to retrieve the document’s checksums.

To learn how to retrieve a document’s checksum without using the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header, go to this section.

Body

The body of the request is a SPARQL query. For this example we are executing a CONSTRUCT query that aims to retrieve only certain properties from all the documents contained in a specific resource. In this case, the query will retrieve all documents that are contained by the resource <http://localhost:8083/books/.

Note that we enclose the property ex:name in an OPTIONAL pattern. Basically, any resource that complies with the rest of the patterns specified in the query will be returned in the final result; regardless of the resource not containing a property specified within an optional clause. Finally, if the remaining resources have a ex:name property, it will be included in the result.

As a direct user of the REST API you should be aware of the freedom CONSTRUCT queries provide. You could end up creating valid RDF graphs that are not actually contained within your database. To prevent this from happening we encourage you to always review the CONSTRUCT portion of your query. Be sure that you are building documents that comply with your application’s object model.

If there is anything from the request body you don’t understand you can check the SPARQL W3C specifications, which Carbon LDP complies to.

Issue the POST request

A successful request will result in HTTP status code 200 OK.

HTTP Header Value
Content-Type application/ld+json
Preference-Applied include="https://carbonldp.com/ns/v1/platform#PreferResultsContexts"
and / or
include="https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums"

As part of the response headers:

  • The Content-Type will describe the format in which the result of the query is returned. If you set an Accept header for your request it will conform to this format.
  • The Preference-Applied will describe the preferences applied by the platform when executing your request. If you set a Prefer header for your request and the platform applied it, the value of that Prefer header will be included.

Review the query result

Furthermore, the response body will contain the result obtained from your query. Naturally, this result may vary depending on the documents that you have in your platform when running the query. For this example, you should get something like this:

[
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-1/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 1"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-258015764"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-1/"
    },
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-2/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 2"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-526036773"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-2/"
    },
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-3/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 3"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-391739953"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-3/"
    },
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-4/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 4"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "440089371"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-4/"
    }
]

The platform returns a graph with the documents that matched your query, along with the specific properties that were requested. Also, note that for every document returned, the platform added the c:checksum property with its corresponding value.

Bear in mind that the property c:checksum is not contained within your database, its only generated when requested. What is more, it acts as a System Managed Property, which means you will only be able to read it, but not modify it.

Retrieving only documents of a specific type

A document can store any type of documents as its children/members, but most of the time, applications only care about documents that follow a particular shape. Or said in other words, have a specific type.

To retrieve only specific properties of document’s with a specific type, you will need to execute a CONSTRUCT SPARQL query. In this case, we will try to retrieve all the documents of type ex:Book to render a list of their names.

Create the POST request

Create the following HTTP request to execute a SPARQL query on the platform.

POST http://localhost:8083/

HTTP Header Value Required/Optional
Content-Type application/sparql-query Required
Prefer include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts Optional (strongly recommended)
Prefer include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums Optional (strongly recommended)
Accept application/ld+json Optional (defaults to text/turtle)
PREFIX ex: <http://example.org/ns#>

CONSTRUCT {
    ?book
    ex:name ?bookName .
}
WHERE {
    ?book a ex:Book .
    OPTIONAL { ?book ex:name ?bookName } .
}

Review the POST request

It is important that before issuing this request, you understand all its parts. Next, you’ll get more information about each part in the request.

Content-Type

Since the request method is POST it requires a body. Therefore, the Content-Type HTTP header tells the platform what kind of content to expect in the body of the request. In the examples we use application/sparql-query (SPARQL) because we are describing a SPARQL query that we need the platform to execute.

Note that it is essential that you include this header with the appropriate value. Otherwise, if you include the header with a valid value other than application/sparql-query your request might be accepted by the platform and a new document can be accidentally created, instead of running a SPARQL query.

Accept

Because of the different results that can be obtained from an SPARQL query, this header will vary depending on the query form that you are executing.

Since we are executing a CONSTRUCT query, we will receive RDF graphs as a result. Hence, the platform’s response can include different RDF Dataset Languages supported by Carbon LDP. These include:

  • CONSTRUCT: RDF Dataset Languages like:
    • JSON-LD: application/ld+json
    • TriG: application/trig
    • Turtle: application/turtle
    • RDF XML: application/rdf+xml
Prefer

In this case, we strongly recommend the usage of the following preference headers:

  • include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts
    • Ensures that every property retrieved via a CONSTRUCT query is contained within a context.
  • include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums
    • Includes the checksums of the resources you are retrieving in the c:checksum property.

It is important to stand out that CONSTRUCT queries don’t return contexts. This is an issue if you’re trying to work with only certain properties of a Carbon LDP document. In Carbon LDP, all properties live within the context of the document that contains them. If a property is not a part of the context in which Carbon LDP stores it, you could be building content that does not exist in your database. To avoid this, we recommend you always include the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts header.

Furthermore, we also recommend including the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header. Having the resource’s checksums will help you ensure that the data you are modifying is accurate before changing anything within your document; this is necessary to maintain data consistency.

Bear in mind that the property c:checksum is not contained within your database, its only generated when requested. What is more, it acts as a System Managed Property, which means you will only be able to read it, but not modify it.

Also, note that including the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header without the include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts header will not allow you to retrieve the document’s checksums.

To learn how to retrieve a document’s checksum without using the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header, go to this section.

Body

The body of the request is a SPARQL query. For this example we are executing a CONSTRUCT query that aims to retrieve only certain properties from all the documents of a certain type. In this case, the query will retrieve all documents of type ex:Book.

Note that we enclose the property ex:name in an OPTIONAL pattern. Basically, any resource that complies with the rest of the patterns specified in the query will be returned in the final result; regardless of the resource not containing a property specified within an optional clause. Finally, if the remaining resources have a ex:name property, it will be included in the result.

As a direct user of the REST API you should be aware of the freedom CONSTRUCT queries provide. You could end up creating valid RDF graphs that are not actually contained within your database. To prevent this from happening we encourage you to always review the CONSTRUCT portion of your query. Be sure that you are building documents that comply with your application’s object model.

If there is anything from the request body you don’t understand you can check the SPARQL W3C specifications, which Carbon LDP complies to.

Issue the POST request

A successful request will result in HTTP status code 200 OK.

HTTP Header Value
Content-Type application/ld+json
Preference-Applied include="https://carbonldp.com/ns/v1/platform#PreferResultsContexts"
and / or
include="https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums"

As part of the response headers:

  • The Content-Type will describe the format in which the result of the query is returned. If you set an Accept header for your request it will conform to this format.
  • The Preference-Applied will describe the preferences applied by the platform when executing your request. If you set a Prefer header for your request and the platform applied it, the value of that Prefer header will be included.

Review the query result

Furthermore, the response body will contain the result obtained from your query. Naturally, this result may vary depending on the documents that you have in your platform when running the query. For this example, you should get something like this:

[
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-1/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 1"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-258015764"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-1/"
    },
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-2/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 2"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-526036773"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-2/"
    },
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-3/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 3"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-391739953"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-3/"
    },
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-4/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 4"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "440089371"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-4/"
    }
]

The platform returns a graph with the documents that matched your query, along with the specific properties that were requested. Also, note that for every document returned, the platform added the c:checksum property with its corresponding value.

Bear in mind that the property c:checksum is not contained within your database, its only generated when requested. What is more, it acts as a System Managed Property, which means you will only be able to read it, but not modify it.

Filtering documents

The previous sections have detailed how to filter documents based on their location in your platform’s structure or their types, but it is fairly common for applications to need to filter them based on their properties instead (or more complex conditions).

To achieve this we can use SPARQL’s FILTER expression. You can find more about it here. In this case, we will try to retrieve all the documents of type ex:Book which ex:price is greater than $5 to render a list of their names.

Create the POST request

Create the following HTTP request to execute a SPARQL query on the platform.

POST http://localhost:8083/

HTTP Header Value Required/Optional
Content-Type application/sparql-query Required
Prefer include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts Optional (strongly recommended)
Prefer include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums Optional (strongly recommended)
Accept application/ld+json Optional (defaults to text/turtle)
PREFIX ex: <http://example.org/ns#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

CONSTRUCT {
    ?book
    ex:name ?bookName ;
    ex:price ?bookPrice .
}
WHERE {
    ?book a ex:Book .
    OPTIONAL { ?book ex:name ?bookName } .
    ?book ex:price ?bookPrice .

    FILTER ( ?bookPrice > xsd:integer( "5" ) )
}

Review the POST request

It is important that before issuing this request, you understand all its parts. Next, you’ll get more information about each part in the request.

Content-Type

Since the request method is POST it requires a body. Therefore, the Content-Type HTTP header tells the platform what kind of content to expect in the body of the request. In the examples we use application/sparql-query (SPARQL) because we are describing a SPARQL query that we need the platform to execute.

Note that it is essential that you include this header with the appropriate value. Otherwise, if you include the header with a valid value other than application/sparql-query your request might be accepted by the platform and a new document can be accidentally created, instead of running a SPARQL query.

Accept

Because of the different results that can be obtained from an SPARQL query, this header will vary depending on the query form that you are executing.

Since we are executing a CONSTRUCT query, we will receive RDF graphs as a result. Hence, the platform’s response can include different RDF Dataset Languages supported by Carbon LDP. These include:

  • CONSTRUCT: RDF Dataset Languages like:
    • JSON-LD: application/ld+json
    • TriG: application/trig
    • Turtle: application/turtle
    • RDF XML: application/rdf+xml
Prefer

In this case, we strongly recommend the usage of the following preference headers:

  • include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts
    • Ensures that every property retrieved via a CONSTRUCT query is contained within a context.
  • include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums
    • Includes the checksums of the resources you are retrieving in the c:checksum property.

It is important to stand out that CONSTRUCT queries don’t return contexts. This is an issue if you’re trying to work with only certain properties of a Carbon LDP document. In Carbon LDP, all properties live within the context of the document that contains them. If a property is not a part of the context in which Carbon LDP stores it, you could be building content that does not exist in your database. To avoid this, we recommend you always include the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts header.

Furthermore, we also recommend including the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header. Having the resource’s checksums will help you ensure that the data you are modifying is accurate before changing anything within your document; this is necessary to maintain data consistency.

Bear in mind that the property c:checksum is not contained within your database, its only generated when requested. What is more, it acts as a System Managed Property, which means you will only be able to read it, but not modify it.

Also, note that including the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header without the include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts header will not allow you to retrieve the document’s checksums.

To learn how to retrieve a document’s checksum without using the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header, go to this section.

Body

The body of the request is a SPARQL query. For this example we are executing a CONSTRUCT query that aims to retrieve only certain properties from all the documents of a specific type, that comply with the condition set within the FILTER expression. In this case, the query will retrieve all documents of type ex:Book. It will retrieve the ex:price property and will filter out the ones that are $5 or less.

Note that we enclose the property ex:name in an OPTIONAL pattern. Basically, any resource that complies with the rest of the patterns specified in the query will be returned in the final result; regardless of the resource not containing a property specified within an optional clause. Finally, if the remaining resources have a ex:name property, it will be included in the result.

As a general rule, we recommend that any property used within a FILTER expression is not enclosed within an OPTIONAL pattern.

As a direct user of the REST API you should be aware of the freedom CONSTRUCT queries provide. You could end up creating valid RDF graphs that are not actually contained within your database. To prevent this from happening we encourage you to always review the CONSTRUCT portion of your query. Be sure that you are building documents that comply with your application’s object model.

If there is anything from the request body you don’t understand you can check the SPARQL W3C specifications, which Carbon LDP complies to.

Issue the POST request

A successful request will result in HTTP status code 200 OK.

HTTP Header Value
Content-Type application/ld+json
Preference-Applied include="https://carbonldp.com/ns/v1/platform#PreferResultsContext"
and / or
include="https://carbonldp.com/ns/v1/platform#PreferDocumentETags"

As part of the response headers:

  • The Content-Type will describe the format in which the result of the query is returned. If you set an Accept header for your request it will conform to this format.
  • The Preference-Applied will describe the preferences applied by the platform when executing your request. If you set a Prefer header for your request and the platform applied it, the value of that Prefer header will be included.

Review the query result

Furthermore, the response body will contain the result obtained from your query. Naturally, this result may vary depending on the documents that you have in your platform when running the query. For this example, you should get something like this:

[
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-1/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 1"
                    }
                ],
                "http://example.org/ns#price": [
                    {
                        "@type": "http://www.w3.org/2001/XMLSchema#integer",
                        "@value": "10"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-258015764"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-1/"
    },
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-3/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 3"
                    }
                ],
                "http://example.org/ns#price": [
                    {
                        "@type": "http://www.w3.org/2001/XMLSchema#integer",
                        "@value": "7"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-391739953"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-3/"
    }
]

The platform returns a graph with the documents that matched your query, along with the specific properties that were requested. Also, note that for every document returned, the platform added the c:checksum property with its corresponding value.

Bear in mind that the property c:checksum is not contained within your database, its only generated when requested. What is more, it acts as a System Managed Property, which means you will only be able to read it, but not modify it.

Paging through documents

Sometimes collections can get very big and retrieving them all together on a single request can take a long time (or even crash).

To avoid this we can use SPARQL’s LIMIT and OFFSET expressions. In this case, we will try to retrieve the second and third most expensive books to render a list of their names and prices.

Create the POST request

Create the following HTTP request to execute a SPARQL query on the platform.

POST http://localhost:8083/

HTTP Header Value Required/Optional
Content-Type application/sparql-query Required
Prefer include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts Optional (strongly recommended)
Prefer include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums Optional (strongly recommended)
Accept application/ld+json Optional (defaults to text/turtle)
PREFIX ex: <http://example.org/ns#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

CONSTRUCT {
    ?book
    ex:name ?bookName ;
    ex:price ?bookPrice .
}
WHERE {
    {
    SELECT DISTINCT ?book {
        ?book a ex:Book .
        ?book ex:price ?bookPrice .
    }
    ORDER BY DESC ( ?bookPrice )
    LIMIT 2
    OFFSET 1
    }
    OPTIONAL { ?book ex:name ?bookName } .
    ?book ex:price ?bookPrice .
}

Review the POST request

It is important that before issuing this request, you understand all its parts. Next, you’ll get more information about each part in the request.

Content-Type

Since the request method is POST it requires a body. Therefore, the Content-Type HTTP header tells the platform what kind of content to expect in the body of the request. In the examples we use application/sparql-query (SPARQL) because we are describing a SPARQL query that we need the platform to execute.

Note that it is essential that you include this header with the appropriate value. Otherwise, if you include the header with a valid value other than application/sparql-query your request might be accepted by the platform and a new document can be accidentally created, instead of running a SPARQL query.

Accept

Because of the different results that can be obtained from an SPARQL query, this header will vary depending on the query form that you are executing.

Since we are executing a CONSTRUCT query, we will receive RDF graphs as a result. Hence, the platform’s response can include different RDF Dataset Languages supported by Carbon LDP. These include:

  • CONSTRUCT: RDF Dataset Languages like:
    • JSON-LD: application/ld+json
    • TriG: application/trig
    • Turtle: application/turtle
    • RDF XML: application/rdf+xml
Prefer

In this case, we strongly recommend the usage of the following preference headers:

  • include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts
    • Ensures that every property retrieved via a CONSTRUCT query is contained within a context.
  • include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums
    • Includes the checksums of the resources you are retrieving in the c:checksum property.

It is important to stand out that CONSTRUCT queries don’t return contexts. This is an issue if you’re trying to work with only certain properties of a Carbon LDP document. In Carbon LDP, all properties live within the context of the document that contains them. If a property is not a part of the context in which Carbon LDP stores it, you could be building content that does not exist in your database. To avoid this, we recommend you always include the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts header.

Furthermore, we also recommend including the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header. Having the resource’s checksums will help you ensure that the data you are modifying is accurate before changing anything within your document; this is necessary to maintain data consistency.

Bear in mind that the property c:checksum is not contained within your database, its only generated when requested. What is more, it acts as a System Managed Property, which means you will only be able to read it, but not modify it.

Also, note that including the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header without the include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts header will not allow you to retrieve the document’s checksums.

To learn how to retrieve a document’s checksum without using the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header, go to this section.

Body

The body of the request is a SPARQL query. For this example we are executing a CONSTRUCT query that aims to retrieve a limited number of resources, of a specific type, and with only certain properties. In this case, the query will retrieve the subjects of a limited number of documents of type ex:Book via a sub-select.

This SELECT retrieves the resources that comply with having a specified type and property. Then, they are ordered by the property stated in the ORDER BY DESC clause. Note that the default declaration in SPARQL is ORDER BY, which executes an ascending ordering of results.

Afterwards, the ordered results will skip the number of entries specified by the OFFSET pattern and the number of subjects retrieved will be limited by the LIMIT clause. For simplicity, we recommend executing all of these within a sub-select that only retrieves the subjects of the resources that comply to the properties you are looking for. Afterwards, you can use these subjects to CONSTRUCT the specific information you want to get from them.

Note that we enclose the property ex:name in an OPTIONAL pattern. Basically, any resource that complies with the rest of the patterns specified in the query will be returned in the final result; regardless of the resource not containing a property specified within an optional clause. Finally, if the remaining resources have a ex:name property, it will be included in the result.

As a direct user of the REST API you should be aware of the freedom CONSTRUCT queries provide. You could end up creating valid RDF graphs that are not actually contained within your database. To prevent this from happening we encourage you to always review the CONSTRUCT portion of your query. Be sure that you are building documents that comply with your application’s object model.

If there is anything from the request body you don’t understand you can check the SPARQL W3C specifications, which Carbon LDP complies to.

[
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-2/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 2"
                    }
                ],
                "http://example.org/ns#price": [
                    {
                        "@type": "http://www.w3.org/2001/XMLSchema#integer",
                        "@value": "3"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-526036773"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-2/"
    },
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-3/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 3"
                    }
                ],
                "http://example.org/ns#price": [
                    {
                        "@type": "http://www.w3.org/2001/XMLSchema#integer",
                        "@value": "7"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-391739953"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-3/"
    }
]

The platform returns a graph with the documents that matched your query, along with the specific properties that were requested. Also, note that for every document returned, the platform added the c:checksum property with its corresponding value.

Bear in mind that the property c:checksum is not contained within your database, its only generated when requested. What is more, it acts as a System Managed Property, which means you will only be able to read it, but not modify it.

Retrieving nested objects

So far we have covered only simple object structures. But sometimes an application needs to retrieve a more complex structure, one that contains nested objects.

Retrieving connected objects can be accomplished by a SPARQL CONSTRUCT query. In this case, we will try to retrieve all documents of type ex:Book to render a list of their names and their author’s names.

Create the POST request

Create the following HTTP request to execute a SPARQL query on the platform.

POST http://localhost:8083/

HTTP Header Value Required/Optional
Content-Type application/sparql-query Required
Prefer include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts Optional (strongly recommended)
Prefer include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums Optional (strongly recommended)
Accept application/ld+json Optional (defaults to text/turtle)
PREFIX ex: <http://example.org/ns#>

CONSTRUCT {
    ?book
    ex:name ?bookName ;
    ex:author ?author .
    ?author ex:name ?authorName .
}
WHERE {
    ?book a ex:Book .
    OPTIONAL { ?book ex:name ?bookName } .
    OPTIONAL{
    ?book ex:author ?author .
    ?author ex:name ?authorName .
    } .
}

Review the POST request

It is important that before issuing this request, you understand all its parts. Next, you’ll get more information about each part in the request.

Content-Type

Since the request method is POST it requires a body. Therefore, the Content-Type HTTP header tells the platform what kind of content to expect in the body of the request. In the examples we use application/sparql-query (SPARQL) because we are describing a SPARQL query that we need the platform to execute.

Note that it is essential that you include this header with the appropriate value. Otherwise, if you include the header with a valid value other than application/sparql-query your request might be accepted by the platform and a new document can be accidentally created, instead of running a SPARQL query.

Accept

Because of the different results that can be obtained from an SPARQL query, this header will vary depending on the query form that you are executing.

Since we are executing a CONSTRUCT query, we will receive RDF graphs as a result. Hence, the platform’s response can include different RDF Dataset Languages supported by Carbon LDP. These include:

  • CONSTRUCT: RDF Dataset Languages like:
    • JSON-LD: application/ld+json
    • TriG: application/trig
    • Turtle: application/turtle
    • RDF XML: application/rdf+xml
Prefer

In this case, we strongly recommend the usage of the following preference headers:

  • include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts
    • Ensures that every property retrieved via a CONSTRUCT query is contained within a context.
  • include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums
    • Includes the checksums of the resources you are retrieving in the c:checksum property.

It is important to stand out that CONSTRUCT queries don’t return contexts. This is an issue if you’re trying to work with only certain properties of a Carbon LDP document. In Carbon LDP, all properties live within the context of the document that contains them. If a property is not a part of the context in which Carbon LDP stores it, you could be building content that does not exist in your database. To avoid this, we recommend you always include the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts header.

Furthermore, we also recommend including the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header. Having the resource’s checksums will help you ensure that the data you are modifying is accurate before changing anything within your document; this is necessary to maintain data consistency.

Bear in mind that the property c:checksum is not contained within your database, its only generated when requested. What is more, it acts as a System Managed Property, which means you will only be able to read it, but not modify it.

Also, note that including the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header without the include=https://carbonldp.com/ns/v1/platform#PreferResultsContexts header will not allow you to retrieve the document’s checksums.

To learn how to retrieve a document’s checksum without using the Prefer, include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums header, go to this section.

Body

The body of the request is a SPARQL query. For this example we are executing a CONSTRUCT query that aims to retrieve all the resources of a specific type, including certain properties that point to nested objects, and specific properties from these objects. In this case, the query will retrieve all the documents of type ex:Book.

Note that we enclose the property ex:name in an OPTIONAL pattern. Basically, any resource that complies with the rest of the patterns specified in the query will be returned in the final result; regardless of the resource not containing a property specified within an optional clause. Finally, if the remaining resources have a ex:name property, it will be included in the result.

Here, we are using an OPTIONAL statement that includes multiple statements. We know that the property ex:author points to another resource, rather than a simple value. Therefore, we want to make sure that the property is only included if the nested resource has a property ex:name. Otherwise, our result would point to the correct resource, but the name, which is a property we want to retrieved, would not be included.

As a direct user of the REST API you should be aware of the freedom CONSTRUCT queries provide. You could end up creating valid RDF graphs that are not actually contained within your database. To prevent this from happening we encourage you to always review the CONSTRUCT portion of your query. Be sure that you are building documents that comply with your application’s object model.

If there is anything from the request body you don’t understand you can check the SPARQL W3C specifications, which Carbon LDP complies to.

Issue the POST request

A successful request will result in HTTP status code 200 OK.

HTTP Header Value
Content-Type application/ld+json
Preference-Applied include="https://carbonldp.com/ns/v1/platform#PreferResultsContext"
and / or
include="https://carbonldp.com/ns/v1/platform#PreferDocumentETags"

As part of the response headers:

  • The Content-Type will describe the format in which the result of the query is returned. If you set an Accept header for your request it will conform to this format.
  • The Preference-Applied will describe the preferences applied by the platform when executing your request. If you set a Prefer header for your request and the platform applied it, the value of that Prefer header will be included.

Review the query result

Furthermore, the response body will contain the result obtained from your query. Naturally, this result may vary depending on the documents that you have in your platform when running the query. For this example, you should get something like this:

[
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/authors/author-1/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Author 1"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-402392604"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/authors/author-1/"
    },
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/authors/author-2/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Author 2"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "743946608"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/authors/author-2/"
    },
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-1/",
                "http://example.org/ns#author": [
                    {
                        "@id": "http://localhost:8083/authors/author-1/"
                    }
                ],
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 1"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-258015764"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-1/"
    },
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-2/",
                "http://example.org/ns#author": [
                    {
                        "@id": "http://localhost:8083/authors/author-2/"
                    }
                ],
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 2"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-526036773"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-2/"
    },
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-3/",
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 3"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "-1646761005"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-3/"
    },
    {
        "@graph": [
            {
                "@id": "http://localhost:8083/books/book-4/",
                "http://example.org/ns#author": [
                    {
                        "@id": "http://localhost:8083/authors/author-1/"
                    }
                ],
                "http://example.org/ns#name": [
                    {
                        "@value": "Book 4"
                    }
                ],
                "https://carbonldp.com/ns/v1/platform#checksum": [
                    {
                        "@value": "440089371"
                    }
                ]
            }
        ],
        "@id": "http://localhost:8083/books/book-4/"
    }
]

The platform returns a graph with the documents that matched your query, along with the specific properties that were requested. Also, note that for every document returned, the platform added the c:checksum property with its corresponding value.

Bear in mind that the property c:checksum is not contained within your database, its only generated when requested. What is more, it acts as a System Managed Property, which means you will only be able to read it, but not modify it.

Improving performance when retrieving checksums

To be able to return a document’s checksum, the platform needs to know the document’s context. While the simplest way of obtaining this is including the Prefer, include="https://carbonldp.com/ns/v1/platform#PreferResultsContexts" header, this may hinder the platform’s performance in large data sets. The following request will detail how to obtain a document’s cheksum without necessarily retrieving its context.

POST http://localhost:8083/

HTTP Header Value Required/Optional
Content-Type application/sparql-query Required
Prefer include=https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums Required
Accept application/ld+json Optional (defaults to text/turtle)
PREFIX ex: <http://example.org/ns#>
PREFIX ldp: <http://www.w3.org/ns/ldp#>
PREFIX c: <https://carbonldp.com/ns/v1/platform#>

CONSTRUCT {
    ?book
    ex:name ?bookName ;
    ex:author ?bookAuthor ;
    c:document ?bookDocument .
}
WHERE {
    <http://localhost:8083/books/> ldp:contains ?book .
    GRAPH ?bookDocument { ?book ex:name ?bookName } .
    OPTIONAL { ?book ex:author ?bookAuthor } .
}

In your request body, you will need to include the GRAPH keyword, which lets you match patterns against named graphs. When including the name of that graph in the c:document property in your CONSTRUCT query, you are letting the platform know which document you are retrieving; allowing it to obtains its corresponding checksum.

It is mandatory that your CONSTRUCT query matches the ?subject c:document ?document pattern to retrieve the document’s checksum.

Also, if your query contains mandatory triples, including one of those triples within the GRAPH keyword will suffice. However, if all the properties your query includes are OPTIONAL, we recommend you include the GRAPH keyword in all of them.

For example, if you were only requesting an OPTIONAL book author, your query would look like this:

PREFIX ex: <http://example.org/ns#>
PREFIX ldp: <http://www.w3.org/ns/ldp#>
PREFIX c: <https://carbonldp.com/ns/v1/platform#>

CONSTRUCT {
    ?book
    ex:author ?bookAuthor ;
    c:document ?bookDocument .
}
WHERE {
    <http://localhost:8083/books/> ldp:contains ?book .
    OPTIONAL { GRAPH ?bookDocument { ?book ex:author ?bookAuthor } } .
}

A successful request will result in HTTP status code 200 OK.

HTTP Header Value
Preference-Applied include="https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums"

Overall, this request will allow you to obtain a document’s checksum by only adding the Prefer, include="https://carbonldp.com/ns/v1/platform#PreferDocumentChecksums" header. The response body you obtain will vary depending on the information stored in your platform, but it should look similar to this:

[
    {
        "@id": "http://localhost:8083/books/book-1/",
        "http://example.org/ns#author": [
            {
                "@id": "http://localhost:8083/authors/author-1/"
            }
        ],
        "http://example.org/ns#name": [
            {
                "@value": "Book 1"
            }
        ],
        "https://carbonldp.com/ns/v1/platform#checksum": [
            {
                "@value": "-258015764"
            }
        ],
        "https://carbonldp.com/ns/v1/platform#document": [
            {
                "@id": "http://localhost:8083/books/book-1/"
            }
        ]
    },
    {
        "@id": "http://localhost:8083/books/book-2/",
        "http://example.org/ns#author": [
            {
                "@id": "http://localhost:8083/authors/author-2/"
            }
        ],
        "http://example.org/ns#name": [
            {
                "@value": "Book 2"
            }
        ],
        "https://carbonldp.com/ns/v1/platform#checksum": [
            {
                "@value": "-526036773"
            }
        ],
        "https://carbonldp.com/ns/v1/platform#document": [
            {
                "@id": "http://localhost:8083/books/book-2/"
            }
        ]
    },
    {
        "@id": "http://localhost:8083/books/book-3/",
        "http://example.org/ns#name": [
            {
                "@value": "Book 3"
            }
        ],
        "https://carbonldp.com/ns/v1/platform#checksum": [
            {
                "@value": "-1646761005"
            }
        ],
        "https://carbonldp.com/ns/v1/platform#document": [
            {
                "@id": "http://localhost:8083/books/book-3/"
            }
        ]
    },
    {
        "@id": "http://localhost:8083/books/book-4/",
        "http://example.org/ns#author": [
            {
                "@id": "http://localhost:8083/authors/author-1/"
            }
        ],
        "http://example.org/ns#name": [
            {
                "@value": "Book 4"
            }
        ],
        "https://carbonldp.com/ns/v1/platform#checksum": [
            {
                "@value": "440089371"
            }
        ],
        "https://carbonldp.com/ns/v1/platform#document": [
            {
                "@id": "http://localhost:8083/books/book-4/"
            }
        ]
    }
]

Please note that the result retrieved with this query is not contained within a graph. In order to later use this information with the platform, you should parse it into a valid format. Finally, bear in mind that the properties c:checksum and c:document are not contained within your database and act as System Managed Properties, which means you will only be able to read them, but not modify them.

Conclusion

By executing more complex SPARQL queries, the application can control the data it is going to retrieve and reduce the number of requests it executes. Furthermore, via different Prefer headers, Carbon LDP provides all the necessary information to work with partial documents through the REST API. However, this functionality should be used responsibly given the amount of freedom SPARQL provides when building RDF graphs. Be sure that you always build documents that comply with your application’s object model.

Normally, developers will prefer use of the Carbon LDP™ Workbench (GUI) and an SDK, which together simplify the process of building and working with the platform. Still, all functions of the platform can be accessed through the REST API and those developers who understand the REST API may find it advantageous to use in some cases.