GraphQL Mutation Tutorial

The following sections provide examples of Star Wars GraphQL mutations 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.

Object Creation

There are a few types of object creations: creating a single or multiple objects of the same type, creating objects of different types, as well as creating multiple nested objects at once. A create mutation is added for every concrete Semantic Object. It is not possible to create objects from an abstract object such as Character, but create mutations for all its concrete objects are available, e.g., create_Human, create_Droid, create_Aleena, etc.

Note

As we do have automatic ID generation, it is not necessary to provide an ID for every object. Other mandatory properties (such as name in the Star Wars schema) must be provided on every object creation.

Each create mutation consists of two parts - mutation part and result part:

  • The mutation part is where we define what objects we are creating.

  • The result part is where we define the result we want to get from the mutation (created object with its properties).

The result part contains the following fields:

  • affected_count with the fields count and kind – used to query the number of the objects modified by the current mutation, grouped by type.

  • affected_objects with the fields ids and kind – used to query the IRIs of the objects modified by the current mutation, grouped by type.

  • a property that matches the mutation type and can be used to access the created objects. For example, the mutation create_Human will have a property named human that can be used to query the created humans from the current mutation. This selection supports result ordering and limiting via the arguments orderBy, limit, and offset. Additional filtering can be applied via the where argument, and default language for fetching the Literal properties can be applied via the lang argument.

    For more information on the latter, check the Language Fetching section.

Note

Create, update, and delete mutations have identical response format. They only differ in the actual response that is returned.

For example:

  • create mutations return the newly created objects

  • update mutations return the newly created and updates objects

  • delete mutations return the removed objects before their deletion

If the two selections are included in the result part, the Platform will return the number of the affected objects and their IDs.

Create Semantic Objects of a Single Type (without Nesting)

Let’s start with simple, single type object creation without nesting.

Let’s say we want to create a human named Lando Calrissian with a unique ID property https://swapi.co/resource/human/25.

The following create mutation will create an object of type Human with name Lando Calrissian and eyeColor blue, and will return the created Human with its ID and name that were requested in the sub-selection.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation createHuman { create_Human(objects: { id: "https://swapi.co/resource/human/1025" rdfs_label: {value:"Lando"} eyeColor: "blue" }) { human { id name type eyeColor } affected_count { kind count } affected_objects { kind ids } } }
{ "data": { "create_Human": { "human": [ { "id": "https://swapi.co/resource/human/1025", "name": "Lando", "type": [ "https://swapi.co/vocabulary/Human", "https://swapi.co/vocabulary/Sentient", "https://swapi.co/vocabulary/Mammal" ], "eyeColor": [ "blue" ] } ], "affected_count": [ { "kind": "Human", "count": 1 } ], "affected_objects": [ { "kind": "Human", "ids": [ "https://swapi.co/resource/human/1025" ] } ] } } }

We can see the selected properties for the newly created Human. We also see that some of the properties like the type are filled automatically according to the schema.

Now let’s create two Human objects in a batch and also skip the ID of the second Human.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation createHuman { create_Human(objects: [ { id: "https://swapi.co/resource/human/1026" rdfs_label: {value: "Anakin"} eyeColor: "blue" }, { rdfs_label: {value: "Palpatin"} eyeColor: "green" } ]) { human { id name type eyeColor } affected_count { kind count } affected_objects { kind ids } } }
{ "data": { "create_Human": { "human": [ { "id": "http://example.org/resource/33476ae2-fa9b-5d1e-87c2-ed996d2e6ce6", "name": "Palpatin", "type": [ "https://swapi.co/vocabulary/Human", "https://swapi.co/vocabulary/Sentient", "https://swapi.co/vocabulary/Mammal" ], "eyeColor": [ "green" ] }, { "id": "https://swapi.co/resource/human/1026", "name": "Anakin", "type": [ "https://swapi.co/vocabulary/Human", "https://swapi.co/vocabulary/Sentient", "https://swapi.co/vocabulary/Mammal" ], "eyeColor": [ "blue" ] } ], "affected_count": [ { "kind": "Human", "count": 2 } ], "affected_objects": [ { "kind": "Human", "ids": [ "http://example.org/resource/33476ae2-fa9b-5d1e-87c2-ed996d2e6ce6", "https://swapi.co/resource/human/1026" ] } ] } } }

Note how the two Human objects are now wrapped inside [], which indicates batch objects creation. Also, the second Human has a generated ID as we have skipped adding one during the creation.

Create Semantic Objects of Multiple Types (without Nesting)

Let’s say we want to create a Human named Lor San Tekka, another one named Major Bren Derlin, and a Droid named R2-D3.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation createHumanAndDroid { create_Human(objects: [ { id: "https://swapi.co/resource/human/1027" rdfs_label: {value: "Lor San Tekka"} eyeColor: "blue" }, { rdfs_label: {value: "Major Bren Derlin"} eyeColor: "green" } ]) { human { id name type eyeColor } affected_objects { kind ids } }, create_Droid(objects: { rdfs_label: {value: "R2-D3"} mass: 222 }) { droid { id name type mass } affected_objects { kind ids } } }
{ "data": { "create_Human": { "human": [ { "id": "http://example.org/resource/3b5e417f-c812-5cc4-aa29-b9d01c40b0a7", "name": "Major Bren Derlin", "type": [ "https://swapi.co/vocabulary/Human", "https://swapi.co/vocabulary/Sentient", "https://swapi.co/vocabulary/Mammal" ], "eyeColor": [ "green" ] }, { "id": "https://swapi.co/resource/human/1027", "name": "Lor San Tekka", "type": [ "https://swapi.co/vocabulary/Human", "https://swapi.co/vocabulary/Sentient", "https://swapi.co/vocabulary/Mammal" ], "eyeColor": [ "blue" ] } ], "affected_objects": [ { "kind": "Human", "ids": [ "http://example.org/resource/3b5e417f-c812-5cc4-aa29-b9d01c40b0a7", "https://swapi.co/resource/human/1027" ] } ] }, "create_Droid": { "droid": [ { "id": "http://example.org/resource/f874920d-3f5d-5dba-9d91-5823a20dad79", "name": "R2-D3", "type": [ "https://swapi.co/vocabulary/Droid", "https://swapi.co/vocabulary/Sentient", "https://swapi.co/vocabulary/Artificial" ], "mass": "222" } ], "affected_objects": [ { "kind": "Droid", "ids": [ "http://example.org/resource/f874920d-3f5d-5dba-9d91-5823a20dad79" ] } ] } } }

A few things to note here:

  • Every type of object (Human and Droid in the example) has its own mutation and results parts.

  • affected_count and affected_objects are not mandatory.

  • You can mix batch creations with single object creations, and also mix different types.

Nested Creation of Semantic Objects

We already saw how Semantic Objects are created, but there is an even more powerful option for creating them - the creation of nested Semantic Objects. While creating an object, you can create nested objects which will be “linked” to the current object. For example, let’s create a Human who is from an unknown (new) Homeworld.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation createHumanWithHomeworld { create_Human(objects: [ { id: "https://swapi.co/resource/human/1028" rdfs_label: {value: "Lando's brother"} eyeColor: [ "blue" ] homeworld: { planet: { id: "https://swapi.co/resource/planet/111" rdfs_label: {value: "Earth"} } } } ]) { human { id name type eyeColor homeworld { id type name } } affected_objects { kind ids } } }
{ "data": { "create_Human": { "human": [ { "id": "https://swapi.co/resource/human/1028", "name": "Lando's brother", "type": [ "https://swapi.co/vocabulary/Human", "https://swapi.co/vocabulary/Sentient", "https://swapi.co/vocabulary/Mammal" ], "eyeColor": [ "blue" ], "homeworld": { "id": "https://swapi.co/resource/planet/111", "type": [ "https://swapi.co/vocabulary/Planet" ], "name": "Earth" } } ], "affected_objects": [ { "kind": "Planet", "ids": [ "https://swapi.co/resource/planet/111" ] }, { "kind": "Human", "ids": [ "https://swapi.co/resource/human/1028" ] } ] } } }

Let’s see what’s new here:

  • We are creating a Human in the usual way it is done, but now Homeworld is included.

  • The Homeworld is created during the Human creation, and it is of type Planet (as this is the only option in this case).

  • The newly created Homeworld can be queried in the result.

  • Now there are two objects created - one Human and one Planet. The Planet is linked to the Human as the Human’s Homeworld.

One more example of nested objects, this time with multiple levels of nesting:

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation createHumans { create_Human(objects: [{ id: "https://swapi.co/resource/human/1011" rdfs_label: {value: "Palpatin"} starship: { starship: { id: "https://swapi.co/resource/starship/7451", rdfs_label: {value: "Deathstar X"} pilot: { yodasspecies: { id: "https://swapi.co/resource/yodasspecies/221" rdfs_label: {value: "Baby Yoda"} desc: {value: "Cute, but deadly", lang: "en"} film: { film: { id: "https://swapi.co/resource/film/4131", rdfs_label: {value: "One film to rule them all"}, vehicle: { vehicle: { id:"https://swapi.co/resource/vehicle/451", rdfs_label: {value: "Lightbuster"} crew:666 pilot: { human: { id:"https://swapi.co/resource/human/1001" rdfs_label: {value: "Admiral Conan Antonio Motti"} } } } } } } } } } } }]) { human { name id starship{ id name pilot{ id name desc{ value } film{ id name vehicle{ id name crew pilot{ id name } } } } } } affected_count { kind count } affected_objects{ ids kind } } }
{ "data": { "create_Human": { "human": [ { "name": "Palpatin", "id": "https://swapi.co/resource/human/1011", "starship": [ { "id": "https://swapi.co/resource/starship/7451", "name": "Deathstar X", "pilot": [ { "id": "https://swapi.co/resource/yodasspecies/221", "name": "Baby Yoda", "desc": [ { "value": "Cute, but deadly" } ], "film": [ { "id": "https://swapi.co/resource/film/4131", "name": "One film to rule them all", "vehicle": [ { "id": "https://swapi.co/resource/vehicle/451", "name": "Lightbuster", "crew": "666", "pilot": [ { "id": "https://swapi.co/resource/human/1001", "name": "Admiral Conan Antonio Motti" } ] } ] } ] } ] } ] } ], "affected_count": [ { "kind": "Yodasspecies", "count": 1 }, { "kind": "Film", "count": 1 }, { "kind": "Vehicle", "count": 1 }, { "kind": "Human", "count": 2 }, { "kind": "Starship", "count": 1 } ], "affected_objects": [ { "ids": [ "https://swapi.co/resource/yodasspecies/221" ], "kind": "Yodasspecies" }, { "ids": [ "https://swapi.co/resource/film/4131" ], "kind": "Film" }, { "ids": [ "https://swapi.co/resource/vehicle/451" ], "kind": "Vehicle" }, { "ids": [ "https://swapi.co/resource/human/1001", "https://swapi.co/resource/human/1011" ], "kind": "Human" }, { "ids": [ "https://swapi.co/resource/starship/7451" ], "kind": "Starship" } ] } } }

A bit of a more complicated example, but let’s see what’s going on here:

  1. A Human named “Palpatin” is created.

  2. His starship is created using a nested creation.

  3. The starship’s pilot is created using a nested creation, the pilot is of type yodasspecies.

  4. The pilot will “act” in a film that is created using a nested creation.

  5. Also, a vehicle is created using nested creation inside the film.

  6. And finally. the vehicle has a pilot who is Human and is created using a nested creation.

Note that in the result, there are two humans on the same level as we are requesting all created humans with their properties. Although the last human in the nested creation is very deep, it is still a Human and is listed at level 1 as there is the Human request.

Creation of Nested Semantic Objects Using ID Filter

Another useful feature is using an ID filter during the creation of a nested object.

For example, you can create a Human and “link” it to a Homeworld by using the Homeworld’s ID. This will save us one update mutation. Normally, this would require the following mutations:

  1. Create Human.

  2. Update the Human “linking” it to a Homeworld.

Instead, if we know the Homeworld’s ID (for example let’s say the ID of the “Tatooine” is https://swapi.co/resource/planet/1), we can do this:

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation createHumanWithHomeworld { create_Human(objects: [ { id: "https://swapi.co/resource/human/1029" rdfs_label: {value: "Paige Tico"} eyeColor: "blue" homeworld: { ids: "https://swapi.co/resource/planet/1" } } ]) { human { id name type eyeColor homeworld { id type name } } affected_objects { kind ids } } }
{ "data": { "create_Human": { "human": [ { "id": "https://swapi.co/resource/human/1029", "name": "Paige Tico", "type": [ "https://swapi.co/vocabulary/Human", "https://swapi.co/vocabulary/Sentient", "https://swapi.co/vocabulary/Mammal" ], "eyeColor": [ "blue" ], "homeworld": { "id": "https://swapi.co/resource/planet/1", "type": [ "https://swapi.co/vocabulary/Planet" ], "name": "Tatooine" } } ], "affected_objects": [ { "kind": "Human", "ids": [ "https://swapi.co/resource/human/1029" ] } ] } } }

Note that only the Human is listed in the affected_objects result as the planet “Tatooine” already exists.

We can also have multiple ID values if the property type is multi value. For example a Human can be “linked” to multiple Vehicle objects. The same mutation, but with multiple vehicle IDs (of vehicles that exist) would look like this:

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation createHumanWithVehicle { create_Human(objects: [ { id: "https://swapi.co/resource/human/1030" rdfs_label: {value: "Dak Ralter"} eyeColor: "blue" vehicle: { ids: ["https://swapi.co/resource/vehicle/4", "https://swapi.co/resource/vehicle/7"] } } ]) { human { id name vehicle { id type name } } affected_objects { kind ids } } }
{ "data": { "create_Human": { "human": [ { "id": "https://swapi.co/resource/human/1030", "name": "Dak Ralter", "vehicle": [ { "id": "https://swapi.co/resource/vehicle/4", "type": [ "https://swapi.co/vocabulary/Vehicle" ], "name": "Sand Crawler" }, { "id": "https://swapi.co/resource/vehicle/7", "type": [ "https://swapi.co/vocabulary/Vehicle" ], "name": "X-34 landspeeder" } ] } ], "affected_objects": [ { "kind": "Human", "ids": [ "https://swapi.co/resource/human/1030" ] } ] } } }

Creation of Nested Semantic Objects Using Full Filters

Another case that we might encounter is when we do not know the IDs of some objects, but we know some of their properties. In this example we want to create a Human and “link” it to all existing starships that have female pilots.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation create_human { create_Human (objects: [{ id: "https://swapi.co/resource/human/1031" rdfs_label: {value: "Admiral Piett"} starship: { where: {ALL_EXISTS: {pilot: {gender: {EQ: "female"}}}} } }]) { human { id name starship { id name pilot { id name gender } } } } }
{ "data": { "create_Human": { "human": [ { "id": "https://swapi.co/resource/human/1031", "name": "Admiral Piett", "starship": [ { "id": "https://swapi.co/resource/starship/49", "name": "H-type Nubian yacht", "pilot": [ { "id": "https://swapi.co/resource/human/35", "name": "Padmé Amidala", "gender": "female" } ] } ] } ] } } }

All filters are available during nested creation. You can learn more about the filters here.

And as a final example, we can combine everything above in a single mutation like this:

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation create_human { create_Human (objects: [{ id: "https://swapi.co/resource/human/1032" rdfs_label: {value: "Palpatin"} starship: { where: {ALL_EXISTS: {pilot: {gender: {EQ: "female"}}}} } homeworld: { ids: "https://swapi.co/resource/planet/8" } vehicle: { vehicle: { id: "https://swapi.co/resource/vehicle/111" rdfs_label: {value: "Rocket X"} } } }]) { human { id name starship { id name pilot { id name gender } } homeworld { id name } vehicle { id name } } } }
{ "data": { "create_Human": { "human": [ { "id": "https://swapi.co/resource/human/1032", "name": "Palpatin", "starship": [ { "id": "https://swapi.co/resource/starship/49", "name": "H-type Nubian yacht", "pilot": [ { "id": "https://swapi.co/resource/human/35", "name": "Padmé Amidala", "gender": "female" } ] } ], "homeworld": { "id": "https://swapi.co/resource/planet/8", "name": "Naboo" }, "vehicle": [ { "id": "https://swapi.co/resource/vehicle/111", "name": "Rocket X" } ] } ] } } }

Object Updates

In the Object Creation section above, we learned how to create one or multiple Semantic Objects. Now, let’s see how we can modify and update them.

Using the update mutation requests, we can do the following:

  • modify single-valued scalar properties

  • modify multi-valued scalar properties

  • add or remove relations to other object

  • update specific objects by IRI

  • update multiple objects using filters

  • update objects via their interfaces

  • do all of the above for nested objects

In future releases, you will also be able to:

  • create relations between objects using select filters

  • create new a Semantic Object and link it to the updated parent object

  • remove relations and delete the other Semantic Objects

Before we take a look at each functionality, let’s see what the similarities and differences are compared to the Semantic Objects create.

Firstly, the mutation names are changed to update_Type, where for Type we can use any defined type: abstract and non-abstract such as update_Human, update_Droid and update_Film. The main difference here is that we have update_Character. This enables us to create requests that modify common properties for all sub-types.

Another major difference is that we can have only single root update request per mutation. In contrast, the creates can have multiple root level object creates of the same type.

There is a change in the multi-valued scalar properties update format as well. The format is as follows:

"Input for updating multi-value String fields"
input String_Multi_Value_Input {
  "Values to add to the affected property"
  value: [String!]
  "Replace values matching this property. Cannot be combined with replace"
  patch: String
  "Overwrite all values for this property. Defaults to false."
  replace: Boolean = false
}

Definitions as the one above one are defined for all literal types. This allows the creation of very powerful multi-valued update operations. To illustrate the possible combinations, we will use the Species type and its property hairColor:

  • Remove all Species hair colors

    hairColor: []
    
  • Add hair color brown to the updated Species

    hairColor: [{value: "brown"}]
    
  • Add hair color brown and blond

    hairColor: [{value: "brown"}, {value: "blond"}]
    
    hairColor: [{value: ["brown", "blond"]}]
    
  • Update hair color brown with value brownish

    hairColor: [{value: "brownish", patch: "brown"}]
    
  • Update hair color brown set as brownish and add hair color red

    hairColor: [{value: "brownish", patch: "brown"}, {value: "red"}]
    
  • Update hair color brown and replace with brownish; remove hair color red

    hairColor: [{value: "brownish", patch: "brown"}, {value: null, patch: "red"}]
    
  • Update hair color red and replace it with pink, peachy, and velvet

    hairColor: [{value: ["pink", "peachy", "velvet"], patch: "red"}]
    
  • Replace all hair colors with pink and velvet

    hairColor: [{value: ["pink", "velvet"], replace: true}]
    
  • Patching and Replacement are invalid, and will throw a Validation exception

    hairColor: [{value: ["bright red"], patch: "red", replace: true}]
    

The following sections demonstrate all features in detail, so let’s begin.

Update Semantic Objects by ID (without Nesting)

Let’s start with a simple update by ID for scalar properties.

Say we want to update a human named Lando Calrissian to have different eye and hair colors, assuming we also know his ID to be https://swapi.co/resource/human/25.

The following mutation will do an update by ID and type Human, and will set new eyeColor to dark blue and hairColor to blond. It will return the updated Human with its ID, name, eyeColor, and hairColor that were requested in the sub-selection.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation updateHuman { update_Human(where: {ID: "https://swapi.co/resource/human/25"}, objects: { eyeColor: {value: "dark blue", replace: true} hairColor: {value: "blond", replace: true} }) { human { id name eyeColor hairColor } affected_count { kind count } affected_objects { kind ids } } }
{ "data": { "update_Human": { "human": [ { "id": "https://swapi.co/resource/human/25", "name": "Lando Calrissian", "eyeColor": [ "dark blue" ], "hairColor": [ "blond" ] } ], "affected_count": [ { "kind": "Human", "count": 1 } ], "affected_objects": [ { "kind": "Human", "ids": [ "https://swapi.co/resource/human/25" ] } ] } } }

We can see the selected properties for the updated Human. We also see that some of the properties like the name are fetched from the database and returned.

Now let’s change some multi-valued properties of Human Species and:

  • add skinColor albino

  • update the blue eyeColor with light blue

  • remove the hairColor red

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation updateHumanSpecies { update_Species(where: {ID: "https://swapi.co/vocabulary/Human"}, objects: { skinColor: [{value: "albino"}] eyeColor: [{value: "light blue", patch: "blue"}] hairColor: [{value: null, patch: "red"}] }) { species { id name skinColor eyeColor hairColor } affected_count { kind count } affected_objects { kind ids } } }
{ "data": { "update_Species": { "species": [ { "id": "https://swapi.co/vocabulary/Human", "name": "Human", "skinColor": [ "black", "asian", "caucasian", "hispanic", "albino" ], "eyeColor": [ "brown", "hazel", "green", "grey", "amber", "light blue" ], "hairColor": [ "brown", "black", "blonde" ] } ], "affected_count": [ { "kind": "Species", "count": 1 } ], "affected_objects": [ { "kind": "Species", "ids": [ "https://swapi.co/vocabulary/Human" ] } ] } } }

That was easy, right? Now let’s change some relations. We go to Lando Calrissian again and change him again:

  • to appear in the new film The Phantom Menace identified by the ID https://swapi.co/resource/film/4.

  • remove the appearance from the film The Empire Strikes Back with ID https://swapi.co/resource/film/2.

  • change his starship allocation from Millennium Falcon with ID https://swapi.co/resource/starship/10 to the Death Star with ID https://swapi.co/resource/starship/9.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation updateHuman { update_Human(where: {ID: "https://swapi.co/resource/human/25"}, objects: { film: [{ ids: "https://swapi.co/resource/film/4" },{ patch: "https://swapi.co/resource/film/2" }] starship: { ids: "https://swapi.co/resource/starship/9", patch: "https://swapi.co/resource/starship/10" } }) { human { name film { id name } starship { id name } } } }
{ "data": { "update_Human": { "human": [ { "name": "Lando Calrissian", "film": [ { "id": "https://swapi.co/resource/film/4", "name": "Star Wars: Episode I – The Phantom Menace" }, { "id": "https://swapi.co/resource/film/3", "name": "Return of the Jedi" } ], "starship": [ { "id": "https://swapi.co/resource/starship/9", "name": "Death Star" } ] } ] } } }

Update Semantic Objects by ID with Nesting

Now let’s dive into the object structure and try to update other objects through an existing relation between them. If a nested update points to an object that does not have an existing relation, but the parent updates, then nothing will be changed.

Note

If no relation exists between the parent and the nested object, the nested update operation does nothing.

Let’s update Lando Calrissian again by updating his homeworld and set a new description, a planet diameter, as well as add new resident https://swapi.co/resource/human/28.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation updateHuman { update_Human(where: {ID: "https://swapi.co/resource/human/25"}, objects: { homeworld: { planet: { desc: { value: {value: "The home planet of the famous Lando Calrissian", lang: "en"}} diameter: 14600 resident: { ids: "https://swapi.co/resource/human/28" } } patch: "https://swapi.co/resource/planet/30" } }) { human { name homeworld { id name desc{ value } diameter resident { id name } } } } }
{ "data": { "update_Human": { "human": [ { "name": "Lando Calrissian", "homeworld": { "id": "https://swapi.co/resource/planet/30", "name": "Socorro", "desc": [ { "value": "The home planet of the famous Lando Calrissian" } ], "diameter": "14600", "resident": [ { "id": "https://swapi.co/resource/human/25", "name": "Lando Calrissian" }, { "id": "https://swapi.co/resource/human/28", "name": "Mon Mothma" } ] } } ] } } }

We can see that the changes are applied to the linked homeworld and the planet’s resident list now include the Mon Mothma.

The other important thing to note here is how the nested changes are wrapped in the property named planet. This property matches the relation target type. When dealing with concrete types, the relation range matches the type name. However, when the range of the relation is interface type, then we have more options. We can choose to use one of the sub-types or the parent interface. Let’s look at an example.

We will update the planet Socorro and its residents.

In the previous example, we added a second resident to the planet of Socorro with name Mon Mothma. Now, let’s make Lando and Mon friends by creating a friend relation between them. As the friend relation is not part of the Character interface, we cannot use it directly during an update operation. What we can do is use a concrete type name to access any specific property for that type. In this case, we can use the human type selector to access the specific Human properties as well as the inherited properties from the Character interface. This is called a type selector as the Platform will automatically add a check whether the updated object matches the specified type, and will not modify the related entity if the type does not match.

Note

To access a specific type property, use the concrete type selector in relations with interface range.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation updatePlanet { update_Planet(where: {ID: "https://swapi.co/resource/planet/30"}, objects: { resident: [ { character:{ desc: {value: {value: "Lando Calrissian's friend", lang: "en"}} } human: { friend: { ids: "https://swapi.co/resource/human/25" } } where: {ID: "https://swapi.co/resource/human/28"} }, { character:{ desc: {value: {value: "Mon Mothma's friend", lang: "en"}} } human: { friend: { ids: "https://swapi.co/resource/human/28" } } where: {ID: "https://swapi.co/resource/human/25"} } ] }) { planet { name resident { id name desc{ value } ... on Human { friend { name desc { value } ... on Human { friend { name } } } } } } } }
{ "data": { "update_Planet": { "planet": [ { "name": "Socorro", "resident": [ { "id": "https://swapi.co/resource/human/25", "name": "Lando Calrissian", "desc": [ { "value": "Mon Mothma's friend" } ], "friend": [ { "name": "Mon Mothma", "desc": [ { "value": "Lando Calrissian's friend" } ], "friend": [ { "name": "Lando Calrissian" } ] } ] }, { "id": "https://swapi.co/resource/human/28", "name": "Mon Mothma", "desc": [ { "value": "Lando Calrissian's friend" } ], "friend": [ { "name": "Lando Calrissian", "desc": [ { "value": "Mon Mothma's friend" } ], "friend": [ { "name": "Mon Mothma" } ] } ] } ] } ] } } }

Update Semantic Objects Using Full Filters

In this section, we will learn how to update objects using filters.

Let’s start with a simple update by name.

Suppose we want to update our friend Lando Calrissian but we do not know his ID. This will not be a problem, as we can search him by his first name, and add a new friend to him. While we are at it, let’s also add Lando’s friend as a resident in his homeworld.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation updateHuman { update_Human(where: {name: {IRE: "Lando"}}, objects: { friend: [{ ids: "https://swapi.co/resource/human/39" }] homeworld:{ patch: "https://swapi.co/resource/planet/30" planet:{ resident: { ids: "https://swapi.co/resource/human/39" } } } }) { human { name friend { id name } } } }
{ "data": { "update_Human": { "human": [ { "name": "Lando Calrissian", "friend": [ { "id": "https://swapi.co/resource/human/28", "name": "Mon Mothma" }, { "id": "https://swapi.co/resource/human/39", "name": "Ric Olié" } ] } ] } } }

Now let’s dive deeper. Suppose we have a task to do all of the following in a single request:

  1. If there are Human residents on planet Socorro, do the following tasks:

  2. Add new resident to the planet with ID https://swapi.co/resource/droid/3 (this would be R2-D2);

  3. Assign a primaryFunction to that droid as well as change his homeworld;

  4. Assign Lando and Mon and the newly added droid to starship https://swapi.co/resource/starship/10 (the Millennium Falcon) if they are residents on Socorro;

  5. Make Lando and Mon friends with R2-D2.

Let’s try something different and build the query step by step.

In order to update any Planet, we need to use the update_Planet mutation. As we saw earlier, we can filter by any property. So write the following filter:

update_Planet(where: {name: {EQ: "Socorro"}, resident: {type: {IN: "https://swapi.co/vocabulary/Human"}}}

Or we can use the new type selector syntax and change it to

update_Planet(where: {name: {EQ: "Socorro"}, resident: {_ifHuman:{}}}

Next step will be to add a new resident. We already know how to it from the previous examples:

resident: {
   ids: "https://swapi.co/resource/droid/3"
}

We have added the new resident, so let’s assign it a primaryFunction. One of the features is to allow the modifying of an object right after adding it to the parent object so we can do the following:

resident: [
  {
    ids: "https://swapi.co/resource/droid/3"
  },
  {
    patch: "https://swapi.co/resource/droid/3"
    droid: {
      primaryFunction: "repair droid"
    }
  }

So we set a primary function, but what about changing the homeworld? As we need to replace the previous value of the droid’s homeworld with new value, we need to use the replace: true argument. With that change, the droid update is complete:

resident: [
  {
    ids: "https://swapi.co/resource/droid/3"
  },
  {
    patch: "https://swapi.co/resource/droid/3"
    droid: {
      primaryFunction: "repair droid"
      homeworld: {
        ids: "https://swapi.co/resource/planet/30"
        replace: true
      }
    }
  }

For the next step, we want to select Lando and Mon and the newly added droid, but we do not want to select Ric Olié that we added in the previous example. This can be done with a filter like:

where: {_ifHuman: {name: {IRE: "lando|mon"}}, _ifDroid: {}}

We have selected everyone we need to update, so let’s add a change that is common for all of them, namely to assign them to the starship with ID https://swapi.co/resource/starship/10.

resident: {
    where: {_ifHuman: {name: {IRE: "lando|mon"}}, _ifDroid: {}}
    character: {
      starship: {
        ids: "https://swapi.co/resource/starship/10"
        replace: true
      }
    }
}

What remains is to add the droid as a friend of Lando and Mon. From the previous examples, we saw that we can combine multiple type classifiers in a single request. They work as a filter as well, so we can safely add the last requirement to the same change.

resident: {
    where: {_ifHuman: {name: {IRE: "lando|mon"}}, _ifDroid: {}}
    character: {
      starship: {
        ids: "https://swapi.co/resource/starship/10"
        replace: true
      }
    }
    human: {
      friend: {
        ids: "https://swapi.co/resource/droid/3"
      }
    }
}

With this, the request is complete. These are the full request and the expected response:

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation updateHuman { update_Planet(where: {name: {EQ: "Socorro"}, resident: {_ifHuman:{}}}, objects: { resident: [ { ids: "https://swapi.co/resource/droid/3" }, { patch: "https://swapi.co/resource/droid/3" droid: { primaryFunction: "repair droid" homeworld: { ids: "https://swapi.co/resource/planet/30" replace: true } } }, { where: {_ifHuman: {name: {IRE: "lando|mon"}}, _ifDroid: {}} character: { starship: { ids: "https://swapi.co/resource/starship/10" replace: true } } human: { friend: { ids: "https://swapi.co/resource/droid/3" } } } ] }) { planet { name resident(orderBy: {name:ASC}) { name ... on Human { friend { name } } ... on Droid { primaryFunction } starship { name } homeworld { name } } } } }
{ "data": { "update_Planet": { "planet": [ { "name": "Socorro", "resident": [ { "name": "Lando Calrissian", "friend": [ { "name": "R2-D2" }, { "name": "Mon Mothma" }, { "name": "Ric Olié" } ], "starship": [ { "name": "Millennium Falcon" } ], "homeworld": { "name": "Socorro" } }, { "name": "Mon Mothma", "friend": [ { "name": "R2-D2" }, { "name": "Lando Calrissian" } ], "starship": [ { "name": "Millennium Falcon" } ], "homeworld": { "name": "Chandrila" } }, { "name": "R2-D2", "primaryFunction": "repair droid", "starship": [ { "name": "Millennium Falcon" } ], "homeworld": { "name": "Socorro" } }, { "name": "Ric Olié", "friend": [], "starship": [ { "name": "Naboo Royal Starship" } ], "homeworld": { "name": "Naboo" } } ] } ] } } }

Object Deletion

Delete Semantic Objects of a Single Type

It is possible to delete a single or multiple objects of the same type. A delete mutation is added for every concrete Semantic Object. It is not possible to delete objects from an abstract object such as Character, but delete mutations for all its concrete objects are available, e.g., delete_Human, delete_Droid, delete_Aleena, etc.

Note

When deleting Semantic Objects, it is mandatory to provide a Semantic Object ID filter for one or many objects. Filtered deletes, like for example “Delete all Humans with green eyes”, are currently not supported. Delete mutations are kept simple and will operate using a mandatory where: {ID: ["X"]} filter. Batch delete using filters such as where: {ID: ["X", "Y"] is supported as well.

The sub-selection in the mutation is mandatory. We can either retrieve the deleted properties or check the affected_count and affected_objects in it.

  • affected_count returns the number of deleted objected sorted by kind.

  • affected_objects returns the ids of the removed objects by kind.

Let’s say we want to delete a human named Lando Calrissian who has a unique ID property that equals https://swapi.co/resource/human/25.

The following delete mutation will remove all properties of Lando Calrissian from the database and return the deleted values that were requested in the sub-selection.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation deleteHumans { delete_Human( where: {ID: ["https://swapi.co/resource/human/25"]}) { human { id name (lang: "en") type desc (lang: "en") { value } eyeColor hairColor skinColor birthYear film { id } height mass homeworld { id } starship { id } vehicle { id } species { id } gender } affected_count { kind count } affected_objects { kind ids } } }
{ "data": { "delete_Human": { "human": [ { "id": "https://swapi.co/resource/human/25", "name": "Lando Calrissian", "type": [ "https://swapi.co/vocabulary/Character", "https://swapi.co/vocabulary/Human", "https://swapi.co/vocabulary/Sentient", "https://swapi.co/vocabulary/Mammal" ], "desc": [ { "value": "Mon Mothma's friend" } ], "eyeColor": [ "brown" ], "hairColor": [ "black" ], "skinColor": [ "dark" ], "birthYear": "31BBY", "film": [ { "id": "https://swapi.co/resource/film/2" }, { "id": "https://swapi.co/resource/film/3" } ], "height": "177.0", "mass": "79.0", "homeworld": { "id": "https://swapi.co/resource/planet/30" }, "starship": [ { "id": "https://swapi.co/resource/starship/10" } ], "vehicle": [], "species": { "id": "https://swapi.co/vocabulary/Human" }, "gender": "male" } ], "affected_count": [ { "kind": "Human", "count": 1 } ], "affected_objects": [ { "kind": "Human", "ids": [ "https://swapi.co/resource/human/25" ] } ] } } }

Here is another example with batch delete, where multiple objects of type Human are being deleted.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation deleteHumans { delete_Human( where: {ID: ["https://swapi.co/resource/human/1", "https://swapi.co/resource/human/42", "https://swapi.co/resource/human/88"]}) { human { id name (lang: "en") type desc (lang: "en") { value } eyeColor hairColor skinColor birthYear film { id } height mass homeworld { id } starship { id } vehicle { id } species { id } gender } affected_count { kind count } } }
{ "data": { "delete_Human": { "human": [ { "id": "https://swapi.co/resource/human/88", "name": "Captain Phasma", "type": [ "https://swapi.co/vocabulary/Character", "https://swapi.co/vocabulary/Human", "https://swapi.co/vocabulary/Sentient", "https://swapi.co/vocabulary/Mammal" ], "desc": [], "eyeColor": [], "hairColor": [], "skinColor": [], "birthYear": null, "film": [ { "id": "https://swapi.co/resource/film/7" } ], "height": null, "mass": null, "homeworld": { "id": "https://swapi.co/resource/planet/28" }, "starship": [], "vehicle": [], "species": { "id": "https://swapi.co/vocabulary/Human" }, "gender": "female" }, { "id": "https://swapi.co/resource/human/42", "name": "Quarsh Panaka", "type": [ "https://swapi.co/vocabulary/Character", "https://swapi.co/vocabulary/Human", "https://swapi.co/vocabulary/Sentient", "https://swapi.co/vocabulary/Mammal" ], "desc": [], "eyeColor": [ "brown" ], "hairColor": [ "black" ], "skinColor": [ "dark" ], "birthYear": "62BBY", "film": [ { "id": "https://swapi.co/resource/film/4" } ], "height": "183.0", "mass": null, "homeworld": { "id": "https://swapi.co/resource/planet/8" }, "starship": [], "vehicle": [], "species": { "id": "https://swapi.co/vocabulary/Human" }, "gender": "male" }, { "id": "https://swapi.co/resource/human/1", "name": "Luke Skywalker", "type": [ "https://swapi.co/vocabulary/Character", "https://swapi.co/vocabulary/Human", "https://swapi.co/vocabulary/Sentient", "https://swapi.co/vocabulary/Mammal" ], "desc": [ { "value": "character in Star Wars" } ], "eyeColor": [ "blue" ], "hairColor": [ "blond" ], "skinColor": [ "fair" ], "birthYear": "19BBY", "film": [ { "id": "https://swapi.co/resource/film/6" }, { "id": "https://swapi.co/resource/film/2" }, { "id": "https://swapi.co/resource/film/3" }, { "id": "https://swapi.co/resource/film/1" }, { "id": "https://swapi.co/resource/film/7" } ], "height": "172.0", "mass": "77.0", "homeworld": { "id": "https://swapi.co/resource/planet/1" }, "starship": [ { "id": "https://swapi.co/resource/starship/12" }, { "id": "https://swapi.co/resource/starship/22" } ], "vehicle": [ { "id": "https://swapi.co/resource/vehicle/14" }, { "id": "https://swapi.co/resource/vehicle/30" } ], "species": { "id": "https://swapi.co/vocabulary/Human" }, "gender": "male" } ], "affected_count": [ { "kind": "Human", "count": 3 } ] } } }

There are some basic validations performed upon mutation execution. The mutation below, for example, tries to delete a Droid instead of Human:

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation deleteHumans { delete_Human( where: {ID: ["https://swapi.co/resource/droid/8"]}) { human { id name (lang: "en") type desc (lang: "en") { value } eyeColor hairColor skinColor birthYear film { id } height mass homeworld { id } starship { id } vehicle { id } species { id } gender } affected_count { kind count } affected_objects { kind ids } } }
{ "errors": [ { "message": "ERROR: The object with ID: 'https://swapi.co/resource/droid/8' is expected to be of type '[voc:Human]'. However, the RDF data for this ID corresponds to type(s): 'Droid'.", "locations": [ { "line": 2, "column": 27 } ] } ] }

Not specifying any ID will result in an empty response as nothing has been deleted.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation deleteWithoutID { delete_Human (where:{ID:[]}){ human { id name } affected_count { kind count } affected_objects { kind ids } } }
{ "errors": [ { "message": "ERROR: Term 'IN' with empty value is not allowed @ 'delete_Human'", "locations": [ { "line": 2, "column": 13 } ], "extensions": { "classification": "ValidationError" } } ] }

Mutations for Literal Properties

Literal properties have special handling as they are scalar properties but are represented as multi-property structures like objects.

Any scalar or object type can be represented with the Literal type, with the difference being the type property of the queried or mutated object. The Ontotext Platform supports the following scalar-based types:

  • langString - represents literal string literals with non-empty language

  • stringOrLangString - represents literal string with or without language tag

You can read more about the literal-based types in the Literals and Union Datatypes section.

Creating Literal Properties

When adding literal values, the value property is required, but the rest of the properties are optional based on the concrete type of the property being saved.

When creating langString, the language property is required as the type does not allow properties without language tag. This rule, however, is only valid if a schema or property default implicit language spec is not set.

Note

For more information about the language configurations and defaults, see SOML property language configurations.

Let’s demonstrate a create mutation with the following example:

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation createHumanWithDescription { create_Human (objects: [ { rdfs_label: {value: "Poe Dameron"} desc: [{ value: "Poe Dameron is a fictional character in the Star Wars franchise. Introduced in the 2015 film Star Wars: The Force Awakens" }, { value: "Poe Dameron ist eine fiktive Figur in der Star Wars-Reihe. Eingeführt im Film Star Wars 2015: The Force Awakens", lang: "de" }] } ]) { human { name desc { value lang } } } }
{ "data": { "create_Human": { "human": [ { "name": "Poe Dameron", "desc": [ { "value": "Poe Dameron is a fictional character in the Star Wars franchise. Introduced in the 2015 film Star Wars: The Force Awakens", "lang": "en" }, { "value": "Poe Dameron ist eine fiktive Figur in der Star Wars-Reihe. Eingeführt im Film Star Wars 2015: The Force Awakens", "lang": "de" } ] } ] } } }

Here, we added two values for the desc property. The first one did not have language tag specified, and in the response, it does. This is the result of the implicit language configuration that in this case was set to implicit: en.

All possible configurations for langString typed properties are as follows:

value: “A title”

No implicit in schema

implicit: en

lang: “de”

“A title”@de

“A title”@de

lang: “”

error

error

lang: null

error

“A title”@en

no lang value

error

“A title”@en

When creating a stringOrLangString value, the language is optional, and the end result depends on the value of the lang property. This behavior looks the following way:

value: “A title”

No implicit in schema

implicit: en

lang: “de”

“A title”@de

“A title”@de

lang: “”

“A title”

“A title”

lang: null

“A title”

“A title”@en

no lang value

“A title”

“A title”@en

For any other type, the lang argument is forbidden and an error will be emitted if a non-null value is passed.

For both langString or stringOrLangString typed properties, the type property is forbidden. This is because it is implied based on the lang argument, and will have either rdf:langString or xsd:string value.

Updating Literal Properties

The Platform provides powerful literal properties updates. In addition to the supported operations over scalar properties such as add, replace all, or update a specific value, it also supports arbitrary condition filtering of the value and/or the language by adding support for the where argument.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation addHumanDescription { update_Human (where: {name: "Poe Dameron"}, objects: [ { desc: [{ value: { value: "Poe Dameron es un personaje ficticio de la franquicia de Star Wars. Introducido en la película de 2015 Star Wars: The Force Awakens", lang: "es" } }] } ]) { human { name desc { value lang } } } }
{ "data": { "update_Human": { "human": [ { name: "Poe Dameron", desc: [ { value: "Poe Dameron is a fictional character in the Star Wars franchise. Introduced in the 2015 film Star Wars: The Force Awakens", lang: "en" }, { value: "Poe Dameron ist eine fiktive Figur in der Star Wars-Reihe. Eingeführt im Film Star Wars 2015: The Force Awakens", lang: "de" }, { value: "Poe Dameron es un personaje ficticio de la franquicia de Star Wars. Introducido en la película de 2015 Star Wars: The Force Awakens", lang: "es" } ] } ] } } }

In this example, we added a new Spanish-language description to the previously added Human named Poe Dameron. In the next one, we will update the value with more details.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation updateHumanDescription { update_Human (where: {name: "Poe Dameron"}, objects: [ { desc: [{ value: { value: "Poe Dameron es un personaje ficticio de la franquicia de Star Wars. Introducido en la película de 2015 Star Wars: The Force Awakens, es interpretado por Oscar Isaac. Poe es un piloto de caza del ala X para la Resistencia que inadvertidamente trae al soldado de asalto renegado Finn (John Boyega) y al carroñero Jakku Rey (Daisy Ridley) en la lucha contra, y eventualmente una victoria sobre, la siniestra Primera Orden. Aparece en los medios y el merchandising de The Force Awakens, así como en una serie de cómics del mismo nombre, y aparecerá en la próxima secuela de películas, Star Wars: The Last Jedi. Isaac y el personaje han recibido críticas positivas, comparando a Poe con la caracterización de Han Solo (Harrison Ford) en la trilogía original de Star Wars.", lang: "es" }, patch: { value: "Poe Dameron es un personaje ficticio de la franquicia de Star Wars. Introducido en la película de 2015 Star Wars: The Force Awakens", lang: "es" } }] } ]) { human { name desc { value lang } } } }
{ "data": { "update_Human": { "human": [ { name: "Poe Dameron", desc: [ { value: "Poe Dameron is a fictional character in the Star Wars franchise. Introduced in the 2015 film Star Wars: The Force Awakens", lang: "en" }, { value: "Poe Dameron ist eine fiktive Figur in der Star Wars-Reihe. Eingeführt im Film Star Wars 2015: The Force Awakens", lang: "de" }, { value: "Poe Dameron es un personaje ficticio de la franquicia de Star Wars. Introducido en la película de 2015 Star Wars: The Force Awakens, es interpretado por Oscar Isaac. Poe es un piloto de caza del ala X para la Resistencia que inadvertidamente trae al soldado de asalto renegado Finn (John Boyega) y al carroñero Jakku Rey (Daisy Ridley) en la lucha contra, y eventualmente una victoria sobre, la siniestra Primera Orden. Aparece en los medios y el merchandising de The Force Awakens, así como en una serie de cómics del mismo nombre, y aparecerá en la próxima secuela de películas, Star Wars: The Last Jedi. Isaac y el personaje han recibido críticas positivas, comparando a Poe con la caracterización de Han Solo (Harrison Ford) en la trilogía original de Star Wars.", lang: "es" } ] } ] } } }

Literal Properties Validation

When adding or updating langString or stringOrLangString types, the language parameter will be validated against an optional validation pattern.

Note

You can read more on how to configure the validation pattern in the SOML property language configurations section.

During the validation process, the following rules apply:

  • The mutation satisfies the property language validation pattern if all provided values match the positive list of allowed languages (if any), and no value matches the negative list of language exclusions (if any).

  • If UNIQ is specified in the validation pattern, then a property cannot have multiple values in the same language.

Note

Due to technical limitations, you can leverage the full support of the unique language validation feature only when SHACL validation is enabled. When SHACL validation is disabled, only create mutations will be validated for language uniqueness.

To enable SHACL support, see SHACL static validators.

Given the following SOML schema:

objects:
  Person:
    props:
      rdfs:label
        min: 1
        lang: {validate: "UNIQ"}

Either of these mutations will cause a validation error:

mutation {
  create_Human(objects: [
    {
       id: "http://example.org/resource/Person1"
       rdfs_label: [
         {value: "Alice"},
         {value: "Bob"}
       ]
    },
    {
       id: "http://example.org/resource/Person2"
       rdfs_label: [
         {value: "Alice", lang: "en"},
         {value: "Bob", lang: "en"}
       ]
    }
  ]) {
    human {
      rdfs_label {
        value
        lang
      }
    }
  }
}

This is intended to check the SKOS Integrity Condition S14, which states that a resource must have no more than one value of skos:prefLabel per language tag.

Mutations that specify an invalid language tag or violate a lang.validate spec are rolled back, and a corresponding error is returned.

Note

Validation of constraints based on ALL is currently not supported, because it relies on a specific SHACL shape that is currently not available in the RDF4J library. The progress of the required functionality in RDF4J can be tracked in the SHACL - Qualified shapes issue.

Multiple Mutations per Request

If multiple mutations are part of a mutation request, they are executed strictly one after another, starting from the top of the request.

Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation insertHuman { create_Human(objects: [{id: "https://swapi.co/resource/human/1001", type: ["https://swapi.co/vocabulary/Human", "https://swapi.co/vocabulary/Character"], rdfs_label: {value: "New human", lang: "en"}}]) { human { name } affected_count { kind count } affected_objects { kind ids } } update_Human_1: update_Human(objects: {rdfs_label: {value: {value: "New human edit 1", lang: "en"}}}, where: {ID: "https://swapi.co/resource/human/1001"}) { human { name } affected_count { kind count } affected_objects { kind ids } } update_Human_2: update_Human(objects: {rdfs_label: {value: {value: "New human edit 2", lang: "en"}}}, where: {ID: "https://swapi.co/resource/human/1001"}) { human { name } affected_count { kind count } affected_objects { kind ids } } }
{ "data": { "create_Human": { "human": [ { "name": "New human" } ], "affected_count": [ { "kind": "Human", "count": 1 } ], "affected_objects": [ { "kind": "Human", "ids": [ "https://swapi.co/resource/human/1001" ] } ] }, "update_Human_1": { "human": [ { "name": "New human" } ], "affected_count": [ { "kind": "Human", "count": 1 } ], "affected_objects": [ { "kind": "Human", "ids": [ "https://swapi.co/resource/human/1001" ] } ] }, "update_Human_2": { "human": [ { "name": "New human" } ], "affected_count": [ { "kind": "Human", "count": 1 } ], "affected_objects": [ { "kind": "Human", "ids": [ "https://swapi.co/resource/human/1001" ] } ] } } }

As you can see, first a new object of type Human was created, then updated twice, and finally it was completely deleted in one mutation request.

Note

All mutation requests are executed in a single transaction in order to ensure data consistency. If there is a problem with any of the mutations, the transaction will be rolled back and no changes will be committed to the store.

Furthermore, the result parts of the mutations are executed in a separate transaction after the data update is done. They only read the data from the store, which should not trigger the rollback of the request in case there is a problem during execution. This behavior is also necessary when the store is used in cluster mode.

Automatic Object Type Generation

As explained in the Object Typing section:

  1. Each concrete Semantic Object type has a Type Descriptor.

  2. The Type Descriptor is optional for the Abstract types.

  3. Each object of a certain type is restricted to its own type descriptor as well as all its parents’ ones.

During the create and update phases, we need to make sure that all Type Descriptors are satisfied.

This is why for the create mutations we have enabled the option for automatic generation of the properties that describe the object. This functionality is triggered with the following property (enabled by default):

graphql.mutation.generation.options.TypeDataGenerator.enabled=true

So given the following schema:

objects:
  ConceptScheme: {kind: abstract, type: [skos:ConceptScheme]}
  Industry:      {inherits: ConceptCommon, type: [industry/], typeProp: skos:inScheme}

If we try to create an Industry object without specifying its type and skos:inScheme, they will be automatically inferred to skos:ConceptScheme and industry/, and added to the mutation.

However, sometimes we cannot infer a value for the descriptors. This happens when we have a type with multi-valued type property. For example:

objects:
  Geoname:
    regex: '^http://sws\.geonames\.org/\d+/$'
    type: [gn:Feature]
  Country:
    inherits: Geoname
    typeProp: gn:featureCode
    type: [gn:A.PCLI, gn:A.PCLD, gn:A.PCLIX, gn:A.PCLS, gn:A.PCL, gn:A.TERR, gn:A.PCLF]

If we want to create an object of type Country, we would not be able to determine its gn:featureCode. The reason for this is that, for the object to be valid, it is sufficient to have as gn:featureCode at least one of the following: [gn:A.PCLI, gn:A.PCLD, gn:A.PCLIX, gn:A.PCLS, gn:A.PCL, gn:A.TERR, gn:A.PCLF], but not all of them. So there is no way to determine which gn:featureCode needs to be picked from the list.

So for a create mutation to pass, each Country object needs to have its gn:featureCode explicitly set. However, the Type Generation will still work for the Geoname class, so the property type: gn:Feature will be added automatically.

Note that the Automatic Type Generation will only work if the property that needs to be added is not added explicitly. This means that if we are creating a Country with type: skos:Person, we would not perform any of the following possible steps:

  • replace it with the proper type,

  • append the proper type resulting in type: [skos:Person, gn:featureCode].

In this case, you will get notified that the object does not conform to its class and the mutation will not be executed.