[フレーム]

Introduction

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools. (Description from the GraphQL webpage.)

Usage

AllegroGraph can run GraphQL queries at repositories/REPOSITORY/graphql HTTP endpoint as well as from Lisp using the agq.graphql:run-graphql function.

The HTTP POST arguments:

  • default-prefix - namespace or URI to prepend to GraphQL fields in the query.

  • infer - Specifies the kind of inference to use for this query. Valid values are false, rdfs++/true, and hasvalue.

The next three arguments have as values comma-separated with each item having two space separated parts.

  • namespaces - comma-separated list of prefix/values separated by spaces. For example
    "foo <http://foo.com/>,bar <http://bar.com/>" 

These namespaces shadow user/repo/default namespaces (see Namespaces and query options, IRI aliases, and typevars). Space and other special characters must be uuencoded.

Here is an example HTTP POST:

POST /repositories/REPONAME/graphql?default-prefix=http://example.com/&namespaces=rdfs%20http://www.w3.org/2000/01/rdf-schema# 

Example curl request to repository 'test' at port 10050 from user test with password xtzzy:

curl 'test:xyzzy@localhost:10050/repositories/test/graphql?default-prefix=http://example.com/&namespaces=purl%20http://purl.org/vocab/relationship/' --data-binary @query.gql 
 
Request body: GraphQL query 

Lisp equivalent:

(with-namespace-abbreviations 
 '(("rdfs" . "http://www.w3.org/2000/01/rdf-schema#")) 
 (agq.graphql::run-graphql 
 my-graphql-query 
 :default-prefix "http://example.com/" 
 :results-format :list)) 

Possible values for `:results-format are

  • :json

  • :list - the list form of the JSON

  • :listupi - the list form of the JSON but instead of converting the upis to their upi->value so they are readable, we leave the upi's alone which makes them easier to use if you're doing subsequent work with them

When run in AGWebView, GraphQL query results are displayed as pretty-printed JSON. In AGWebView, a user can access it by choosing GraphQL as the language in the Query Editor.

Predefined namespaces can be used in the query (see Namespaces and query options)

Extensions to GraphQL syntax

Additionally, the GraphQL syntax is extended with magic comments (see below for more information):

  • default prefix
    full iri: 
     #@default <http://example.com/> 
    if 'ex' is defined: 
     #@default ex 
  • namespaces (same syntax as in SPARQL after the comment character '#')
    #@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> 
  • aliases
    #@alias type <http://www.w3.org/2000/01/rdf-schema#type> 
  • string designation: single quotes can be used to designate strings, so 'foo' is the same as "foo". '''string''' is supported like """string""" as well (so within triple quotes or double quotes, everything is in the string and " or ' is just another character.

GraphQL in AllegroGraph: General description

Summary of the AllegroGraph GraphQL implementation:

  • Top Level Operators:
    • queries and fragment operators are supported.

    • type and mutation operators are not supported.

  • Directives:
    • Standard directives: @include(if: boolean) and @skip(if: boolean)

    • Common directives: @filter(if: "SPARQL expression")

    • AllegroGraph-specific directives: @nullable(if: boolean) which makes it ok for a field in a query to not have a value. boolean can be true or false.

  • External specifications (details below):
    #@default <http://some.uri/> 
    #@prefix ns: <http://whatever.com/> 
    #@alias myuri <http://some.long.uri/> 
  • When used as a boolean value the value nil or "false" represent a false value and everything else represents a true value.

GraphQL and RDF triple stores

In RDF the two main objects are resources and literals. Resources are usually URI's and are in some places written as with the angle brackets not being part of the resource name but simply a means of specifying that the character string inside the brackets denotes a resource.

Literals can be any string and are written surrounded by double quotes.

In GraphQL we have names which consist of letters and digits which cannot be considered a number. There are numbers which are integers or floats. And finally there are strings which is any text surrounded by double quotes.

In the AllegroGraph GraphQL query engine names are considered to be RDF resources. Strings and numbers are considered to be RDF literals.

GraphQL's syntax for names cannot express all RDF resource names so we've extended the GraphQL syntax to include the angle brackets.

You can't write in GraphQL the resource name http://example.com/Hero since the colon is not a permitted character in a GraphQL name. So we've extended the syntax of GraphQL and you can write <http://example.com/Hero> to name this RDF resource in GraphQL.

Also you may have defined "ex" as a namespace abbreviation for "http://example.com/". You can't write ex:Hero in GraphQL since the colon cannot appear in a name. You can write <ex:Hero> and the AllegroGraph GraphQL parser will expand the namespace abbreviation to yield <http://example.com/Hero>.

Consider

#@default http://example.com/ 
{ 
 Hero (name: Luke) 
 { 
 height 
 <http://other.com/weight> 
 } 
} 

Here we see five GraphQL names:

  • Hero
  • name
  • Luke
  • height
  • http://other.com/weight

All five of these will turn into resources and the default prefix will be prepended to the first four.

http://example.com/Hero 
http://example.com/name 
http://example.com/Luke 
http://example.com/height 
http://other.com/weight 

If we had written it as

#@default http://example.com/ 
{ 
 Hero (name: "Luke Skywalker") 
 { 
 height 
 <http://other.com/weight> 
 } 
} 

Then we have four GraphQL names and one GraphQL string so when this is applied to an RDF store we have four resources:

http://example.com/Hero 
http://example.com/name 
http://example.com/height 
http://other.com/weight 

and one literal

"Luke Skywalker" 

Note that in the directives that follow #@ you can write a resource with or without the angle brackets.

#@default http://example.com/ 

is the same as

#@default <http://example.com/> 

Details

A query can be explicit

query myquery($var1: type = value) 
{ 
 alpha 
 { beta 
 gamma 
 } 
} 

or implied

{ 
 alpha 
 { beta 
 gamma 
 } 
} 

The explicit syntax allows you to specify variables which can be used inside the query.

There is no standard mapping of a GraphQL query to an RDF store like AllegroGraph so we have chosen a mapping that makes the most sense and is similar to the mapping used by other RDF stores.

In this query

{ 
 alpha 
 { beta 
 { delta } 
 gamma 
 } 
} 

alpha is the name of a class, and beta, gamma, and delta are predicates.

That is we find all objects ?0 such that:

?0 rdf:type alpha 

and for each we retrieve the objects of the triples

?0 beta ?1 

and

?0 gamma ?2 

and further we look for the delta predicate value for those objects pointed to by the beta predicate

?1 delta ?3 

An RDF store consists of triples or quads of resources and literals and blank nodes.

Resources are usually named something like http://example.com/alpha and this is written <http://example.com/alpha> to denote that this is a resource with that name and not simply that literal string.

In order to make GraphQL easy to write and read we allow one to specify part of the resource name outside the query itself

#@default <http://example.com/> 
{ 
 alpha 
 { beta 
 { delta } 
 gamma 
 } 
} 

says that the default prefix for resources is http://example.com/ Thus the above is the same as:

{ 
 <http://example.com/alpha> 
 { <http://example.com/beta> 
 { <http://example.com/delta> } 
 <http://example.com/gamma> 
 } 
} 

But the former form is much to read.

You can also declare namespaces as done in SPARQL if a single prefix does not sufice for all resources mentioned, for example:

#@prefix ex: <http://example.com/> 
{ 
 <ex:alpha> 
 { <ex:beta> 
 { <ex:delta> } 
 <ex:gamma> 
 } 
} 

To summarize, a resource can be written in a GraphQL query as

  1. a name like alpha in which case the default prefix is prepended
  2. fully specified like <http://example.com/alpha>
  3. use a defined namespace to specify the prefix <ex:alpha>

AllegroGraph has a set of the common namespaces predefined. See the Namespaces and query options document.

Star Wars Example

In the following, we'll make use of our version of the Star Wars database commonly used in GraphQL documentation:

@prefix ex: <http://example.com/> . 
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . 
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . 
 
ex:star-wars rdf:type ex:Movie . 
ex:star-wars ex:episode ex:newhope . 
ex:star-wars ex:episode ex:empire . 
ex:star-wars ex:episode ex:jedi . 
 
ex:newhope rdf:type ex:Episode . 
ex:empire rdf:type ex:Episode . 
ex:jedi rdf:type ex:Episode . 
 
ex:star-wars ex:hero ex:r2-d2 . 
ex:star-wars ex:hero ex:Luke . 
ex:star-wars ex:hero ex:Lando . 
 
ex:r2-d2 rdf:type ex:Hero . 
ex:Luke rdf:type ex:Hero . 
ex:Lando rdf:type ex:Hero . 
 
ex:newhope ex:name "Star Wars" . 
ex:newhope ex:date "May 25, 1977"^^<rdfs:date> . 
ex:newhope ex:number 4 . 
 
ex:empire ex:name "The Empire Strikes Back" . 
ex:empire ex:date "May 17, 1980"^^<rdfs:date> . 
ex:empire ex:number 5 . 
 
ex:jedi ex:name "Return of the Jedi" . 
ex:jedi ex:date "May 25, 1983"^^<rdfs:date> . 
ex:jedi ex:number 6 . 
 
ex:r2-d2 ex:name "R2-D2" . 
ex:r2-d2 rdf:type ex:Robot . 
ex:r2-d2 ex:appearsIn ex:newhope . 
ex:r2-d2 ex:appearsIn ex:empire . 
ex:r2-d2 ex:appearsIn ex:jedi . 
ex:r2-d2 <http://purl.org/vocab/relationship/friendOf> ex:Luke . 
ex:r2-d2 ex:owner ex:Luke . 
ex:r2-d2 ex:primaryFunction "Astromech" . 
 
ex:Luke ex:name "Luke Skywalker" . 
ex:Luke rdf:type ex:Human . 
ex:Luke ex:appearsIn ex:newhope . 
ex:Luke ex:appearsIn ex:empire . 
ex:Luke ex:appearsIn ex:jedi . 
ex:Luke <http://purl.org/vocab/relationship/friendOf> ex:Lando . 
ex:Luke <http://purl.org/vocab/relationship/friendOf> ex:r2-d2 . 
ex:Luke ex:height "1.60"^^xsd:double . 
 
ex:Lando ex:name "Lando Calrissian" . 
ex:Lando rdf:type ex:Human . 
ex:Lando ex:appearsIn ex:empire . 
ex:Lando ex:appearsIn ex:jedi . 
ex:Lando <http://purl.org/vocab/relationship/friendOf> ex:Luke . 
ex:Lando <http://purl.org/vocab/relationship/friendOf> ex:r2-d2 . 
ex:Lando ex:height "1.65"^^xsd:double . 

Here is very simple query

{ 
 <http://example.com/Episode> { <http://example.com/name> } 
} 

and the result is JSON which we show here this way to make it more readable

{ 
 "<http://example.com/Episode>": 
 [ 
 { 
 "<http://example.com/name>": "Return of the Jedi", 
 }, 
 { 
 "<http://example.com/name>": "The Empire Strikes Back", 
 }, 
 { 
 "<http://example.com/name>": "Star Wars", 
 }, 
 ], 
 } 

There are multiple Episodes so we have an array of results. Each result shows the name of the Episode.

If we wrote the query this way:

#@default <http://example.com/> 
{ 
 Episode { name } 
} 

the we get an easier to read result:

{ 
 "Episode": [ 
 { 
 "name": "Return of the Jedi", 
 }, 
 { 
 "name": "The Empire Strikes Back", 
 }, 
 { 
 "name": "Star Wars", 
 }, 
 ], 
} 

Directives are used to alter how predicates are processed.

Each GraphQL implementation supports the @include and @skip directives. We also support the @nullable directive which is important since we don't process GraphQL type definitions so normally a type definition specifies which fields are nullable

A field marked as @nullable need be present and if it's not present the query will continue and that single field will not be present in the result.

For example

#@default <http://example.com/> 
{ 
 Hero 
 { name 
 height 
 } 
} 

returns:

[ 
 { 
 "Hero": [ 
 { 
 "name": "Lando Calrissian", 
 "height": "\"1.65E0\"^^<http://www.w3.org/2001/XMLSchema#double>" 
 }, 
 { 
 "name": "Luke Skywalker", 
 "height": "\"1.6E0\"^^<http://www.w3.org/2001/XMLSchema#double>" 
 } 
 ] 
 } 
] 

You'll note that rd-d2 isn't returned since it doesn't have a height property. That doesn't seem reasonable since we want a list of heroes and whether or not they have a given height should not be important.

So we declare the height property to be nullable. Directives must take an argument to determine if they are active which is the reason for the (if: true).

Given this:

 #@default <http://example.com/> 
 { 
 Hero 
 { name 
 height @nullable(if: true) 
 } 
 } 

we get back a complete list of the heroes but r2-d2 doesn't have a height value returned.

[ 
 { 
 "Hero": [ 
 { 
 "name": "Lando Calrissian", 
 "height": "\"1.65E0\"^^<http://www.w3.org/2001/XMLSchema#double>" 
 }, 
 { 
 "name": "Luke Skywalker", 
 "height": "\"1.6E0\"^^<http://www.w3.org/2001/XMLSchema#double>" 
 }, 
 { 
 "name": "R2-D2" 
 } 
 ] 
 } 
] 

If you want be explicit about the types for which you want to retrieve a slot you can do the same as the above using the fragment syntax. Here the height property will only be checked if the Hero is also of type Human

#@default <http://example.com/> 
{ Hero 
 {name 
 ... on Human 
 { height } 
 } 
} 

with the result

[ 
 { 
 "Hero": [ 
 { 
 "name": "Lando Calrissian", 
 "height": "\"1.65E0\"^^<http://www.w3.org/2001/XMLSchema#double>" 
 }, 
 { 
 "name": "Luke Skywalker", 
 "height": "\"1.6E0\"^^<http://www.w3.org/2001/XMLSchema#double>" 
 }, 
 { 
 "name": "R2-D2" 
 } 
 ] 
 } 
] 

That was an anonymous fragment. You can also define a fragment and use it by name.

This returns exactly what the previous query returns.

#@default <http://example.com/> 
{ Hero 
 {name 
 ... humancheck 
 } 
} 
fragment humancheck on Human 
{ 
 height 
} 

It's possible to do more than one query in a single invocation of GraphQL as in the following examples.

This:

#@default <http://example.com/> 
{ Hero 
 { name } 
} 
{ 
 Human 
 { name } 
} 

or this:

#@default <http://example.com/> 
{ Hero 
 { name } 
 Human 
 { name } 
} 

will return an array of the results:

[ 
 { 
 "Human": [ 
 { 
 "name": "Lando Calrissian", 
 }, 
 { 
 "name": "Luke Skywalker", 
 }, 
 ], 
 }, 
 { 
 "Hero": [ 
 { 
 "name": "Lando Calrissian", 
 }, 
 { 
 "name": "Luke Skywalker", 
 }, 
 { 
 "name": "R2-D2", 
 }, 
 ], 
 }, 
] 

The @filter directive is given after a name but it actually applies to the whole enclosing object

For example:

#@default <http://example.com/> 
{ 
 Hero 
 { 
 name 
 height 
 appearsIn @filter(if: "$height > 1.62") 
 } 
} 

the filter applies to all of

 { 
 name 
 height 
 appearsIn 
 } 

it could have been placed after any of the name, e.g. this is equivalent:

#@default <http://example.com/> 
{ 
 Hero 
 { 
 name @filter(if: "$height > 1.62") 
 height 
 appearsIn 
 } 
} 

The filter expression is any valid SPARQL expression that might be found in a SPARQL filter clause.

In the filter expression $xxxx ($height in the example) refers to the value of field named xxxx. While $xxxx looks like a GraphQL variable, it is not. You can use $xxxx to refer to any field name given in the GraphQL expression.

AllegroGraph can run GraphQL queries at repositories/REPOSITORY/graphql HTTP endpoint as well as from Lisp using the agq.graphql:run-graphql function.

The HTTP request arguments:

  • prefix - namespace or URI to prepend to GraphQL fields in the query.

  • namespaces - space-separated list of namespace abbreviations, shadows user/repo/default namespaces. Space and other special characters must be uuencoded.

  • infer - Specifies the kind of inference to use for this query. Valid values are false, rdfs++/true, and hasvalue.

  • var - space-separated list of values of GraphQL variables.

Here is an example HTTP request:

POST /reporitories/REPONAME/graphql?default-prefix=http://example.com/&namespaces=rdfs http://www.w3.org/2000/01/rdf-schema# 

Example curl request to repository 'test' at port 10050

curl 'test:xyzzy@localhost:10050/repositories/test/graphql?default-prefix=http://example.com/&namespaces=purl%20http://purl.org/vocab/relationship/' --data-binary @query.gql 
 
Request body: GraphQL query 

Lisp equivalent:

(with-namespace-abbreviations 
 '(("rdfs" . "http://www.w3.org/2000/01/rdf-schema#")) 
 (agq.graphql::run-graphql 
 my-graphql-query 
 :default-prefix "http://example.com/" 
 :results-format :alist)) 

When run in AGWebView, GraphQL query results are displayed as pretty-printed JSON. In AGWebView, a user can access it by choosing "GraphQL" as the language in the Query Editor.

Predefined namespaces can be used in the query.

Extensions to GraphQL syntax

Additionally, the GraphQL syntax is extended with magic comments:

  • default prefix:
    full iri: 
     #@default <http://example.com/> 
    if 'ex' is defined: 
     #@default ex 
  • namespaces (same syntax as in SPARQL after the comment character '#'):
    #@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> 
  • The colon after the namespace abbreviation is optional as are the angle brackets around the resource. So an equivalent declaration is:

    #@prefix rdfs http://www.w3.org/2000/01/rdf-schema# 

aliases: The alias magic comment is a bit more complex.

Unlike the other magic comments the angle brackets are important in an alias since it's basically a string substitution

#@alias type http://www.w3.org/2000/01/rdf-schema#type 

is not correct since that's not a good name while

#@alias type <http://www.w3.org/2000/01/rdf-schema#type> 

is correct since you want it to be interpreted a resource.

Some examples using alias:

#@default <http://example.com/> 
#@alias ain appearsIn 
{ 
 Hero 
 { 
 name 
 ain 
 } 
} 

yields:

[ 
 { 
 "Hero": [ 
 { 
 "name": "Lando Calrissian", 
 "appearsIn": [ 
 "<http://example.com/jedi>", 
 "<http://example.com/empire>" 
 ] 
 }, 
 { 
 "name": "Luke Skywalker", 
 "appearsIn": [ 
 "<http://example.com/jedi>", 
 "<http://example.com/empire>", 
 "<http://example.com/newhope>" 
 ] 
 }, 
 { 
 "name": "R2-D2", 
 "appearsIn": [ 
 "<http://example.com/jedi>", 
 "<http://example.com/empire>", 
 "<http://example.com/newhope>" 
 ] 
 } 
 ] 
 } 
] 

and if we instead alias to the full resource name:

#@default <http://example.com/> 
#@alias ain <http://example.com/appearsIn> 
{ 
 Hero 
 { 
 name 
 ain 
 } 
} 

yields:

[ 
 { 
 "Hero": [ 
 { 
 "name": "Lando Calrissian", 
 "<http://example.com/appearsIn>": [ 
 "<http://example.com/jedi>", 
 "<http://example.com/empire>" 
 ] 
 }, 
 { 
 "name": "Luke Skywalker", 
 "<http://example.com/appearsIn>": [ 
 "<http://example.com/jedi>", 
 "<http://example.com/empire>", 
 "<http://example.com/newhope>" 
 ] 
 }, 
 { 
 "name": "R2-D2", 
 "<http://example.com/appearsIn>": [ 
 "<http://example.com/jedi>", 
 "<http://example.com/empire>", 
 "<http://example.com/newhope>" 
 ] 
 } 
 ] 
 } 
] 

AltStyle によって変換されたページ (->オリジナル) /