{
  "title": "Sparnatural query schema",
  "version": "10.0.0",
  "description": "A JSON schema describing the structure of queries as read or written by the Sparnatural query builder UI",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://sparnatural.eu/def/schemas/sparnatural-query-schema.json",
  "$defs": {

    "VariableTerm": {
      "description": "A SPARQL variable with its name, such as 'x'",
      "type":"object",
      "properties": {
        "termType": {
          "type" : "string",
          "const": "Variable"
        },
        "value": {
          "type": "string"
        }
      },
      "required":["termType", "value"],
      "additionalProperties":false
    },

    "VariableExpression": {
      "description": "A SPARQL aggregation expression, with the aggregation function on the original variable and the resulting variable, such as '(COUNT(?x) AS ?count)' ",
      "type":"object",
      "properties": {
        "variable" : {
          "$ref": "#/$defs/VariableTerm"
        },
        "expression": {
          "description": "An aggregation expression, with an aggregation function and the variable being aggregated, such as 'COUNT(?x)'",
          "type":"object",
          "properties": {
            "type": {
              "type":"string",
              "const":"aggregate"
            },
            "aggregation": {
              "description": "The aggregation function name, as one of the allowed SPARQL aggregation functions",
              "type":"string",
              "enum": ["count","max","min","sample","sum","avg","group_concat"]
            },
            "distinct": {
              "description": "Whether the aggregation function should use the DISTINCT keywork",
              "type":["boolean","null"]
            },
            "expression": {
              "$ref": "#/$defs/VariableTerm"
            }
          },
          "required":["type","aggregation","expression","distinct"],
          "additionalProperties":false
        }
      },
      "required":["variable","expression"],
      "additionalProperties":false
    },

    "Branch": {
      "description": "A Sparnatural query branch, consisting of a criteria line (subject, predicate, object), and optional children branches",
      "type" : "object",
      "properties": {
        "line" : {
          "description": "One line of criteria in Sparnatural, corresponding to a subject, a predicate, an object, and also the type (rdf:type) of the subject and the object",
          "type":"object",
          "properties": {
            "s" : {
              "description": "The subject variable name, such as 'Person_1'",
              "type": "string"
            },
            "p" : {
              "description": "The full URI of the predicate",
              "type": "string",
              "enum" : [ 
"https://data.mydomain.com/ontologies/sparnatural-config/Artwork_label",
"https://data.mydomain.com/ontologies/sparnatural-config/Artwork_author",
"https://data.mydomain.com/ontologies/sparnatural-config/Artwork_creationYear",
"https://data.mydomain.com/ontologies/sparnatural-config/Artwork_displayedAt",
"https://data.mydomain.com/ontologies/sparnatural-config/Artwork_thumbnail",
"https://data.mydomain.com/ontologies/sparnatural-config/Artwork_description",
"https://data.mydomain.com/ontologies/sparnatural-config/Museum_country",
"https://data.mydomain.com/ontologies/sparnatural-config/Museum_label",
"https://data.mydomain.com/ontologies/sparnatural-config/Museum_numberOfVisitors",
"https://data.mydomain.com/ontologies/sparnatural-config/Museum_displays",
"https://data.mydomain.com/ontologies/sparnatural-config/Museum_inWikidata",
"https://data.mydomain.com/ontologies/sparnatural-config/Museum_thumbnail",
"https://data.mydomain.com/ontologies/sparnatural-config/Museum_description",
"https://data.mydomain.com/ontologies/sparnatural-config/Country_countryOf",
"https://data.mydomain.com/ontologies/sparnatural-config/Country_label",
"https://data.mydomain.com/ontologies/sparnatural-config/Country_deathPlace",
"https://data.mydomain.com/ontologies/sparnatural-config/Country_birthPlace",
"https://data.mydomain.com/ontologies/sparnatural-config/Person_bornIn",
"https://data.mydomain.com/ontologies/sparnatural-config/Person_diedIn",
"https://data.mydomain.com/ontologies/sparnatural-config/Person_label",
"https://data.mydomain.com/ontologies/sparnatural-config/Person_birthDate",
"https://data.mydomain.com/ontologies/sparnatural-config/Person_classifiedIn",
"https://data.mydomain.com/ontologies/sparnatural-config/Person_created",
"https://data.mydomain.com/ontologies/sparnatural-config/Person_deathYear",
"https://data.mydomain.com/ontologies/sparnatural-config/Person_movement",
"https://data.mydomain.com/ontologies/sparnatural-config/Person_thumbnail",
"https://data.mydomain.com/ontologies/sparnatural-config/Person_description",
"https://data.mydomain.com/ontologies/sparnatural-config/Movement_movementIncludes",
"https://data.mydomain.com/ontologies/sparnatural-config/Movement_description",
"https://data.mydomain.com/ontologies/sparnatural-config/Movement_label",
"https://data.mydomain.com/ontologies/sparnatural-config/MuseumWikidata_situe_a"
                 ]
            },
            "o" : {
              "description": "The object variable name, such as 'Country_2'",
              "type": "string"
            },
            "sType" : {
              "description": "The full URI of the type of the subject",
              "type": "string",
              "enum" : [
"https://data.mydomain.com/ontologies/sparnatural-config/Artwork",
"https://data.mydomain.com/ontologies/sparnatural-config/Museum",
"https://data.mydomain.com/ontologies/sparnatural-config/Country",
"https://data.mydomain.com/ontologies/sparnatural-config/Person",
"https://data.mydomain.com/ontologies/sparnatural-config/Movement",
"https://data.mydomain.com/ontologies/sparnatural-config/Category",
"https://data.mydomain.com/ontologies/sparnatural-config/MuseumWikidata",
"https://data.mydomain.com/ontologies/sparnatural-config/Image",
"https://data.mydomain.com/ontologies/sparnatural-config/Date",
"https://data.mydomain.com/ontologies/sparnatural-config/Position",
"https://data.mydomain.com/ontologies/sparnatural-config/Text",
"https://data.mydomain.com/ontologies/sparnatural-config/Number"
                ]
            },
            "oType" : {
              "description": "The full URI of the type of the object",
              "type": "string",
              "enum" : [
"https://data.mydomain.com/ontologies/sparnatural-config/Artwork",
"https://data.mydomain.com/ontologies/sparnatural-config/Museum",
"https://data.mydomain.com/ontologies/sparnatural-config/Country",
"https://data.mydomain.com/ontologies/sparnatural-config/Person",
"https://data.mydomain.com/ontologies/sparnatural-config/Movement",
"https://data.mydomain.com/ontologies/sparnatural-config/Category",
"https://data.mydomain.com/ontologies/sparnatural-config/MuseumWikidata",
"https://data.mydomain.com/ontologies/sparnatural-config/Image",
"https://data.mydomain.com/ontologies/sparnatural-config/Date",
"https://data.mydomain.com/ontologies/sparnatural-config/Position",
"https://data.mydomain.com/ontologies/sparnatural-config/Text",
"https://data.mydomain.com/ontologies/sparnatural-config/Number"
                ]
            },
            "values": {
              "description": "The values selected as search criteria in this line. This is optional, no values may be selected.",
              "type": ["array", "null"],
              "items": {
                 "anyOf" : [
                    {
                      "description": "An RDF term value, either an IRI or a Literal",
                      "type":"object",
                      "extends" : [{"$ref": "#/$defs/WidgetValue"}],
                      "properties": {
                        "type" : {
                          "description": "The type of the RDF node, either literal, blank node or URI",
                          "type" : "string",
                          "enum" : ["literal","bnode","uri"]
                        },
                        "value" : {
                          "description": "The value, either the URI itself, the lexical form of the literal, or the blank node identifier",
                          "type" : "string"
                        },
                        "xml:lang" : {
                          "description": "The language code of the value, if the term is a literal with a language.",
                          "type" : ["string","null"]
                        },
                        "datatype" : {
                          "description": "The full URI of the datatype of the Literal, it it has one.",
                          "type" : ["string","null"]
                        },
                        "label" : {
                          "description": "The display label of this search criteria, typically the name of the named entity recognized in the text.",
                          "type" : ["string","null"]
                        }
                      },
                      "required" : ["type", "value", "xml:lang", "datatype", "label"],
                      "additionalProperties": false
                    },
                    {
                      "description": "A boolean value criteria",
                      "type":"object",
                      "properties": {
                        "boolean": {
                          "description": "The boolean value, either tru or false",
                          "type":"boolean"
                        },
                        "label" : {
                          "description": "The display label of this search criteria, typically 'true' or 'false'",
                          "type" : ["string","null"]
                        }
                      },
                      "required" : ["boolean","label"],
                      "additionalProperties": false
                    },
                    {
                      "description": "A date range criteria, with a start date and an end date. At least a start date or an end date must be provided.",
                      "type":"object",
                      "properties": {
                        "start": {
                          "description": "The start date of the range, either in a date format, a year format, or a date-time format",
                          "type":["string","null"]
                        },
                        "stop": {
                          "description": "The end date of the range, either in a date format, a year format, or a date-time format",
                          "type":["string","null"]
                        },
                        "label" : {
                          "description": "The display label of this search criteria, typically indicating 'from start date to end date'",
                          "type" : ["string","null"]
                        }
                      },
                      "required":["start", "stop", "label"],
                      "additionalProperties": false
                    },
                    {
                      "description": "A GeoSPARQL query criteria. This is a list of shapes, either polygons or rectangles, each polygons containing a list of latitude and longitude pairs",
                      "type":"object",
                      "properties": {
                        "type": {
                          "description": "The type of the searched shape, either 'Rectangle' or 'Polygon'.",
                          "type": "string",
                          "enum": ["Rectangle","Polygon"]
                        },
                        "coordinates": {
                          "description": "The coordinates of the shapes, a list of latitude/longitude pairs",
                          "type" : "array",
                          "items" : {
                            "type" : "array",
                            "items" : {
                              "type": "object",
                              "properties": {
                                "lat": {
                                  "description": "A latitude",
                                  "type":"number"
                                },
                                "long": {
                                  "description": "A longitude",
                                  "type":"number"
                                }
                              },
                              "required":["lat","long"],
                              "additionalProperties": false
                            }            
                          }
                        },
                        "label" : {
                          "description": "The display label of this search criteria, which can be for example the number of square kilometer of the area selected",
                          "type" : ["string","null"]
                        }
                      },
                      "required":["type","coordinates","label"],
                      "additionalProperties": false
                    },
                    {
                      "description": "A number search range, with a minimum and maximum value. At least the minimum or maximum should be provided.",
                      "type":"object",
                      "properties": {
                        "min" : {
                          "type":["number","null"]
                        },
                        "max" : {
                          "type":["number","null"]
                        },
                        "label" : {
                          "description": "The display label of this search criteria, mentioning the min and max values",
                          "type" : ["string","null"]
                        }
                      },
                      "required":["min","max","label"],
                      "additionalProperties": false
                    },
                    {
                      "description": "A string search criteria that will typically be translated to a FILTER(REGEX(...)) SPARQL function",
                      "type":"object",
                      "properties": {
                        "regex": {
                          "description": "The string to search on, that can be a regular expression",
                          "type":"string"
                        },
                        "label" : {
                          "description": "The display label of this search criteria",
                          "type" : ["string","null"]
                        }
                      },
                      "required":["regex","label"],
                      "additionalProperties": false
                    }
                  ]
              }
            }
          },
          "required": ["s","p","o","sType","oType", "values"],
          "additionalProperties": false
        },
        "children": {
          "description": "The children branches. This is a recusrsive reference. The array may be empty if there are no children.",
          "type":"array",
          "items": {
            "$ref": "#/$defs/Branch"
          }
        },
        "optional": {
          "description": "Whether the OPTIONAL SPARQL keyword is applied at this level, on this branch and all children branches",
          "type":["boolean","null"]
        },
        "notExists": {
          "description": "Whether the FILTER NOT EXISTS SPARQL keyword is applied at this level, on this branch and all children branches",
          "type":["boolean","null"]
        }
      },
      "required":["line","children","optional","notExists"],
      "additionalProperties":false
    }
  },
  "type": "object",
  "description": "A full Sparnatural query, with selected variables, query criterias, order clause",
  "properties": {
    "distinct": {
      "description": "Whether the SELECT clause will use a DISTINCT keyword.",
      "type":["boolean","null"]
    },
    "variables": {
      "description": "The variables in the SELECT clause of the query, either simple variables, or aggregated variables",
      "type":"array",
      "items": {
        "anyOf": [
          {"$ref": "#/$defs/VariableTerm"},
          {"$ref": "#/$defs/VariableExpression"}
        ]
      }
    },
    "order": {
      "description": "The order criteria to be applied to the first column of the query, either ascending, descending, or no order",
      "type":["string", "null"],
      "enum": ["asc","desc","noord"]
    },
    "branches": {
      "description": "The query criterias, forming the 'WHERE' clause of the query",
      "type":"array",
      "items": {
        "$ref": "#/$defs/Branch"
      }
    }
  },
  "required":["distinct","branches","order","variables"],
  "additionalProperties":false
}