This chapter explains how to create and use RDF views over relational data in Oracle Spatial and Graph RDF Semantic Graph. Relational data is viewed as virtual RDF triples using one of the two forms of RDB2RDF mapping described in W3C documents on Direct Mapping and R2RML mapping:
R2RML: RDB to RDF Mapping Language, W3C Recommendation (http://www.w3.org/TR/r2rml/
)
A Direct Mapping of Relational Data to RDF, W3C Recommendation (http://www.w3.org/TR/rdb-direct-mapping/
)
This chapter contains the following major sections:
Section 10.3, "Example: Using an RDF View with Direct Mapping"
Section 10.4, "Combining Native RDF Data with Virtual RDB2RDF Data"
Using RDF views on relational data enables you to integrate data available from different sources. You can exploit the advantages of relational data without the need for physical storage of the RDF triples that correspond to the relational data. Before RDF views were included in RDF Semantic Graph in Oracle Database 12c Release 1 (12.1), you needed to write custom SQL queries or use non-standard mappings and physically store the generated RDF triples in an RDF model.
The simplest way to create a mapping of relational data to RDF data is by calling the SEM_APIS.CREATE_RDFVIEW_MODEL procedure to create an RDF view and supplying the list of tables or views whose content you would like to be viewed as RDF. This provides a direct mapping of those relational tables or views.
To get a more customized mapping, you can write an R2RML mapping document (in RDF using Turtle, for example) to specify the desired mapping, load the mapping document (after converting it to N-Triple format) into a staging table (for the table definition, see Section 1.7.1, "Bulk Loading Semantic Data Using a Staging Table"), and then call the SEM_APIS.CREATE_RDFVIEW_MODEL procedure to create an RDF view by supplying the name of the staging table.
Subprograms are included in the SEM_APIS package (documented in Chapter 11) for creating, dropping, and exporting (that is, materializing the content of) RDF views. An RDF view is created as an RDF model, but the RDF model physically contains only metadata. The actual data is still stored in the relational tables for which the RDF view has been created.
For the examples in the rest of this section, assume that the following relational tables exist in the invoker's schema:
CREATE TABLE dept ( deptno NUMBER CONSTRAINT pk_DeptTab_deptno PRIMARY KEY, dname VARCHAR2(30), loc VARCHAR2(30) ); CREATE TABLE emp ( empno NUMBER PRIMARY KEY, ename VARCHAR2(30), job VARCHAR2(20), deptno NUMBER REFERENCES dept (deptno) );
Note that if these tables are in a different schema (for example, SCOTT
) than the invoker's, when specifying the names of these tables, you need to use schema-qualified table names: "SCOTT"."DEPT"
and "SCOTT"."EMP"
.
Example 10-1 creates an RDF view model using direct mapping of two tables, EMP and DEPT, with a base prefix of http://empdb/
. The (virtual) RDF terms are generated according to A Direct Mapping of Relational Data to RDF, W3C Recommendation (http://www.w3.org/TR/rdb-direct-mapping/
).
Example 10-1 Creating an RDF View with Direct Mapping
BEGIN sem_apis.create_rdfview_model( model_name => 'empdb_model', tables => SYS.ODCIVarchar2List('EMP', 'DEPT'), prefix => 'http://empdb/', options => 'KEY_BASED_REF_PROPERTY=T' ); END; /
To see the properties that are generated, enter the following statement (which assumes that the objects are created in the schema of a user named TESTUSER):
SELECT DISTINCT p FROM TABLE(SEM_MATCH( '{?s ?p ?o}', SEM_Models('empdb_model'), NULL, NULL, NULL)); P -------------------------------------------------------------------------------- http://empdb/TESTUSER.EMP#DEPTNO http://empdb/TESTUSER.DEPT#LOC http://empdb/TESTUSER.EMP#JOB http://empdb/TESTUSER.DEPT#DEPTNO http://empdb/TESTUSER.EMP#ENAME http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://empdb/TESTUSER.DEPT#DNAME http://empdb/TESTUSER.EMP#EMPNO http://empdb/TESTUSER.EMP#ref-DEPTNO 9 rows selected.
Example 10-2 is essentially the same as Example 10-1, but it uses the CONFORMANCE=T
option (see the options
parameter description for SEM_APIS.CREATE_RDFVIEW_MODEL). Notice in the output that the schema name is not included in the list of properties; for example, the first output record in Example 10-2 is http://empdb/DEPT#LOC
, whereas its counterpart generated by Example 10-1 is http://empdb/TESTUSER.DEPT#LOC
.
Example 10-2 Using CONFORMANCE=T
BEGIN
sem_apis.create_rdfview_model(
model_name => 'empdb_model',
tables => SYS.ODCIVarchar2List('EMP', 'DEPT'),
prefix => 'http://empdb/',
options => 'CONFORMANCE=T'
);
END;
/
SELECT DISTINCT p
FROM TABLE(SEM_MATCH(
'{?s ?p ?o}',
SEM_Models('empdb_model'),
NULL,
NULL,
NULL));
P
--------------------------------------------------------------------------------
http://empdb/DEPT#LOC
http://empdb/EMP#ref-DEPTNO
http://empdb/EMP#ENAME
http://empdb/DEPT#DEPTNO
http://empdb/EMP#JOB
http://empdb/EMP#EMPNO
http://www.w3.org/1999/02/22-rdf-syntax-ns#type
http://empdb/DEPT#DNAME
http://empdb/EMP#DEPTNO
9 rows selected.
If you wanted to create an RDF view using the two tables EMP and DEPT, but with your own customizations, you could create an R2RML mapping document specified using Turtle, such as the following:
@prefix rr: <http://www.w3.org/ns/r2rml#>. @prefix xsd: <http://www.w3.org/2001/XMLSchema#>. @prefix ex: <http://example.com/ns#>. ex:TriplesMap_Dept rr:logicalTable [ rr:tableName "DEPT" ]; rr:subjectMap [ rr:template "http://data.example.com/department/{DEPTNO}"; rr:class ex:Department; ]; rr:predicateObjectMap [ rr:predicate ex:deptNum; rr:objectMap [ rr:column "DEPTNO" ; rr:datatype xsd:integer ]; ]; rr:predicateObjectMap [ rr:predicate ex:deptName; rr:objectMap [ rr:column "DNAME" ]; ]; rr:predicateObjectMap [ rr:predicate ex:deptLocation; rr:objectMap [ rr:column "LOC" ]; ]. ex:TriplesMap_Emp rr:logicalTable [ rr:tableName "EMP" ]; rr:subjectMap [ rr:template "http://data.example.com/employee/{EMPNO}"; rr:class ex:Employee; ]; rr:predicateObjectMap [ rr:predicate ex:empNum; rr:objectMap [ rr:column "EMPNO" ; rr:datatype xsd:integer ]; ]; rr:predicateObjectMap [ rr:predicate ex:empName; rr:objectMap [ rr:column "ENAME" ]; ]; rr:predicateObjectMap [ rr:predicate ex:jobType; rr:objectMap [ rr:column "JOB" ]; ]; rr:predicateObjectMap [ rr:predicate ex:worksForDeptNum; rr:objectMap [ rr:column "DEPTNO" ; rr:dataType xsd:integer ]; ]; rr:predicateObjectMap [ rr:predicate ex:worksForDept; rr:objectMap [ rr:parentTriplesMap ex:TriplesMap_Dept ; rr:joinCondition [ rr:child "DEPTNO"; rr:parent "DEPTNO" ]]].
Then, load your R2RML mapping (converted into N-Triples format) into a staging table, such as SCOTT.R2RTAB, and grant the SELECT
privilege for this table to MDSYS.
Next, call SEM_APIS.CREATE_RDFVIEW_MODEL, as in Example 10-3.
An RDF view can be dropped using the SEM_APIS.DROP_RDFVIEW_MODEL procedure, as shown in Example 10-4.
The content of an RDF view is virtual; that is, the RDF triples corresponding to the underlying relational data, as mapped by direct mapping or R2RML mapping, are not materialized and stored anywhere. You may, however, want to materialize and store these virtual RDF triples in an RDF model for your testing purposes. The SEM_APIS.EXPORT_RDFVIEW_MODEL subprogram lets you store the RDF triples of an RDF view in a staging table. The staging table can then be used for loading into an RDF model.
Example 10-5 materializes (in N-Triples format) the content of RDF view empdb_model
into the staging table SCOTT.RDFTAB.
Example 10-6 shows a simple workflow using an RDF view with direct mapping. In it, you:
Create two relational tables (EMP and DEPT).
Insert data into the tables.
Create an RDF view model (empdb_model
) using direct mapping of the two tables.
Query the RDF view using SPARQL in a SEM_MATCH-based SQL query.
Example 10-7 shows the output of the statements in Example 10-6.
Example 10-6 Using an RDF View with Direct Mapping
-- Use the following relational tables. CREATE TABLE dept ( deptno NUMBER CONSTRAINT pk_DeptTab_deptno PRIMARY KEY, dname VARCHAR2(30), loc VARCHAR2(30) ); CREATE TABLE emp ( empno NUMBER PRIMARY KEY, ename VARCHAR2(30), job VARCHAR2(20), deptno NUMBER REFERENCES dept (deptno) ); -- Insert some data. INSERT INTO dept (deptno, dname, loc) VALUES (1, 'Sales', 'Boston'); INSERT INTO dept (deptno, dname, loc) VALUES (2, 'Manufacturing', 'Chicago'); INSERT INTO dept (deptno, dname, loc) VALUES (3, 'Marketing', 'Boston'); INSERT INTO emp (empno, ename, job, deptno) VALUES (1, 'Alvarez', 'SalesRep', 1); INSERT INTO emp (empno, ename, job, deptno) VALUES (2, 'Baxter', 'Supervisor', 2); INSERT INTO emp (empno, ename, job, deptno) VALUES (3, 'Chen', 'Writer', 3); INSERT INTO emp (empno, ename, job, deptno) VALUES (4, 'Davis', 'Technician', 2); -- Create an RDF view model using direct mapping of two tables, EMP and DEPT, -- with a base prefix of http://empdb/. -- Specify KEY_BASED_REF_PROPERTY=T for the options parameter. BEGIN sem_apis.create_rdfview_model( model_name => 'empdb_model', tables => SYS.ODCIVarchar2List('EMP', 'DEPT'), prefix => 'http://empdb/', options => 'KEY_BASED_REF_PROPERTY=T' ); END; / -- Query an RDF view using SPARQL in a SEM_MATCH-based SQL query. -- The next statament is a query against an RDF view named empdb_model -- to find the employees who work for any department located in Boston. SELECT emp FROM TABLE(SEM_MATCH( '{?emp emp:ref-DEPTNO ?dept . ?dept dept:LOC "Boston"}', SEM_Models('empdb_model'), NULL, SEM_ALIASES( SEM_ALIAS('dept','http://empdb/TESTUSER.DEPT#'), SEM_ALIAS('emp','http://empdb/TESTUSER.EMP#') ), null)); -- The preceding query is functionally comparable to this: SELECT e.empno FROM emp e, dept d WHERE e.deptno = d.deptno AND d.loc = 'Boston';
Example 10-7 Output of Example 10-6, "Using an RDF View with Direct Mapping"
SQL> -- Use the following relational tables. SQL> SQL> CREATE TABLE dept ( 2 deptno NUMBER CONSTRAINT pk_DeptTab_deptno PRIMARY KEY, 3 dname VARCHAR2(30), 4 loc VARCHAR2(30) 5 ); Table created. SQL> SQL> CREATE TABLE emp ( 2 empno NUMBER PRIMARY KEY, 3 ename VARCHAR2(30), 4 job VARCHAR2(20), 5 deptno NUMBER REFERENCES dept (deptno) 6 ); Table created. SQL> SQL> -- Insert some data. SQL> SQL> INSERT INTO dept (deptno, dname, loc) 2 VALUES (1, 'Sales', 'Boston'); 1 row created. SQL> INSERT INTO dept (deptno, dname, loc) 2 VALUES (2, 'Manufacturing', 'Chicago'); 1 row created. SQL> INSERT INTO dept (deptno, dname, loc) 2 VALUES (3, 'Marketing', 'Boston'); 1 row created. SQL> SQL> INSERT INTO emp (empno, ename, job, deptno) 2 VALUES (1, 'Alvarez', 'SalesRep', 1); 1 row created. SQL> INSERT INTO emp (empno, ename, job, deptno) 2 VALUES (2, 'Baxter', 'Supervisor', 2); 1 row created. SQL> INSERT INTO emp (empno, ename, job, deptno) 2 VALUES (3, 'Chen', 'Writer', 3); 1 row created. SQL> INSERT INTO emp (empno, ename, job, deptno) 2 VALUES (4, 'Davis', 'Technician', 2); 1 row created. SQL> SQL> -- Create an RDF view model using direct mapping of two tables, EMP and DEPT, SQL> -- with a base prefix of http://empdb/. SQL> -- Specify KEY_BASED_REF_PROPERTY=T for the options parameter. SQL> SQL> BEGIN 2 sem_apis.create_rdfview_model( 3 model_name => 'empdb_model', 4 tables => SYS.ODCIVarchar2List('EMP', 'DEPT'), 5 prefix => 'http://empdb/', 6 options => 'KEY_BASED_REF_PROPERTY=T' 7 ); 8 END; 9 / PL/SQL procedure successfully completed. SQL> SQL> -- Query an RDF view using SPARQL in a SEM_MATCH-based SQL query. SQL> -- The next statament is a query against an RDF view named empdb_model SQL> -- to find the employees who work for any department located in Boston. SQL> SQL> SELECT emp 2 FROM TABLE(SEM_MATCH( 3 '{?emp emp:ref-DEPTNO ?dept . ?dept dept:LOC "Boston"}', 4 SEM_Models('empdb_model'), 5 NULL, 6 SEM_ALIASES( 7 SEM_ALIAS('dept','http://empdb/TESTUSER.DEPT#'), 8 SEM_ALIAS('emp','http://empdb/TESTUSER.EMP#') 9 ), 10 null)); EMP -------------------------------------------------------------------------------- http://empdb/TESTUSER.EMP/EMPNO=1 http://empdb/TESTUSER.EMP/EMPNO=3 SQL> SQL> -- The preceding query is functionally comparable to this: SQL> SELECT e.empno FROM emp e, dept d WHERE e.deptno = d.deptno AND d.loc = 'Boston'; EMPNO ---------- 1 3
You can combine native triple data with virtual RDB2RDF triple data in a single SEM_MATCH query by means of the SERVICE keyword (explained in Section 1.6.8, "Graph Patterns: Support for SPARQL 1.1 Federated Query"). The SERVICE keyword is overloaded through the use of special SERVICE URLs that signify local (virtual) RDF data. The following prefixes are used to denote special SERVICE URLs:
Native models - oram
: <http://xmlns.oracle.com/models/>
Native virtual models - oravm
: <http://xmlns.oracle.com/virtual_models/>
RDB2RDF models - orardbm
: <http://xmlns.oracle.com/rdb_models/>
Example 10-8 queries multiple data sets. In this query, the first triple pattern { ?x rdf:type :Person }
will go against native model m1
as usual, but { ?x :name ?name }
will go against the local native model m2
, and { ?x :email ?email }
will go against the local RDB2RDF model rdfview1
.
Example 10-8 Querying Multiple Data Sets
select * from table (sem_match( 'SELECT ?x ?name ?email WHERE { ?x rdf:type :Person . OPTIONAL { SERVICE oram:m2 { ?x :name ?name } } OPTIONAL { SERVICE orardbm:rdfview1 { ?x :email ?email } } }' sem_models('m1'), null, null, null, null, ' '));
Overloaded SERVICE use is only allowed with a single model specified in the models
argument of SEM_MATCH. Overloaded SERVICE queries do not allow multiple models or a rulebase as input. A virtual model that contains multiple models and/or entailments should be used instead for such combinations. In addition, the index_status
argument for SEM_MATCH will only check the entailment contained in the virtual model passed as input in the models parameter. This means the status of entailments that are referenced in overloaded SERVICE calls will not be checked.
Example 10-9 queries two data sets: the empdb_model
from Example 10-6, "Using an RDF View with Direct Mapping"and a native model named people
.
Example 10-9 Querying Virtual RDB2RDF Data and Native RDF Data
-- Create native model people -- create table atab (gval varchar2(4000), tri sdo_rdf_triple_s); execute sem_apis.create_sem_model('people','atab','tri'); create table stab(RDF$STC_GRAPH varchar2(4000), RDF$STC_sub varchar2(4000), RDF$STC_pred varchar2(4000), RDF$STC_obj varchar2(4000)); grant select on stab to mdsys; grant insert on atab to mdsys; insert into stab values (null, '<http://empdb/TESTUSER.EMP/EMPNO=1>', '<http://people.org/age>', '"35"^^<http://www.w3.org/2001/XMLSchema#int>'); insert into stab values (null, '<http://empdb/TESTUSER.EMP/EMPNO=2>', '<http://people.org/age>', '"39"^^<http://www.w3.org/2001/XMLSchema#int>'); insert into stab values (null, '<http://empdb/TESTUSER.EMP/EMPNO=3>', '<http://people.org/age>', '"30"^^<http://www.w3.org/2001/XMLSchema#int>'); insert into stab values (null, '<http://empdb/TESTUSER.EMP/EMPNO=4>', '<http://people.org/age>', '"42"^^<http://www.w3.org/2001/XMLSchema#int>'); commit; exec sem_apis.bulk_load_from_staging_table('people','testuser','stab'); -- Querying multiple datasets -- SELECT emp, age FROM TABLE(SEM_MATCH( 'SELECT ?emp ?age WHERE{ ?emp peop:age ?age SERVICE orardbm:empdb_model { ?emp emp:ref-DEPTNO ?dept . ?dept dept:LOC "Boston" } }', SEM_Models('people'), NULL, SEM_ALIASES( SEM_ALIAS('dept','http://empdb/TESTUSER.DEPT#'), SEM_ALIAS('emp','http://empdb/TESTUSER.EMP#'), SEM_ALIAS('peop','http://people.org/') ), NULL));