Primary Keys with URI Pattern

From RDF Data Shapes Working Group
Jump to: navigation, search

In many scenarios a class has a primary key - one of its properties that uniquely identifies instances of that class. For example, ex:ISOCountry would have primary key ex:code. Furthermore, it is often useful to have a mapping between those property values and the URI of that instance. For example, if you have an instance with

    ex:code "de" .

then the URI of that instance should be <http://example.org/Country-de> based on a URI start of "http://example.org/Country-".

This information is useful on many levels:

  • When importing data from databases or spreadsheets, suitable URIs can be created for each row
  • When someone creates a new instance, the UI can force her to enter a primary key, make sure it's unique and directly create a suitable URI
  • a URI pattern makes sure that no duplicate instances exist
  • primary keys can be handled with a special life cycle, e.g. a UI can prevent changing primary key values once they are in production.

This is an example of information that is both a constraint check and structural metadata.

An example representation of this using a SPIN template is

   ex:ISOCountry a owl:Class ;
       spin:constraint [
           a spl:PrimaryKeyPropertyConstraint ;
           arg:property ex:code ;
           arg:uriStart "http://example.org/Country-"
       ] ...

Applications can query this structural metadata without a SPIN engine or knowledge of SPARQL. They only need to agree on the URI (here: spl:PrimaryKeyPropertyConstraint).

The SPIN template above can be represented as

   spl:PrimaryKeyPropertyConstraint
        a                   spl:ConstraintTemplate ;
        rdfs:comment        """
            Specifies that the given property is a primary key for instances of the associated
            class (and its subclasses).  If a property has been declared to be the primary key
            then each instance of the class must have exactly one value for that property.
            Furthermore, the URIs of those instances must start with a given string 
            (arg:uriStart), followed by the URL-encoded primary key value. For 
            example if arg:uriStart is \"http://example.org/country-\" and the 
            primary key for an instance is \"de\" then the URI must be 
            \"http://example.org/country-de\". Finally, as a result of the URI 
            policy, there can not be any other instance with the same value under 
            the same primary key policy."""^^xsd:string ;
        rdfs:label          "Primary key property constraint"^^xsd:string ;
        rdfs:subClassOf     spl:PropertyConstraintTemplates ;
        spin:body           [ a        sp:Construct ;
                              sp:text  """
    CONSTRUCT {
        _:cv a spin:ConstraintViolation ;
             rdfs:label ?label ;
             spin:violationRoot ?this ;
             spin:violationPath ?property .
    }
    WHERE {
        {
            FILTER NOT EXISTS {
                ?this ?property ?any .
            } .
            BIND (\"Missing value for primary key property\" AS ?label) .
        }
        UNION    {
            FILTER (spl:objectCount(?this, ?property) > 1) .
            BIND (\"Multiple values of primary key property\" AS ?label) .
        }
        UNION    {
            FILTER (spl:objectCount(?this, ?property) = 1) .
            ?this ?property ?value .
            BIND (CONCAT(?uriStart, ENCODE_FOR_URI(xsd:string(?value))) AS ?uri) .
            FILTER (xsd:string(?this) != ?uri) .
            BIND (CONCAT(\"Primary key value \", xsd:string(?value), \" does not align with the expected URI \", ?uri) AS ?label) .
        } .
    }"""^^xsd:string
                            ] ;
        spin:constraint     [ a              spl:Argument ;
                              rdfs:comment   "The start of the URIs of well-formed instances of the associated class."^^xsd:string ;
                              spl:predicate  arg:uriStart ;
                              spl:valueType  xsd:string
                            ] ;
        spin:labelTemplate  "The property {?property} is the primary key and URIs start with {?uriStart}"^^xsd:string .

The constraint check above returns a different error message depending on the number of values of the primary key property. (The declaration of the argument arg:property is inherited from its superclass).

This requirement is related to R-2-UNIQUE-INSTANCES and owl:hasKey, but expands on those with the URI generation pattern.