GraphQL Query Tutorial

The following sections provide examples of Star Wars GraphQL queries and responses. If you have started the services following the Getting Started guide, you can also try the queries out and modify them as you see fit.

All examples in this section are based on the Star Wars dataset.

Retrieve Objects

GraphQL queries can fetch single or multiple object types.

Retrieve a List of Objects

All we have to do to fetch multiple object types using the Ontotext Platform is to define a valid nested GraphQL query.

Let’s say that we need some basic information about all the characters in the Star Wars saga. We need to:

  • define a name for our query

query allCharacters {}
  • set the type of objects (character) and properties (id, name, gender, birthYear, height) that we want to retrieve.

 query allCharacters {
    character {
        id
        name
        gender
        birthYear
        height
    }
}

And that’s it! We have our first query that can retrieve all the characters within the Star Wars dataset. You can edit the query directly in the integrated GraphiQL editor.

Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlVWWHMyZ2hidFhBSUtyc2V5QnduTVYzR0dBSEZaRzMwIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE2NjAyOTExMDksImlhdCI6MTU5NzIxOTEwOSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiM2I2YzA3MjktNTJiMC00NDk4LWIxZGUtOTE4YjZjMTU4N2M5IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InJlYWRvbmx5dXNlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJSZWFkT25seVVzZXIiLCJhcHBsaWNhdGlvbklkIjoiNjcyY2E3YjMtYzM3Mi00ZGYzLTgyZDgtOWExYTBkN2Q2OGMxIiwicm9sZXMiOlsiUmVhZE9ubHkiXX0.KIoFrune5hKqkHDr4BgRbaHZyrkoYoCq9SPBuN9NLxE
true
query allCharacters { character { id name gender birthYear height } }

Using the same approach, we can define as many queries as we need. The next example fetches all Star Wars films including some of their properties.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query allFilms { film { name episodeId role { roleType person { name } } releaseDate } }

Retrieve a Single Object

To retrieve a single object, we can filter a list of objects using a known, unique property value.

For example, let’s say we want to retrieve information about a single Star Wars character Luke Skywalker that has a unique ID property which equals https://swapi.co/resource/human/1.

We can do this by altering the allCharacters query (above), by adding a ID filter upon the set of all characters:

Loading...
https://swapi-platform.ontotext.com/graphql
true
query character { character(ID: "https://swapi.co/resource/human/1") { id name gender birthYear height } }

Retrieve Nested Objects

Now let’s fetch objects and their related objects using single or multi-valued object properties that are defined in the SOML schema. The relationships can be defined using objects single value properties (one-to-one) or multi-valued properties (one-to-many).

These nested object queries retrieve data for an object type along with data from nested or related object types.

The previous example only retrieved scalar information for the Luke Skywalker object. We will extend the example to use nested object properties as well as scalar properties.

The SOML schema defines several nested object relationships using object ranges. For example, the film object property has a Film object type for its range.

Character:
  kind: abstract
  typeProp: "rdf:type"
  type: ["voc:Character"]
  props:
    film: {descr: "Star Wars films appeared in", max: inf, range: Film}
    homeworld: {label: "Characters homeworld(planet)", range: Planet}
    starship: {label: "Characters starship(s)", max: inf, range: Starship}
    vehicle: {label: "Characters vehicles(s)", max: inf, range: Vehicle}
    species: {label: "Characters species", range: Species, rdfProp: "rdf:type", rangeCheck: true}

We will use the film relationship and will fetch the names of all films with the Luke Skywalker character.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query lukeSkywalkerFilms { character(ID: "https://swapi.co/resource/human/1") { id name gender birthYear height film{ name } } }

We can go even deeper and extract all the starships that appear in the films that Luke Skywalker appeared in.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query starshipsInLukeSkywalkerFilms { character(ID: "https://swapi.co/resource/human/1") { id name gender birthYear height film { name starship { name } } } }

Ordered Object List

An object list can be ordered by using an orderBy input argument with an ascending (ASC) or descending (DESC) order.

Note

The order can be applied to objects and multi-valued object properties based on single value or multilingual string based scalars. It can be applied to multi-valued scalars as well.

Now let’s sort a list of character objects by height.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query charactersOrderedByHeight { character(orderBy: {height: DESC}) { id name height } }

We can order nested object properties as well. In the next example, we order all the films that Luke Skywalker appeared in by the film’s name in ascending order.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query lukeSkywalkerFilmsOrderedByName { character(ID: "https://swapi.co/resource/human/1") { id name gender birthYear height film (orderBy: {name: ASC}){ name } } }

It is also possible to order using two or more properties.

<object> (orderBy: {<property1>: <ASC/DESC>, <property2>: <ASC/DESC>})   {<properties>}

In the next example, we order all the films that Luke Skywalker appeared in by the films name (in ascending order). We also, order all the starships that appeared in each film by their maxAtmospheringSpeed (in descending order). If the starships have an equal maxAtmospheringSpeed, then the starships are additionally ordered by cargoCapacity in descending order.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query starshipsInLukeSkywalkerFilmsOrderedBySpeedAndCapacity { character(ID: "https://swapi.co/resource/human/1") { id name gender birthYear height film (orderBy: {name: ASC}){ name starship (orderBy: {maxAtmospheringSpeed: DESC, cargoCapacity: DESC}){ name maxAtmospheringSpeed cargoCapacity } } } }

Another possibility is to order an object list using nested object properties.

<object> (orderBy: {<object_property1>: {property1:<ASC/DESC>}})   {<properties> <object_property1>{<property1}}

For example, let’s order a list of character objects by the character’s species name in ascending order.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query charactersOrderedBySpeciesName { character (orderBy: {species: {name:ASC}}) { id name gender birthYear height species {name} } }

We can also order a multi-value scalar property. The species object definition includes several multi-value scalar properties. For example, skinColor has a max cardinality defined as inf. This indicates that the skinColor property can have infinite (multiple) values, e.g., “white”, “brown”, “black”, “beige”, etc.

Species:
   type: ["voc:Species"]
   typeProp: "rdf:type"
   props:
     skinColor: {descr: "Species skin colour", max: inf}
     hairColor: {descr: "Species hair colour", max: inf}
     eyeColor: {descr: "Species eye colour", max: inf}
       ...

Let’s order the species skin color property values:

Loading...
https://swapi-platform.ontotext.com/graphql
true
query characterOrderedBySpeciesSkinColor { character(ID: "https://swapi.co/resource/human/1") { id name gender birthYear height species{ name skinColor(orderBy: ASC) } } }

LangString Ordering

As discussed in the Ordered Object List section, we can apply orderBy in two distinct cases:

  • Order the values of the multi-valued scalar properties

  • Order object array by field: use a single-value field to order its multi-valued parent object

The same two cases apply for langString fields, but such fields are treated as single-valued or multi-valued dynamically, depending on the effective language pattern.

Ordering of LangString Values

When fetching a multi-valued string or langString field, you can use orderBy, limit, and offset to order the values, but only if the language specification in effect is using ALL. Otherwise, an error will be returned.

Here is an example use of the orderBy to return and sort the English and French language values in ascending order:

Loading...
https://swapi-platform.ontotext.com/graphql
true
query humanDescriptions { human(where: {desc: {}}) { id name desc(lang: "ALL:en~,fr~", orderBy: ASC) { value lang } } }

Other examples of ordering a multi-valued literal properties:

Query

Result

{human {desc(lang: “ALL”, orderBy: ASC) {value}}}

All values in ascending alphabetical order

{human (lang: “ALL”) {desc(orderBy: ASC) {value}}}

Same: ALL is inherited from the higher level

{human {desc(orderBy: ASC) {value}}}

Same unless changed in the schema: ALL is the global default

{human {desc(lang: “ALL:en”, orderBy: ASC) {value}}}

All English-language values in ascending alphabetical order

{human {desc(lang: “ALL:en~”,orderBy: ASC) {value}}}

All values in English (or “dialect”) in ascending alphabetical order

{human {desc(lang: “ALL:NONE”,orderBy: ASC) {value}}}

All values without language tag in ascending alphabetical order

{human {desc(lang: “en”, orderBy: ASC) {value}}}

Error: orderBy cannot be used without ALL

Ordering by Single LangString Value

When fetching a multi-valued object field, you can order by a string or langString field if the field is single-valued, or if the language specification in effect does not contain ALL.

Note that the langString fields have different arguments when ordering by them. The format is as follows:

<object>(orderBy : {<langStringProperty>: {dir: <ASC/DESC>, lang: <filterPattern>}}) {
  <langStringProperty> {
    value
    lang
  }
}

The ordering could be ASC, which is the default if not specified, or could be DESC in Unicode collation order ignoring any language tags.

Due to technical limitations, only values matching the first positive language tag are used for ordering.

If an object does not have a matching value, it comes last for both ASC and DESC directions. There must be a single matching value, otherwise the result of ordering is indeterminate.

It is recommended, but not required, that the ordering field has a UNIQ constraint for that language tag.

Let’s illustrate this with an example:

Loading...
https://swapi-platform.ontotext.com/graphql
true
query humanDescriptions { human(where: {desc: {}}, orderBy: {desc: {dir: DESC, lang: "en"}}) { id name desc { value lang } } }

Other examples of ordering by langString properties, where desc is a multi-valued langString, label a single-valued langString:

Query

Result

{character(orderBy: {desc: {lang: “en”, dir: ASC}}) {desc(lang: “en”) {value}}}

Order alphabetically by en language, there must be only one such value, objects with no matching value come first

{character(orderBy:{desc: {lang: “en”}}) {desc(lang: “en”) {value}}}

Same: the default dir is ASC

{character(lang: “en”, orderBy: {desc: {}}) {desc{value}}}

Same: lang spec inherited from higher level, default dir is ASC

{character(orderBy: {desc: {}}) {desc{value}}}

Same, if the default fetch specification is lang: "en"

{character(lang: “en”, orderBy: {desc: {dir: DESC}}) {desc{value}}}

Reverse alphabetical order by en desc, objects with no matching value come last

{character(orderBy: {label: ASC}) {label{value}}}

Order alphabetically; no language spec needed or allowed for single-valued langString

{character(orderBy: {desc: {lang: “en~”}}) {desc(lang: “en~”) {value}}}

Order alphabetically by en~ desc, there must be only one such, objects with no matching value come first

{character(orderBy: {desc: {lang: “ALL”}}) {desc{value}}}

Error: ALL is not allowed for ordering

{character(orderBy: {desc: {lang: “ANY”}}) {desc(lang: “ANY”) {value}}}

Error: ANY does not specify a concrete lang to order by

{character(orderBy: {desc: {lang: “-fr”}}) {desc(lang: “ANY”) {value}}}

Error: -fr does not specify a concrete lang to order by

{character(lang: “fr,en”, orderBy: {desc: {}}) {desc{value}}}

Order alphabetically by fr desc, there must be only one such, objects with no matching value come first, return fr or en descriptions

{character(lang: “BROWSER”, orderBy: {desc: {}}) {desc{value}}}

Assume the HTTP Accept-Language header has the value ‘fr;q=0.5, en;q=1.0’. This will result in ‘en~,fr~’ as the effective language order pattern. Order alphabetically by en desc, there must be only one such, objects with no matching value come first, return en or fr descriptions

Paginated List of Objects

You can paginate an Object list by using LIMIT and OFFSET input arguments.

LIMIT specifies the number of objects to retrieve per page. OFFSET determines which page slice to retrieve.

Pagination and offsets can be applied to multi-valued object properties.

Note

Pagination and offsets can be applied to multi-valued object properties as well as to multi-value scalars.

The following are examples of different pagination queries.

Let’s restrict the list of films to only return the first page of results (No OFFSET argument), where a page of results is LIMIT-ed to only include two films.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query starshipsInTwoLukeSkywalkerFilms { character(ID: "https://swapi.co/resource/human/1") { id name gender birthYear height film (orderBy: {name: ASC}, limit: 2 ){ name starship (orderBy: {maxAtmospheringSpeed: DESC, cargoCapacity: DESC}){ name maxAtmospheringSpeed cargoCapacity } } } }

To retrieve the second page of results, we need to set an OFFSET to two skips to the second page.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query starshipsInTwoLukeSkywalkerSecondPageFilms { character(ID: "https://swapi.co/resource/human/1") { id name gender birthYear height film (orderBy: {name: ASC}, limit: 2, offset: 2 ){ name starship (orderBy: {maxAtmospheringSpeed: DESC, cargoCapacity: DESC}){ name maxAtmospheringSpeed cargoCapacity } } } }

Now let’s LIMIT the list of starships to include only two results. Again the OFFSET is not defined (the first page of results).

Loading...
https://swapi-platform.ontotext.com/graphql
true
query twoStarshipsInTowLukeSkywalkerFilms { character(ID: "https://swapi.co/resource/human/1") { id name gender birthYear height film (orderBy: {name: ASC}, limit: 2 ){ name starship (orderBy: {maxAtmospheringSpeed: DESC, cargoCapacity: DESC}, limit: 2){ name maxAtmospheringSpeed cargoCapacity } } } }

We can also use an OFFSET argument on an object list without specifying a LIMIT argument. This allows you to retrieve all results from the OFFSET until the end of the result set.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query allExceptTwoStarshipsInLukeSkywalkerFilms { character(ID: "https://swapi.co/resource/human/1") { id name gender birthYear height film (orderBy: {name: ASC}){ name starship (orderBy: {maxAtmospheringSpeed: DESC, cargoCapacity: DESC}, offset: 2){ name maxAtmospheringSpeed cargoCapacity } } } }

Filtering Object Lists

You can use where: arguments on object lists to filter lists. A where: argument can use property values and nested objects property values within filter expressions.

For top-level queries and multi-valued properties, you can use a where parameter that expresses a search/filter condition in a structured way, using field names (including nested properties), comparison operators (e.g., LT), and Boolean operators (e.g., OR).

It is possible to combine GraphQL query filters within the same where: argument by using the default `AND connective and explicit connectives such as AND:, OR:, EXISTS:.

Comparison Operators

Comparison operators come in pairs:

  • EQ:NEQ: - equality/inequality. Should be used for single values filter expressions.

<object> (where: {<property>: {EQ:<value>}})   {<properties>}

The following query will filter the list of characters to only include those that have the male gender type.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query allMaleCharacters { character (where: {gender: {EQ:"male"}}) { id name gender birthYear height } }
  • IN:NIN: - inside/outside of an array of values (put them in square brackets).

<object> (where: {<property>: {IN:[<value1, value2>]}}) {<properties>}
<object> (where: {<property>: {IN:<value>}})   {<properties>}

Hint

Please note that SPARQL logic is 3-valued: true, false, and undef (in case the input field is missing), so it is possible for both of these to be unsatisfied:

<object> (where: {<property>: {IN:[3]}}) {<properties>} # looks for a value equal to 3
<object> (where: {<property>: {NIN:3}})   {<properties>} # looks for a value different from 3

The following query will filter the list of characters to include only those that have a birthYear IN 19BBY.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query allCharactersBornIn19BBY { character (where: {birthYear: {IN:"19BBY"}}) { id name gender birthYear height } }
  • LT:GTE and LTE:GT: - less-than/greater-than-or-equal and less-than-or-equal/greater-than. Comparison of numbers, strings, or IRIs.

The following GraphQL query will filter the list of Character objects so that it does not include any characters who have height greater than 2 meters.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query allCharactersTallerThan200 { character (where: {height: {GT: 200}}) { id name gender birthYear height } }
  • RE:NRE and IRE:NIRE: - regular expression match/mismatch. The I variants make case-insensitive comparisons using Unicode alphabet collation.

Warning

Regex comparison is slow, so you should only use it on small result sets.

The following query will filter the list of characters to only include those with sky in their name.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query allCharactersWithNameContainingSky { character (where: {name: {IRE: "sky"}}) { id name gender birthYear height } }
  • ID: - access an object or object property by IRI (id).

<object> (ID:"<value>") {<properties>}
<object> (ID:["<value1>",""<value1>""]) {<properties>}
<object> (where: {<object_property>: {ID:"<value>"}}) {<properties>}

Characters starring in a A New Hope film.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query allCharactersInANewHope { character (where: {film: {ID: "https://swapi.co/resource/film/1"}}) { id name gender birthYear height film { name } } }

Logical Connectives

You can also use the following logical connectives:

  • AND (conjunction) is implicit between clauses in the same input object

    To compare two properties of the same object, put them together.

    <object> (where: {<property1>: {EQ:"<value1>"}, <property2>: {IRE:"<value2>"}}) {<properties>}
    

    Filter the list of characters to only include those with a height less than 100 and a mass greater than or equal to 45.0. (note: commas are optional in GraphQL)

    Loading...
    https://swapi-platform.ontotext.com/graphql
    true
    query characterHeightMassFilter { character(where: {height: {LT: 100}, mass: {GTE: 45.0}}) { id name gender birthYear height mass } }

    To apply two comparisons to one property (e.g., a range check), put them together.

    <object> (where: {<property1>: {GTE:"<value1>", LT:"<value2>"}) {<properties>}
    

    Filter the list of characters to only include those with a height greater than 150 and less than 165.

    Loading...
    https://swapi-platform.ontotext.com/graphql
    true
    query characterHeightBetween { character(where: {height: {GT: 150, LT: 165}}) { id name gender birthYear height } }

    AND is an explicit conjunction: you will need to use it in two cases:

    1. To check two different instances of the same property.

    <object> (where: {AND: [{<object_property1>: {<property2>: {EQ:<value1>}}},
                            {<object_property1>: {<property2>: {EQ:<value2>}}}]}) {<properties>}
    

    Filter the list of characters to only include those that have appeared in films with episodeId 1 AND episodeId 5.

    Loading...
    https://swapi-platform.ontotext.com/graphql
    true
    query commonCharactersInFilms { character (where: {AND: [{film: {episodeId: {EQ: 1}}}, {film: {episodeId: {EQ: 5}}}]}){ id name gender birthYear height film { episodeId } } }

    NOTE: You cannot write this query using IN because that would look for all characters started in the selected episodes.

    <object> (where: {<object_property1>: {<property2>: {IN: [<value1>, <value2>]}}}) {<properties>}
    
    Loading...
    https://swapi-platform.ontotext.com/graphql
    true
    query allCharactersInFilms { character (where: {film: {episodeId: {IN: [1,5]}}}){ id name gender birthYear height } }
  • OR takes an array of clauses and applies a disjunction.

    <object> (where: {OR: [{<property1>: {IRE: <value1>}},
                           {<property2>: {IRE: <value2>}}]}) {<properties>}
    

    Filter the list of characters with name Skywalker or eyeColor green:

    Loading...
    https://swapi-platform.ontotext.com/graphql
    true
    query charactersWithGreenEyesOrNameSkywalker { character (where: {OR: [{name: {IRE: "Skywalker"}}, {eyeColor: {IRE: "green"}}]}) { id name eyeColor birthYear height } }

    NOTE: If you need to check for one of several values in the same field, it is easier to use RE with | (for text comparison) or IN (for fixed values).

    <object> (where: {<property>: {IRE: "<value1>|<value2>"}}) {<properties>}
    

    Filter the list of characters to only include those with a name property Anakin or Luke in the name:

    Loading...
    https://swapi-platform.ontotext.com/graphql
    true
    query charactersWithAnakinOrLukeInName { character (where: {name: {IRE: "Anakin|Luke"}}){ id name eyeColor birthYear height } }

    NOTE: You can use OR at the operator level if you need a disjunction of two comparisons on the same field.

    <object> (where: {<property>: {OR: [{LT: <value1>} {GT: value2}]}}) {<properties>}
    

    Look for characters with height outside the range 100..200.

    Loading...
    https://swapi-platform.ontotext.com/graphql
    true
    query characterHeightBetween { character(where: {height: {OR: [{LT: 100}, {GT: 200}]}}) { id name gender birthYear height } }
  • NOT negates a clause. Most of the time you will not need it because you can use negation at the operator level.

    Loading...
    https://swapi-platform.ontotext.com/graphql
    true
    query notMaleCharacters { character (where: {NOT: {gender: {IRE: "male"}}}){ id name eyeColor birthYear height } }
  • exists is implicit when checking properties and sub-properties (one value is enough to satisfy the condition).

    <object> (where: {<property>: {}}) {<properties>}
    

    Look for characters with at least one starship and vehicle.

    Loading...
    https://swapi-platform.ontotext.com/graphql
    true
    query charactersWithStarshipAndVehicle { character (where: {starship: {}, vehicle: {}}){ id name starship (limit: 1) {name} vehicle (limit: 1) {name} } }
  • ALL is applicable to multi-valued properties and checks that all values satisfy the condition.

    <object> (where: {All: {<object_property>: {<property>: {GTE: <value>}}}}) {<properties>}
    

    Find characters all of whose starships have maxAtmospheringSpeed higher than 1000.

    Loading...
    https://swapi-platform.ontotext.com/graphql
    true
    query charactersWithFastStarshipOrMissing { character (where: {ALL: {starship: {maxAtmospheringSpeed: {GTE: 1000}}}}){ id name starship (limit: 1) {maxAtmospheringSpeed} } }
  • Please note that characters without any starship will also be returned by the above query (a vacuous truth), so you may want to add an existence check:

    <object> (where: {All: {<object_property1>: {<property2>: {GTE: <value>}}}
                            <object_property1>: {<property2>: {GTE: <value>}}}) {<properties>}
    
    Loading...
    https://swapi-platform.ontotext.com/graphql
    true
    query charactersWithFastStarship { character (where: {ALL: {starship: {maxAtmospheringSpeed: {GT: 1000}}} starship: {maxAtmospheringSpeed: {GT: 1000}}}){ id name starship (limit: 1) {maxAtmospheringSpeed} } }
  • ALL_EXISTS is applicable to multi-valued properties and checks both ALL and exists.

    <object> (where: {ALL_EXISTS: {<object_property>: {<property>: {GTE: <value>}}}}) {<properties>}
    

    The query below filters the list of characters that have starship with only tall pilots (over 180cm) and have at least one such pilot, and returns all their pilots:

    Loading...
    https://swapi-platform.ontotext.com/graphql
    true
    query charactersWithStarshipAnaHeightPilots { character (where: {starship: {ALL_EXISTS: {pilot: {height: {GTE:180}}}}}){ id name starship { pilot{ height } } } }

Type Selectors

Version 3.1 of the Platform introduces a special type selector that acts as a shortcut for type checking as well as provides access to specific type properties when we filter properties with interface range.

The type selector has the form _ifType where Type will be any concrete sub-type for the current interface.

Let’s demonstrate this with an example. In the Star Wars model, we have an interface Character that has many sub-types. Some of them has additional properties that are not present in the parent:

Loading...
https://swapi-platform.ontotext.com/graphql
true
query planetHumanResidentsWithFriendsOrDroidsWithPrimaryFunctionOrWookiee{ planet(where: {resident: { _ifHuman: {friend: {}}, _ifDroid: {primaryFunction: {}}, _ifWookiee: {}} } ) { name resident { name ... on Human { friend { name } } ... on Droid { primaryFunction } } } }

If we run the example, we would get:

  • All Humans that have any friends

  • All Droids with set primary function

  • All Wookiees

As we can see, the type selector allows accessing concrete properties for the type, as well as filtering the objects from the same type. As the sub-types are disjoint and we combine multiple type selectors, they all are grouped with implicit OR along with any common filters that apply for all of them.

Note

Multiple type selectors are grouped with implicit OR along with any common filters that apply for all of them.

Filtering Literal Values

Standardized language tags are used in HTML, XML, RDF, and various other web standards. They reuse ISO 639 2-letter and 3-letter language codes, and add extra codes for scripts, regions, and other traits (e.g., “dialects”). The format and handling of language tags is specified in the Best Current Practice 47 (BCP47) Tags for Identifying Languages, IETF Network Working Group, Sep 2009.

Literal values can be filtered using the filter syntax described in the section Filtering Object Lists, while literal language has a different syntax that will be covered in the current section.

We consider the following features for various operations:

  • Fetch the values of a field. This includes restriction of the returned languages, setting a preference order, obtaining the first value according to this order, or all matching values.

For more information, see the Language Fetching tutorial.

  • Filter by the values of a field so that the parent object has values and/or does not have values with specified languages.

For more information, see the Language Filtering tutorial.

  • Validate values during create/update (mutation) in order to restrict certain languages or ensure uniqueness per language.

For more information, see the Literal Properties Validation tutorial.

  • Implicit lang: allows to omit the lang tag on insert (mutation).

For more information, see the tutorials for Creating and Updating Literal Properties.

Language Fetching

The Ontotext Platform uses a specially formatted language string to specify language tags and a preference order for fetching values.

Language elements:

  • lang: exact language match, e.g., en or en-US

  • lang~: language and its “dialects”, i.e., prefix match at the end of a language tag component, for example en~

  • ~: match any non-empty language, i.e., values with rdf:langString type

  • NONE: match values without language tag, i.e., values with xsd:string type

  • ANY: match any language tag, including empty language. Note that this is nearly the same but not equivalent to ~,NONE because that prefers non-empty language first.

  • BROWSER: match language tags passed via the HTTP Accept-Language header. The keyword could be used for fetching and ordering the results. For more information, see the HTTP Accept-Language section.

Exclusions: these discard values with the indicated language:

  • -lang: exclude this language, e.g., -en or -en-US

  • -lang~: exclude this language and any of its “dialects”, e.g., -en~

  • -~: exclude all non-empty languages so it will match values without a language tag

  • -NONE: exclude values without a language tag

  • -ANY is not allowed because it would forbid all values

  • -BROWSER is not allowed

A sequence of elements can also be specified:

  • By default, there are no restrictions on lang, so ALL values are returned.

  • If you specify lang, then only the first value with matching language is returned.

    • Use lang: "ANY" to return a single value, regardless of its language (or whether it has a language at all).

  • Use ALL as first element (with trailing colon :) to return all values with matching languages (e.g., ALL:en,fr should return all values in English and French). If you use this, the order of languages does not matter, and you should not have generic tags that subsume more specific tags (e.g., ALL:en-GB,en-GB~ should not appear).

    • If you use ALL:ANY, then do not use any other language.

  • lang1,lang2,lang3: comma-separated sequence of languages in preference order.

    • You should list more specific tags earlier (e.g., en-GB,en-GB~ or en,en~), else they will have no effect.

    • There should be no duplicates.

  • -lang1,-lang2,-lang3: comma-separated set of exclusions. All are checked, so their order does not matter (and they are customarily specified last). Each exclusion should be less general than any specified language, or it will prevent the language from having an effect. For example, en-GB,en,-en~ makes no sense because the exclusion kills the two preceding languages.

  • BROWSER: the Accept-Language header (see HTTP Accept-Language) is parsed, ordered by specificity, then by q value, converted to lang~ ranges, and inserted instead of the BROWSER keyword.

    • The lang spec is validated as described above (duplicates, lang subsumption and exclusions) before the BROWSER element is replaced, because the Accept-Language header is under user control, not under SOML designer’s control.

    • If the request does not have such header or the value of the header is invalid then the BROWSER keyword is ignored. In this case the following behavior could be expected:

      • lang: "ALL:BROWSER" will have the same effect as lang: "ALL" and will fetch all possible values

      • lang: "BROWSER" will have the same effect as lang: "~" and will fetch the first found language

      • orderBy: {desc: {lang: "BROWSER"}} will have undetermined order result

In order to use any of the above to filter the returned literal values, the fetch spec needs to be used on a lang argument for the given property like in the example below. It returns all characters and a single description in German, or, if no German language description is present, in English.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query charactersWithGermanDescription { character(where: {desc: {}}) { name desc(lang: "de,en") { value lang } } }

Here are some examples of language specs that return one value (or no values). “First” means the first value that matches a condition. In case there are several, one is picked at random.

Language Spec

Returns one value

ANY

first value with any language (or no language)

~,NONE

first value with non-empty language, then no language

en

first English-language value

en,fr

first English-language value, then a French-language value

en-CA,en~,fr~

first en-CA, then any English “dialect”, then any French “dialect”

en-CA,en~,~

first en-CA, then any English “dialect”, then any non-empty language

en-CA,en~,ANY

first en-CA, then any English, then any value

en-CA,en~,~,NONE

first en-CA, then any English, then any non-empty language, then empty language

en,NONE

first value in English, then a pure string (no language tag)

NONE

first pure string (no language tag)

NONE,-~

same, but more verbose

~

first value that has a non-empty language

-NONE

same

~,-NONE

same, but more verbose

-fr

first value that is not in French

-fr~

first value that is not in any French dialect

BROWSER,NONE

first value based on the user preferences or empty language

Examples of language specs that return many values. The language order is irrelevant.

Language Spec

Returns many values

(none)

return all values

null

same - return all values

ALL

same, but more verbose

ALL:ANY

same, but even more verbose

ALL:en

all English-language values

ALL:en~

all English-language or English “dialect” values

ALL:~

all values with language tag

ALL:NONE

all values without language tag (pure strings)

ALL:en~,-en-US

all English-language or English “dialect” values except American English

ALL:BROWSER,en~

all user preferred languages or English-language or English “dialect”

Note

When BROWSER keyword is used with ALL, the language weights that come with the Accept-Language HTTP header are ignored and the order of the results is undetermined.

Examples of invalid language specs:

Language Spec

Error

-ANY

You cannot use language exclusion -ANY because that forbids all values

ALL:ANY,en

Language en is subsumed by ANY so it has no effect in ALL matching mode

ALL:en,en~

Language en is subsumed by en~ so it has no effect in ALL matching mode

en,ALL,fr

Language mode ALL is allowed only in first position

en~,en-GB

Language en-GB is more specific than en~ but comes later in the list so it has no effect

en~,en-GB~

Language en-GB~ is more specific than en~ but comes later in the list so it has no effect

en,fr,bg,en

Language en is specified more than once

en-GB,en,-en~

  • Language exclusion -en~ is more general than language en-GB so the language has no effect

  • Language exclusion -en~ is more general than language en so the language has no effect

HTTP Accept-Language

The HTTP protocol uses an Accept-Language header defined in RFC 2616 Section 14.4 and RFC 3282.

It is based on RFC 4647 and allows the HTTP client (i.e., browser) to:

  • Specify languages acceptable to the user

  • Use “basic lang range” (prefix) matching

  • Match the most specific (longest) range first

  • Use wildcard (*) to match any non-empty lang tag

  • Specify lang precedence by using quality values (q=); the default is 1.0.

Below are some examples and their interpretation:

Accept-Language

Interpretation: lang (quality)

da, en-GB;q=0.8, en;q=0.7

Danish (1.0), British English (0.8), other English (0.7)

bg;q=0.1, en-GB;q=0.9, en;q=1.0

British English (0.9), other English (1.0), Bulgarian (0.1)

*;q=0.1, en;q=1.0

Any English (1.0), any other (0.1)

*;q=1.0, en;q=0.1

Any English (0.1), any other (1.0)

*;q=1.0 or just *

Any language (1.0)

(no value/missing header)

Ignored

In these interpretations, lang ranges are first ordered by specificity, then by quality value.

Please note that examples 2 and 4 do not make a lot of sense since they give more preference to a less specific lang range:

  • Example 2 says “prefer any English to British English” but any does include British English.

  • Example 4 says “prefer any language to English” but any does include English.

In order to access the HTTP header value, the BROWSER keyword must be used.

The header value, if present, is parsed, ordered by specificity, then by q value, converted to lang~ ranges, and inserted instead of the BROWSER keyword.

  • The language spec is validated as described in the previous Language Fetching section (duplicates, lang subsumption, and exclusions) before the BROWSER element is replaced, because the Accept-Language header is controlled by the user and not by the designer of the SOML.

  • If the request does not have such a header or the value of the header is invalid, then the BROWSER keyword is ignored. In this case, the following behavior can be expected:

    • lang: "ALL:BROWSER" will have the same effect as lang: "ALL" and will fetch all possible values.

    • lang: "BROWSER" will have the same effect as lang: "~" and will fetch the first found language.

    • orderBy: {desc: {lang: "BROWSER"}} will have undetermined order result, as for the ordering functionality to work properly, it needs a single value to be selected (preferably in the same language). To minimize the impact of the missing header, add a default to your sort argument, such as orderBy: {desc: {lang: "BROWSER,en"}}.

The following example demonstrates the use of the BROWSER keyword and the HTTP Accept-Language header.

Consider the following SOML schema:

objects:
  skos:Concept:
    props:
      skos:prefLabel:  {max: inf, range: stringOrLangString}
      skos:definition: {max: inf, range: stringOrLangString}

Assume the following data:

<https://nomenclature.info/nom/5313>
  a skos:Concept;
  skos:prefLabel
    "Two-Handed Crosscut Saw"@en,
    "Godendard"@fr-CA,
    "Passe-partout"@fr;
  skos:definition
    "A tool consisting of a stationary, wide, heavy, serrated blade..."@en,
    "Outil composé d'une longue lame dentelée à dos convexe..."@fr.

You can use it to fetch a concept with values for its multi-lingual properties based on the Accept-Language HTTP header with the following query:

query {
  skos_Concept(ID:"https://nomenclature.info/nom/5313") {
    skos_prefLabel (lang:"BROWSER,en") {value lang}
    skos_definition(lang:"ALL:BROWSER") {value lang}
  }
}

It will return:

  • skos:prefLabel - the first value that matches the header value or English value

  • skos:definition - all values matching the header value

Let’s assume the BROWSER Accept-Language is set to "fr, fr-CA". This will effectively set the query lang argument to:

query {
  skos_Concept(ID:"https://nomenclature.info/nom/5313") {
    skos_prefLabel (lang:"fr-CA~,fr~,en") {value lang}
    skos_definition(lang:"ALL:fr~") {value lang}
  }
}

Note that for the skos_definition fetching language pattern the fr-CA~ is ignored, as it is included in the fr~ spec.

Then the following response is returned:

{
  "data": [{
    "skos_Concept": {
      "skos_prefLabel": [
        {"value": "Godendard", "lang": "fr-CA"}
      ],
      "skos_definition": [
        {"value": "Outil composé d'une longue lame dentelée à dos convexe...", "lang": "fr"}
      ]
    }
  }]
}

Now let’s assume the HTTP header Accept-Language is missing. The following response will be returned:

{
  "data": [{
    "skos_Concept": {
      "skos_prefLabel": [
        {"value": "Two-Handed Crosscut Saw", "lang": "en"},
      ],
      "skos_definition": [
        {"value": "A tool consisting of a stationary, wide, heavy, serrated blade...", "lang": "en"},
        {"value": "Outil composé d'une longue lame dentelée à dos convexe...",         "lang": "fr"}
      ]
    }
  }]
}

If the lang pattern with``BROWSER`` is applicable for ordering, like in the example below, only the first value of the pattern will take effect.

query {
  skos_Concept(lang: "BROWSER,en", orderBy: {skos_prefLabel: {dir: DESC}}) {
    skos_prefLabel {value lang}
    skos_definition(lang:"ALL:BROWSER") {value lang}
  }
}

Let’s assume the BROWSER Accept-Language is set to "fr, fr-CA" again. This will set the effective query lang arguments as in the example below.

query {
  skos_Concept(orderBy: {skos_prefLabel: {lang: "fr-CA~", dir: DESC}}) {
    skos_prefLabel(lang: "fr-CA~,fr~,en") {value lang}
    skos_definition(lang:"ALL:fr~") {value lang}
  }
}

For more information about result sorting, please see the Ordered Object List section.

Note

When designing a SOML and language fetching, consider the case that the header value may not be passed and define reasonable defaults in case the value is not present.

The Ontotext Platform provides some defaults for the application to work, but to prevent unexpected behavior, consider combining the BROWSER keyword with other language qualifiers. For example, lang: "BROWSER,en~,NONE" will allow the application to work for English speaking users even if they have not configured their regional settings.

Language Preference Order for Fetching

Language specs can be provided at different levels. The spec at a given level will apply to lower levels if they do not specify their own lang fetching option. Consider the example:

Loading...
https://swapi-platform.ontotext.com/graphql
true
query humanInEnglish { human(lang: "en") { name desc { value lang } film(lang: "it,de") { name desc { value lang } vehicle { name desc(lang: "de") { value lang } } } homeworld { name desc { value lang } } } }

In this example query, the language spec for fetching is applied at multiple levels:

  • When applied at the query level, it will apply for all Literal properties. For our example, it means that Planet.desc and Planet.resident.homeworld.desc will have only English language descriptions, if any.

  • When applied to a complex property like Planet.film, it overrides the upper level (the query in this case) and changes the default behavior for the Planet.film.desc to English, or, if an English language description is not present, to German.

  • When applied to a literal property’s lang argument, it will override any spec coming from an upper level. In our example, the Planet.film.vehicle.desc and Planet.resident.desc have their own language fetch specs, so they will be used for fetching the descriptions for these objects.

If the query does not define any language fetch spec (or lang: null is provided), there are two possible outcomes:

  • The values will be filtered based on the language fetch configuration in the SOML model for the langString or stringOrLangString properties. For more information, see the SOML property language configurations.

  • All values will be returned.

Hint

Language preference is applicable not only for queries, but also for mutations. It can be used for the result part of the mutation the same way it is for the queries. For more information, check the Object creation section.

Language Filtering

While the previous section describes using language specs to fetch language-tagged values, here we will see how to filter objects by language-tagged values of a field. Make sure that the object has values and/or does not have values with specified languages.

Language specs for filtering are similar to language specs for fetching, but simpler:

  • An object satisfies a language spec if it has at least one matching value.

  • To find objects that do not have matching values, you cannot use exclusion (-). Use NOT at the outer level (see examples below).

  • The mode is always ALL because all conditions are checked.

  • ANY is not allowed because it is a vacuous check.

  • -ANY is not allowed because it would exclude all objects.

  • The same conditions about language subsumption and exclusion compatibility apply as in the previous section.

  • BROWSER is not allowed because it makes no sense to search for objects having a value in a user-determined language.

Language specs for filtering add the following features:

  • UNIQ: find objects that do not have multiple values in the same language. This is intended to check SKOS Integrity Condition S14.

  • -UNIQ: find objects that have multiple values in the same language. This can be used to find and fix such objects.

  • These checks pertain to empty language tags.

  • You can combine them with language tags to refine the list of languages being checked.

  • The UNIQ check must come last and be separated with semicolon.

Examples of filtering by language spec:

GraphQL Query

Return objects that

{obj(where: {field: {lang: "en"}}) {...}

have an English-language value

{obj(where: {field: {lang: "en~,-en-US"}}) {...}

have value in any English or “dialect” except American English

{obj(where: {field: {lang: "-en-US"}}) {...}

have a value in any language except American English (including empty language)

{obj(where: {NOT: {field: {lang: "en-US"}}}) {...}

do not have a value in American English

{obj(where: {field: {lang: "UNIQ"}}) {...}

do not have multiple values in the same language

{obj(where: {field: {lang: "-UNIQ"}}) {...}

have multiple values in the same language

{obj(where: {field: {lang: "~;-UNIQ"}}) {...}

have multiple values in the same non-empty language

{obj(where: {field: {lang: "en;-UNIQ"}}) {...}

have multiple values in English

{obj(where: {field: {lang: "en~;-UNIQ"}}) {...}

have multiple values in the same language, which is an English “dialect”

{obj(where: {field: {value: {IRE: "foo"}, lang: "-UNIQ"}}) {...}

have multiple values in the same language that match the string “foo”

Examples of invalid language specs for filtering:

Language Spec

Error

ALL:-en

Mode ALL is implied when filtering by language and should not be specified

ANY,en

ANY is not allowed when filtering by language because it is a vacuous check

-ANY

-ANY is not allowed when filtering by language because it would exclude all objects

en,en~

Language en is subsumed by en~ so it has no effect

en~,en-GB

Language en-GB is more specific than en~ so it has no effect

en~,en-GB~

Language en-GB~ is more specific than en~ so it has no effect

en,fr,bg,en

Language en is specified more than once

en-GB,en,-en~

  • Language exclusion -en~ is more general than language en-GB so the language has no effect

  • Language exclusion -en~ is more general than language en so the language has no effect

Examples:

All objects in the schema.yaml have a multilingual property desc, which can be used for trying the language filtering functionality.

Find all Characters that have values with non-empty language:

Loading...
https://swapi-platform.ontotext.com/graphql
true
query charactersWithNoneEmptyLangs { character(where: {desc: {lang: "~"}}) { name desc { value lang } } }

All Film-s that have description in German, and we fetch only the German descriptions:

Loading...
https://swapi-platform.ontotext.com/graphql
true
query filmsWithGermanDescription { film(where: {desc: {lang: "de"}}) { name desc(lang: "de") { value lang } } }

All Character-s that have non-English description. Try removing the value fetching filter -en to see how the response changes.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query characterWithNoneEnglishDescription { character(where: {desc: {lang: "~,-en~"}}) { name desc(lang: "-en") { value lang } } }

Find Character-s that do not have unique language description. Try changing the filter to UNIQ.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query characterWithNoneUniqueDescription { character(where: {desc: {lang: "-UNIQ"}}) { name desc { value lang } } }

Combine Multiple Arguments

To filter and constrain lists with more complex expressions, we can combine where, orderBy, limit, and offset.

Let’s make some complex queries in order to fetch specific data.

Examples:

All characters ordered in descending character name order, where the character appeared in the film A New Hope and the characters films are filtered to only include A New Hope with the character’s films ordered in ascending releaseDate order.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query charactersInDescendingNameOrderWhereFilmWasNewHopeOrderedByReleaseDate { character(orderBy: {name: DESC}, where: {film: {name: {EQ: "A New Hope"}}}) { name film(orderBy: {releaseDate: ASC}, where: {name: {EQ: "A New Hope"}}) { name releaseDate } } }

Cycle Queries

GraphQL allows cyclic queries, such as the one shown below. They are virtually endless and can be considered DoS queries. To protect the database, we have added two limits:

  • sparql.endpoint.maxTupleResults - maximum number of tuples that can be returned from the DB for one request property.

    Default value is set to 5_000_000. If it is exceeded, the query will be terminated and an error will be thrown. No data will be returned in this case as it is not possible to return a meaningful response.

  • graphql.query.depthLimit - limit on the depth of the GraphQL query.

    Default value is set to 15. If it is exceeded, an error will be thrown notifying the user the query depth limit is 15.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query cyclicQuery { character { friend { film { character { friend { film { character { friend { film { character { friend { film { character { friend { film { character { film { character { film { character { film { character { film { character { film { character { film { id } } } } } } } } } } } } } } } } } } } } } } } } } } } }
{ "errors": [ { "message": "The maximum allowed query depth is '15'" } ] }

Multiple Queries in a Request

If multiple queries are part of the same query request, they are executed in parallel, and the individual responses are collated and returned. You can fetch objects of different unrelated types in the same query.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query multipleRootObjects { film { name releaseDate } starship{ name cargoCapacity costInCredits } }

Variables in Queries

A GraphQL query can be parameterized with variables, maximizing query reuse, and avoiding costly string building in clients at runtime.

If not defined as constant, a variable can be supplied for an input value.

Variables must be defined at the top of an operation and are in scope throughout the execution of that operation.

Let’s try it out. Insert the following variables in the Query variables section of the editor below:

{
    "ids":["https://swapi.co/resource/vehicle/38","https://swapi.co/resource/vehicle/44"]
}
Loading...
https://swapi-platform.ontotext.com/graphql
true
query starship_by_pilot_vehicle_id ($ids: [ID!]!) { starship(where:{ALL_EXISTS: {pilot: {vehicle: {ID: $ids}}}}, limit: 2) { id name pilot(orderBy: {name: DESC}) { id name vehicle(orderBy: {name: DESC}) { id name } } } }

In the example above, we have defined a variable ids.

  • ($ids: [ID!]!) - here we define the variable and say that we expect ID to be passed for it.

  • And here is the usage of the variable in the query filter: (where:{ALL_EXISTS: {pilot: {vehicle:  {ID: $ids}}}}, limit: 2)

Fragment and Aliases

Fragments

Fragments are the primary unit of composition in GraphQL.

Fragments allow for the reuse of common repeated selections of fields, reducing duplicated text in the document. Inline Fragments can be used directly within a selection to condition upon a type condition when querying against an interface or union.

For example, if we want to fetch some common information about a film, we would have the following query:

Loading...
https://swapi-platform.ontotext.com/graphql
true
query usingFragments { film(ID: "https://swapi.co/resource/film/1") { id name role { roleType person { name } character { name } } planet { id name resident { id name film { id name role { roleType person { name } character { name } } } } } } }

The repeated fields can be extracted into a fragment and composed by a parent fragment or query.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query usingFragments { film(ID: "https://swapi.co/resource/film/1") { ...commonFilmProperties planet { id name resident { id name film { ...commonFilmProperties } } } } } fragment commonFilmProperties on Film { id name role { roleType person { name } character { name } } }

Fragments are consumed by using the spread operator (…). All fields selected by the fragment will be added to the query field selection at the same level as the fragment invocation. This happens through multiple levels of fragment spreads.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query usingFragments { film(ID: "https://swapi.co/resource/film/1") { ...detailedFilmProperties planet { id name resident { id name film { ...commonFilmProperties } } } } } fragment commonFilmProperties on Film { id name role { roleType person { name } character { name } } } fragment detailedFilmProperties on Film { releaseDate desc { value lang } episodeId ...commonFilmProperties }

Inline Fragments

Fragments can be defined inline within a selection set. This is done to conditionally include fields based on their runtime type.

Loading...
https://swapi-platform.ontotext.com/graphql
true
query film1 { film(ID: "https://swapi.co/resource/film/1") { id name character { type name ... on Droid { name id } ... on Human { name eyeColor skinColor } } } }

Note that the name field is requested separately for character, Droid, and Human, but it is returned only once for each character. When more than one fields of the same name are executed in parallel, their selection sets are merged together when completing the value in order to continue execution of the sub‐selection sets.

Aliases

GraphQL Aliases allows you to rename the result of a field to anything you want. Let’s say you want to have the same top level field in the same query with different arguments We can achieve this by using alias for film.

For example:

Loading...
https://swapi-platform.ontotext.com/graphql
true
query usingAliases { film(ID: "https://swapi.co/resource/film/1") { id name releaseDate } film(orderBy: {episodeId: DESC}) { id name releaseDate role { roleType person { name } character { name } } episodeId openingCrawl } }

Aliases are necessary for the query to work. If you run the query without aliases, it will return an error:

Loading...
https://swapi-platform.ontotext.com/graphql
true
query usingAliases { Film1:film(ID: "https://swapi.co/resource/film/1") { id name releaseDate } Film2:film(orderBy:{episodeId:DESC}) { id name releaseDate episodeId openingCrawl } }
None

GraphQL Aliases are dynamic, written on the client making the request. This is a workaround for use cases where you want the selection set to be named more readable. It is not prudent to write this for every query from every client making this request for modifying trivial fields.