How to map RDF properties using SPARQL Update

You want to map RDF properties to properties from chosen RDF vocabularies. An inline table is a flexible way to specify mappings in SPARQL Update operations.

Problem

For example, consider you have the following table about Bulgarian local administrative units that you want to convert to RDF:

LAU2_NAT_CODE NAME_1 NAME_2_LAT CHANGE
00045 Мокрен Mokren 2015-01-01
00062 Аврамово Avramovo 2016-01-01
62617 Рибино Ribino 2015-01-01

You can automatically map this table to RDF using the Tabular component, which derives properties from column names in a given namespace, such as http://example.com/:

@prefix : <http://example.com/> .

[ :CHANGE "2015-01-01" ;
  :LAU2_NAT_CODE "00045" ;
  :NAME_1 "Мокрен" ;
  :NAME_2_LAT "Mokren" ] .

[ :CHANGE "2016-01-01" ;
  :LAU2_NAT_CODE "00062" ;
  :NAME_1 "Аврамово" ;
  :NAME_2_LAT "Avramovo" ] .

[ :CHANGE "2015-01-01" ;
  :LAU2_NAT_CODE "62617" ;
  :NAME_1 "Рибино" ;
  :NAME_2_LAT "Ribino" ] .

The initial mapping to RDF can be done via this pipeline:

Source pipeline

Consequently, you want to map the generated properties to properties from RDF vocabularies.

Solution

Use a SPARQL Update operation that defines the mapping via the VALUES clause. VALUES allows you to provide inline tabular data. In this case, you need two columns: one for the property in the source data and one for the property in the target RDF vocabulary:

PREFIX :        <http://example.com/>
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX skos:    <http://www.w3.org/2004/02/skos/core#>

DELETE {
  ?s ?source ?o .
}
INSERT {
  ?s ?target ?o .
}
WHERE {
  VALUES (?source         ?target) {
         (:LAU2_NAT_CODE  skos:notation)
         (:NAME_1         skos:prefLabel)
         (:NAME_2_LAT     skos:altLabel)
         (:CHANGE         dcterms:modified)
  }
  ?s ?source ?o .
}

Each row of the VALUES clause provides one mapping from a source property to a target property. In the example above, we map to properties from the Simple Knowledge Organization System, such as skos:prefLabel, and Dublin Core Terms, such as dcterms:modified. Using the SPARQL Update operation we delete the triples with the source property and insert the triples with the target property. If applied on the input data above, it produces the following result:

@prefix :        <http://example.com/> .
@prefix skos:    <http://www.w3.org/2004/02/skos/core#> .
@prefix dcterms: <http://purl.org/dc/terms/> .

[ dcterms:modified  "2015-01-01" ;
  skos:altLabel     "Ribino" ;
  skos:notation     "62617" ;
  skos:prefLabel    "Рибино" ] .

[ dcterms:modified  "2015-01-01" ;
  skos:altLabel     "Mokren" ;
  skos:notation     "00045" ;
  skos:prefLabel    "Мокрен" ] .

[ dcterms:modified  "2016-01-01" ;
  skos:altLabel     "Avramovo" ;
  skos:notation     "00062" ;
  skos:prefLabel    "Аврамово" ] .

This mapping to properties can be executed via this pipeline:

Target pipeline

Discussion

The declarative specification of the mappings allows you to extend the columns in the VALUES clause to provide additional instructions on how to map the data. For example, we can add a column ?typeOrLang that indicates what datatype or language tag should be applied to the objects of the target properties. If we do not want any processing to be applied to the objects, we can use the keyword UNDEF instead of an RDF term. Literals with data types may be created via strdt() function. Likewise, you can make literals with language tags via the strlang() function.

PREFIX :        <http://example.com/>
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX skos:    <http://www.w3.org/2004/02/skos/core#>
PREFIX xsd:      <http://www.w3.org/2001/XMLSchema#>

DELETE {
  ?s ?source ?_o .
}
INSERT {
  ?s ?target ?o .
}
WHERE {
  VALUES (?source         ?target          ?typeOrLang) {
         (:LAU2_NAT_CODE  skos:notation    UNDEF)
         (:NAME_1         skos:prefLabel   "bg")
         (:NAME_2_LAT     skos:altLabel    UNDEF)
         (:CHANGE         dcterms:modified xsd:date)
  }
  ?s ?source ?_o .
  BIND (if(bound(?typeOrLang),
           if(isLiteral(?typeOrLang),
              strlang(?_o, ?typeOrLang),
              strdt(?_o, ?typeOrLang)),
           ?_o) AS ?o)
}

This update operation produces data in the following shape:

@prefix :        <http://example.com/> .
@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
@prefix skos:    <http://www.w3.org/2004/02/skos/core#> .
@prefix dcterms: <http://purl.org/dc/terms/> .

[ dcterms:modified  "2015-01-01"^^xsd:date ;
  skos:altLabel     "Ribino" ;
  skos:notation     "62617" ;
  skos:prefLabel    "Рибино"@bg ] .

[ dcterms:modified  "2015-01-01"^^xsd:date ;
  skos:altLabel     "Mokren" ;
  skos:notation     "00045" ;
  skos:prefLabel    "Мокрен"@bg ] .

[ dcterms:modified  "2016-01-01"^^xsd:date ;
  skos:altLabel     "Avramovo" ;
  skos:notation     "00062" ;
  skos:prefLabel    "Аврамово"@bg ] .

You may also use the SPARQL CONSTRUCT component to map RDF properties. The CONSTRUCT query form is handy when you want to ignore the data unaffected by the mapping, since CONSTRUCT queries return only the data that is explicitly created via the CONSTRUCT clause. In such case, you would remove the DELETE clause and change the INSERT to CONSTRUCT in the update operation above to obtain a mapping CONSTRUCT query.

See also

Mapping RDF properties via VALUES is used in the tutorial on converting tabular data to RDF, where you can find more fleshed out examples of using this approach.