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 currentlyop
can beeq
,neq
,lt
,gt
,lte
,gte
. More can be added on demand.
Supported filter expressions
equ
neq
lt
lte
gt
gte
like
in
regex
strstr
contains
not_like
not_in
not_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 FIELD
directive @inferenceOption(sameAs:SameAsOption, ifp:IfpOption) on QUERY
directive @notNull on FIELD
directive @filter(expression: String!) on FIELD
directive @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.
basic
mutation definition is based on an RDF/OWL class and its explicit properties.templated
mutation 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
}
}