# University tutorial
This tutorial is an adaptation of the classical Ontop university tutorial (opens new window) to Ontopic Studio.
# Preparation
# Install the tutorial database
# Windows Desktop
The tutorial database is provided in the folder %AppData%/OntopicStudio/sample-db
with the name university
.
# Docker-compose
The tutorial database is normally installed as part of the recommended procedure for Docker-Compose (see the dedicated section). It should be available with destination-tutorial-db
as hostname.
If it is not the case:
- Go to the
studio
git repository - Run the command
docker compose -f docker-compose.yml -f tutorial/docker-compose-tutorial.yml pull && docker compose -f docker-compose.yml -f tutorial/docker-compose-tutorial.yml up`
# Create a project
- Connect to Ontopic Studio (by default http://localhost:8081 (opens new window)) and login with your user account
- Create a project
University_Tutorial
and add an optional description. Select typeSample DB
and save - Open the project
# Provide the database credentials
Go to the
Settings
page# Windows Desktop
- You can see the prefilled credentials for connecting to the database
- Database:
H2
- Driver:
org.h2.Driver
- Database name:
postgres
- Username:
sa
- Connection URL:
jdbc:h2:tcp://localhost:9092/C:\Users\<username>\AppData\Roaming\OntopicStudio\sample-db\destination-db;ACCESS_MODE_DATA=r
- Database:
- Change the end of the connection URL from
destination-tutorial-db
touniversity
:- Connection URL:
jdbc:h2:tcp://localhost:9092/C:\Users\<username>\AppData\Roaming\OntopicStudio\sample-db\university;ACCESS_MODE_DATA=r
- Connection URL:
# Docker-compose
- You can see the prefilled credentials for connecting to the database
- Database:
PostgreSQL
- Host:
destination-tutorial-db
- Database name:
postgres
- Username:
postgres
- Password:
postgres2
- Database:
- Change the database name:
- Database name:
university
- Database name:
- You can see the prefilled credentials for connecting to the database
- Save the credentials and go to the
Lenses
page. You see there, on the left, the list of database tables and views.
# Dataset
# University 1
The dataset is composed of the following tables:
uni1.student
The table uni1.student
contains the local ID (s_id), first and last name of each student.
s_id | first_name | last_name |
---|---|---|
1 | Mary | Smith |
2 | John | Doe |
The column s_id
is the primary key.
uni1.academic
Similarly, the table uni1.academic
contains the local ID (a_id), first and last name of each academic staff member, but also information about their position.
a_id | first_name | last_name | position |
---|---|---|---|
1 | Anna | Chambers | 1 |
2 | Edward | May | 9 |
3 | Rachel | Ward | 8 |
The column position is populated with magic numbers:
- 1 -> Full Professor
- 2 -> Associate Professor
- 3 -> Assistant Professor
- 8 -> External Teacher
- 9 -> PostDoc
The column a_id
is the primary key.
uni1.course
The table uni1.course
contains the local ID (c_id) and title of the courses.
c_id | title |
---|---|
1234 | Linear Algebra |
1235 | Analysis |
The column c_id
is the primary key.
uni1.teaching
The table uni1.teaching
contains the n-n relationship between courses and teachers.
c_id | a_id |
---|---|
1234 | 1 |
1234 | 2 |
There is no primary key, but two foreign keys to the tables uni1.course
and uni1.academic
.
uni1.course-registration
The table uni1.course-registration
contains the n-n relationship between courses and students.
c_id | s_id |
---|---|
1234 | 1 |
1234 | 2 |
There is no primary key, but two foreign keys to the tables uni1.course
and uni1.student
.
# University 2
The dataset is composed of the following tables:
uni2.person
The table uni2.person
describes the students and the academic staff of the university.
pid | fname | lname | status |
---|---|---|---|
1 | Zak | Lane | 8 |
2 | Mattie | Moses | 1 |
3 | Céline | Mendez | 2 |
The column status is populated with magic numbers, which differ from the ones of the table uni1.academic
:
- 1 -> Undergraduate Student
- 2 -> Graduate Student
- 3 -> PostDoc
- 7 -> Full Professor
- 8 -> Associate Professor
- 9 -> Assistant Professor
As you can see, undergraduate and graduate students are now distinguished.
The column pid
is the primary key.
uni2.course
The table uni2.course
contains information about courses, their topic, their lecturer and their lab teacher.
cid | lecturer | lab_teacher | topic |
---|---|---|---|
1 | 1 | 3 | Information security |
2 | 8 | 5 | Software factory |
3 | 7 | 8 | Software process management |
The column cid
is the primary key. Note that in this data source, there are at most two teachers per course. Lecturers and lab teachers are now distinguished.
uni2.registration
The table uni2.registration
contains the n-n relationship between courses and attendees.
pid | cid |
---|---|
2 | 1 |
10 | 4 |
There is no primary key, but two foreign keys to the tables uni2.course
and uni2.person
.
# Modelling
In this tutorial, the ontology file is provided. We will focus on the mapping and the lenses. Our goal is to reproduce the following diagram.
# Ontology
- Download this OWL ontology file
- Go to the
Ontology
page - Import the downloaded file through
Import ontology
- In the tab Classes you can visualize the class hierarchy
- In the tab Properties you can visualize the properties hierarchy. In particular the Data properties are firstName, lastName and title, and the Object properties are teaches (wth two sub-properties givesLecture and givesLab), isTaughtBy, isSupervisedBy, isGivenAt and attends.
# First data source: university 1
- Go to the
Lenses
page - Among the
Source Tables
(on the left), select the schemauni1
to filter the tables - Select the table
uni1.student
and click onGenerate mirror lens
- Click on
lenses.uni1.student
on the right to open the newly created lens - Click on
Constraints
and observe that unique constraints are defined on thes_id
andssn
column, while no foreign key is given
# Mapping uni1.student: first class mapping entry
- Go to the
Mapping
page and click on the lenslenses.uni1.student
- Map all the rows to a class by clicking on the button
C+
for creating a mapping entry - First of all, we need to specify the subject as a template. Make sure the option
Template
is selected. Open the template menu and click onCreate new template
. Ontopic Studio automatically proposes the templatedata:uni1-student/{s_id}
out of the lens name and the unique constraint on the columns_id
. Accept it by clicking onCreate
.
- Let's now assign a class. Make sure
Constant
is selected for the class. In the drop-down menu below select the classuni:Student
. Click and save and you are having your first mapping entry!
# Mapping uni1.student: first data property mapping entry
- Let's now map the first name of each student. Let's create a property mapping entry for the same subject by clicking on the button
P+
on the right of the IRI templatedata:uni1-student/{s_id}
.
- You should see that subject section is already filled with the same values as before. If it is not the case, you probably clicked on the other
P+
button at the top of the page. In that case, go back and click on the correctP+
button. - Select the data property
foaf:firstName
in the property menu. - For the object, we want to directly map the value from the column
first_name
. SelectLiteral
and the typeColumn
and then thefirst_name
in the column drop-down menu. - Notice that the datatype field get automatically filled after selecting the column as
xsd:string
. This is because the column type istext
and, according to R2RML (opens new window) character string types are mapped to that RDF datatype. Click and save to have your first data property mapping entry!
- Repeat the same steps to also map the last name of each student.
# Mapping uni1.academic
Similarly to what we did for students, we create a class mapping entry for each academic staff member and data property mappings for their first and last name.
- Go to the
Lenses
page, selectuni1.academic
and generate a mirror lenslenses.uni1.academic
. We observe that there are two unique constraints on the columnsa_id
andssn
, while there is no foreign key. - Go to the
Mapping
page and select the lenslenses.uni1.academic
- Map all the rows to a class by clicking on the button
C+
for creating a mapping entry. Create a new class mapping entry by clicking on the buttonC+
. - As subject we create a new template: Ontopic Studio automatically proposes the template
data:uni1-academic/{a_id}
using the unique constraint on the columna_id
.
- Let's now assign a class. Make sure
Constant
is selected and in the drop-down menu select as class the data propertyuni:FacultyMember
. Save to create a class mapping for the academic staff.
- As for students, we map the first name of each academic member. Let's create a property mapping entry for the same subject by clicking on the button
P+
on the right of the IRI templatedata:uni1-academic/{a_id}
and select the propertyfoaf:firstName
in the drop-down menu. Finally select as object the columnfirst_name
and save. - Repeat the steps above also for the last name.
# Mapping uni1.course: first object property mapping
- Go to the
Lenses
page, selectuni1.course
and generate a mirror lenslenses.uni1.course
. We observe that there is a unique constraintc_id
. - Map all the rows to a class by clicking on the button
C+
for creating a mapping entry. - We need first to specify the subject as a template. Select the option
Template
and click onCreate new template
. Ontopic Studio automatically proposes the templatedata:uni1-course/{c_id}
out of the lens name and the unique constraint on the columnc_id
. Accept it by clicking onCreate
. - Let's now assign a class. Make sure
Constant
is selected for the class. In the drop-down menu below select the classuni:Course
. Click and save. - Map the course titles: create a property mapping entry for the same subject by clicking on the button
P+
on the right of the IRI templatedata:uni1-course/{c_id}
and select the propertyuni:title
from the menu. Finally select as object the columntitle
and save. - Now we create a new object property mapping for the same subject. With the usual template as subject, select the property
uni:isGivenAt
. For object, you can see that the pre-selected kind value is notLiteral
as before butIRI/BNode
. This is because the propertyuni:isGivenAt
is declared in the ontology as an object property, so it expects an IRI or a BNode as value. The typeTemplate
is also pre-selected because it is the most common mechanism for building an IRI as object. However, we select the typeConstant
and type the valuedata:uni1/university
, since all courses are given at the same university.
# Mapping uni1.teaching
- Go to the
Lenses
page, selectuni1.teaching
and generate a mirror lenslenses.uni1.teaching
. We notice that there are not unique constraint, but two foreign keysa_id
fromuni1.academic
andc_id
fromuni1.course
. - Go to the
Mapping
page and select the lenslenses.uni1.teaching
. - Let's create a mapping between the academic staff (
a_id
) and the taught course (c_id
) through the object propertyteaches
. Click on the buttonP+
. - Choose in the subject section the template
data:uni1-academic/{a_id}
. Then select the propertyuni:teaches
and in the object section the templatedata:uni1-course/{c_id}
. Save.
# Mapping uni1.registration
- Go to the
Lenses
page, selectuni1.course-registration
and generate a mirror lenslenses.uni1.course-registration
. We observe that there are two foreign keysc_id
fromuni1.course
ands_id
fromuni1.student
. Click on0 mapping entries
to be directed to theMapping
page. - Let's create a mapping between the students (
s_id
) and the attended course (c_id
) through the object propertyuni:attends
. Click on the buttonP+
. - As subject select the template
data:uni1-student/{s_id}
, the propertyuni:attends
and as object the templatedata:uni1-course/{c_id}
.
# Mapping uni1.academic: subclasses
- Go to the
Mapping
page and select the lenslenses.uni1.academic
. - We now want to map the subclasses of
uni:FacultyMember
depending on the values found in the columnposition
. Let's create a new mapping entry by clicking onC+
on the right of the templatedata:uni1-academic/{a_id}
. - For the class we don't want to assign the same value to each faculty member found in the lens, so we don't select the type
Constant
, but instead the typeDictionary
. - Select the column
position
. - Let's now fill the first line of the dictionary. On the left field, insert the number 1 as found in the
position
column of the lens. In the right field, select the classuni:FullProfessor
. Everytime the value 1 is found in the columnposition
, the faculty member will be declared instance of the subclassuni:FullProfessor
. - Click on
+
for adding a new line. Map this time the value 2 to the classuni:AssociateProfessor
. - Complete with the other pairs value-class (see description of
uni1.academic
) and save.
# Snapshot and mapping export
- Go on the
Dashboard
page and click onCreate snapshot
. Technically speaking, it will create a Git commit with all the important files of the project. - Open the
Repository
page to see the following snapshotted/committed files:lenses.json
: definition of the lenses in the Ontop format.mapping.ttl
: R2RML mapping file defined over the lenses.ontology.ttl
: RDFS/OWL2QL ontology serialized in Turtle.YOUR_PROJECT.ontopicprj
: internal representation of your project. Can be imported in Ontopic Studio to override the mapping, lenses and the ontology.
- Go back to the Ontopic Studio tab. Go the
Mapping
page. Click onExport mapping
and then onWithout lenses
to download the mapping in R2RML that directly refers to tables in the source instead of the lenses. This file is interoperable with any R2RML processor.
# SPARQL
Let's test our mapping by deploying Ontop directly in Ontopic Studio and issue our first SPARQL query.
- Go to the
Query
page. - Execute the following SPARQL query which outputs the last names of the full professors:
PREFIX uni: <http://uni.example.org/voc#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT DISTINCT ?prof ?lastName {
?prof a uni:FullProfessor ;
foaf:lastName ?lastName .
}
- Click on
View SQL
to see the generated SQL query.
# Inference
Ontop embeds some inference capabilities and is thus capable of answering a query like the following:
PREFIX uni: <http://uni.example.org/voc#>
SELECT DISTINCT ?teacher {
?teacher a uni:Teacher .
}
These inference capabilities can be, for a large part, understood as the ability to infer new mappings from the original mappings and the ontological axioms (e.g. Professor is a sub-class of Teacher).
# Second data source: university 2
We now add the following mapping assertions together with the previous ones.
# Mapping uni2.person
- Go to the
Lenses
page, select the schemauni2
and generate a mirror lens for the tableuni2.person
. Open the newly created lens. - We can see it has similar kind of information of
uni1.student
but with different column names. Let's keep the lens as it is for the moment and start mapping it. For that, click at bottom right on0 mapping entries
to reach the mapping editing page for this lens. - Create a class mapping entry with a new IRI template
data:uni2-person/{pid}
to map all the rows of the lens to the classfoaf:Person
and save. - Map the first and last name of all persons through the properties
foaf:firstName
andfoaf:lastName
, respectively. - Map the following values of the column
status
to create the subclasses of the classfoaf:Person
by clicking on theC+
next to the templatedata:uni2-person/{pid}
and selecting the typeDictionary
:- 1 to
uni:UndergraduateStudent
- 2 to
uni:GraduateStudent
- 3 to
uni:PostDoc
- 7 to
uni:FullProfessor
- 8 to
uni:AssociateProfessor
- 9 to
uni:AssistantProfessor
- 1 to
- Snapshot the changes.
- Go to the
Query
page and update the SPARQL endpoint. - Run the previous SPARQL and observe that it now returns more results. If you look at SQL query, you will see that it now contains a UNION.
# Mapping uni2.course
- Go to the
Lenses
page and generate a mirror lens for the tableuni2.course
. - Go to the
Mapping
page and select the lenslenses.uni2.course
. - Create a class mapping entry with a new IRI template
data:uni2-course/{cid}
to map all the rows of the lens to the classuni:Course
and save. - Map the course title to the property
uni:title
and select the columntopic
and save. - Create the property
uni:isGivenAt
for the same subject. As object choose the kindIRI/BNode
and the typeConstant
with valuedata:uni2/university
.
# Mapping uni2.course: lecturers and lab-teachers
- Go to the
Mapping
page and select the mirror lenslenses.uni2.course
. - Create a new property mapping entry by clicking on the button
P+
. - As subject select the template
data:uni2-person/{pid}
andlecturer
as binding. Note that thepid
in the template is not a column name but just a placeholder that in this case is associated with the columnlecturer
. - Click on the property
uni:givesLecture
. - As object keep the default kind
IRI/BNode
and select the templatedata:uni2-course/{cid}
.
- Repeat the last four steps by modifying only the binding of the template to
lab_teacher
and the property touni:givesLab
.
# Mapping uni2.registration
- Go to the
Mapping
page and select the mirror lenslenses.uni2.registration
. - Create a new property by clicking on the button
P+
. - As subject select the template
data:uni2-person/{pid}
, the propertyuni:attends
and as object the templatedata:uni2-course/{cid}
.
# SPARQL
We can now run the previous SPARQL queries and observe that the results combine entries from the two datasets.