The examples in this post can be replicated using the following methods:
- Access the GraphQL service endpoint at https://graphql.demo.openlinksw.com/graphql.
- Using curl via command-line interface.
- Using a GraphQL-capable GUI client.
-
query HitchhikerGuideFilm { title(id: "tt0371724") { title startYear numVotes averageRating directors { name } writers { name } principals { id name birthYear } } } -
query GetName { name(id:"nm0002490") { name birthYear deathYear profesion { label } knownForTitle { title startYear averageRating } } } -
query TenTitlesIn2022 { titles(first: 10, startYear: 2022) { id title averageRating numVotes startYear } }
Executing example 1 with cURL:
curl --request POST \
--url https://graphql.demo.openlinksw.com/graphql \
--header 'Content-Type: application/json' \
--data '{"query":"query HitchhikerGuide {\n\ttitle(id: \"tt0371724\") {\n\t\ttitle\n\t\tstartYear\n\t\tnumVotes\n\t\taverageRating\n\t\tdirectors {\n\t\t\tname\n\t\t}\n\t\t writers {\n\t\t\t\t\tname\n\t\t\t\t}\n\t\tprincipals {\n\t\t\tid\n\t\t\tname\n\t\t\tbirthYear\n\t\t}\n\t}\n}\n","operationName":"HitchhikerGuide"}'
IRIs in results
When a field is defined as an ObjectProperty, i.e., when it is a reference to an RDF Class, the result would contains the IRI string reference to that object.
In the following example, countries is defined as:
r:countries a owl:ObjectProperty ;
rdfs:isDefinedBy s:schema ;
rdfs:domain r:Region ;
rdfs:range c:Country ;
gql:type gql:Array ;
gql:field gql:countries .
Thus, if it is asked via —
query {
regions (code:["AN"]) {
name
countries
}
}
— it would produce —
{
"data": {
"regions": [
{
"name": "Antarctica",
"countries": [
"http://example.org/country/AQ"
]
}
]
}
}
Variables
The GraphQL protocol may have as an input parameter variables. This parameter is used to bind variables and contains key/value pairs in a JSON object. Here is an example:
Query text
query CountriesForRegion($ccode: String, $ccodes: [String]) {
region(code: $ccode) {
name
code
countries(code: $ccodes) {
name
code3
country_code
}
}
}
Variables document
{
"ccode": "AM",
"ccodes": [
"US",
"CL",
"BR"
]
}
NOTE: The variables JSON document is not a part of a GraphQL document. That is, it is a separate input parameter for the endpoint.
Here is a cURL example for the above request:
curl --request POST \
--url https://graphql.demo.openlinksw.com/graphql \
--header 'Content-Type: application/json' \
--data '{"query":"query CountriesForRegion($ccode: String, $ccodes: [String]) {\n region(code: $ccode) {\n name\n code\n countries(code: $ccodes) {\n name\n code3\n country_code\n }\n }\n}\n","variables":{"ccode":"AM","ccodes":["US","CL","BR"]},"operationName":"CountriesForRegion"}'
Here is an example involving IRIs.
Query text
query FiveEmployeesFiltered($iri: String!) {
Employees(iri: $iri) {
employeeid
iri
firstname
lastname
homephone
hiredate
notes
}
}
Variables document
{
"iri" : "http://[host:port]/Demo/employees/EmployeeID/4#this"
}
Default Variables
In certain cases, we might want to have a default behavior for a parameterized query; to achieve this, we can use default values for variables.
query MyQuery($code: String! = "GB") {
country(code: $code) {
name
code3
region {
name
}
}
}
Conditional directives
A query can have conditional blocks to include or exclude certain blocks of fields or fragments in the selection set. The supported built-in directives are @include and @skip which should be self explanatory. These are used together with an if argument which is boolean. These are particularly useful when developing interfaces, and certain blocks may be used for diagnostics, etc.
Here is an example.
Note the directive use in inlined fragment — ... @include(if: $expand) — and in detached fragment — ...regionFields @include(if: $extended).
Query text
query CountryByCode($expand: Boolean! = true, $extended: Boolean!, $skip: Boolean!) {
country(code: "AQ") {
code
... @include(if: $expand) {
name
code3
}
country_code @skip(if: $skip)
region @include(if: $expand) {
code
...regionFields @include(if: $extended)
}
}
}
fragment regionFields on region {
name
ccode
population
}
Variables document
{
"expand": true,
"skip": false,
"extended": false
}
Searching and filtering
The common use of field:value gives an equality; therefore, the following extensions are added:
- free text search on fields in the form of special argument,
contains: - filters for less-than, greater-than, etc., as a special value object,
{op:value}, where currentlyopcan beeq,neq,lt,gt,lte,gte. More can be added on demand.
Supported filter expressions
equneqltltegtgtelikeinregexstrstrcontainsnot_likenot_innot_contains
Example
Query text
query {
titles(
contains: "Blade runner"
startYear: { lt: 1990 }
numVotes: { gt: 100 }
) {
id
title
genre {
label
}
startYear
runtimeMinutes
averageRating
numVotes
}
}
Custom built-in Directives
directive @sqlOption(option:TableOption, index:IndexOption) on FIELDdirective @inferenceOption(sameAs:SameAsOption, ifp:IfpOption) on QUERYdirective @notNull on FIELDdirective @filter(expression: String!) on FIELDdirective @dataGraph (uri:IRI!) repeatable on FIELD|QUERY|MUTATION|SUBSCRIPTION|FRAGMENT_SPREAD|INLINE_FRAGMENT
— where the following ENUM types are used —
enum TableOption {
INDEX
LOOP
}
enum IndexOption {
RDF_QUAD
RDF_QUAD_POGS
S
O
G
}
enum SameAsOption {
SAME_AS_OFF
SAME_AS_S
SAME_AS_O
SAME_AS_S_O
SAME_AS
SAME_AS_P
}
enum IfpOption {
IFP_OFF
IFP_S
IFP_O
IFP
}
Examples
Query text
getAuthor author(code: "ca6a26894a") @sqlOption(option: LOOP, index: S) {
name
age @notNull
birthYear
}
}
query countryQr @inferenceOption(sameAs:SAME_AS ifp:IFP_OFF) {
country(code: "EN"){
name
region {
name
population
}
}
}
@dataGraph usage examples
query text
query qrGraph @dataGraph (uri:"urn:cciso:data") {
regions {
code
}
}
query fldGraph {
regions @dataGraph (uri:"urn:cciso:data") {
code
}
}
Note: Examples are figurative. Live examples depend on a specific dataset.
Mutations
Mutation support is implemented in two ways; we call these basic and templated.
basicmutation definition is based on an RDF/OWL class and its explicit properties.templatedmutation definition is a SPASQL query with parameters defined in such a way to behave as a mutation operation; therefore, it can implement more sophisticated changes.
Note: since a GraphQL field is meant to be a function which returns results, the template approach can be used for calling PL stored procedures or any arbitrary code supported by the Virtuoso engine.
Basic mutation definition
The relevant ontology definition to update fields on a geographical region can be defined as:
gql:updateRegion gql:type gql:Object ;
rdfs:label "Region update mutaion mapping" ;
gql:mutationType "UPDATE";
gql:rdfClass r:Region .
Then request as —
mutation updateRegion($code: String!, $population: Float) {
updateRegion(code: $code, population: $population) {
name
population
}
}
— with variables document —
{
"code": "AN",
"population": 4490
}
— produces the following SPASQL code —
SPARQL WITH <urn:cciso:data> DELETE {
<http://example.org/region/AN> <http://example.org/region/code> ?updateRegion·code .
<http://example.org/region/AN> <http://example.org/region/population> ?updateRegion·population .
}
WHERE {
<http://example.org/region/AN> <http://example.org/region/code> ?updateRegion·code .
<http://example.org/region/AN> <http://example.org/region/population> ?updateRegion·population .
};
SPARQL WITH <urn:cciso:data> INSERT {
<http://example.org/region/AN> <http://example.org/region/code> 'AN' .
<http://example.org/region/AN> <http://example.org/region/population> 4490 .
};
Template mutation
Ontology annotation:
gql:insertMovie
gql:type gql:Function ;
gql:mutationType "SPARQL";
gql:sparqlQuery """
prefix : <http://example.org/schema/>
WITH <urn:object:data>
INSERT {
`iri(?::ID)` a :Movie ;
:code ?::code ;
:title ?::name ;
}
""";
In this case, there is no generated code; the statement is executed, and if possible, cached, with named parameters. The values are passed via variables or as an inline arguments to the request.
Example request
mutation {
insertMovie(
code: "KZZ"
title: "Kin-dza-dza"
) {
title
code
}
}