www.openlinksw.com
docs.openlinksw.com

Book Home

Contents
Preface

RDF Data Access and Data Management

Data Representation
SPARQL
Extensions
RDF Graphs Security
Linked Data Views over RDBMS Data Source
Automated Generation of Linked Data Views over Relational Data Sources
Virtuoso R2RML Support
What is R2RML? Why use it? How do I use it with Virtuoso? Known Limitations Generating an R2RML Linked Data View from ISQL Virtuoso Conductor R2RML Import Wizard Generate Transient and/or Persistent Linked Data Views atop Remote Relational Data Sources Using Conductor
Examples of Linked Data Views
RDF Insert Methods in Virtuoso
RDFizer Middleware (Sponger)
Virtuoso Faceted Browser Installation and configuration
Virtuoso Faceted Web Service
Linked Data
Inference Rules & Reasoning
RDF Performance Tuning
RDF Data Access Providers (Drivers)

14.7. Virtuoso R2RML Support

14.7.1. What is R2RML?

R2RML is a language for expressing customized mappings from relational databases to RDF data sets. Such mappings provide the ability to view existing relational data in the RDF data model, expressed in a structure and target vocabulary of the mapping author's choice.

R2RML mappings are themselves RDF graphs written in Turtle syntax.


14.7.2. Why use it?

As a W3C working draft, R2RML is becoming the generic standard adopted by most vendors of tools mapping relational data to RDF, enabling the interoperability of R2RML scripts, whether created with such tools or by hand.


14.7.3. How do I use it with Virtuoso?

Virtuoso has its own previously-developed proprietary equivalent of R2RML called Linked Data Views, which uses Virtuoso's Meta Schema Mapping Language to map relational data to RDF.

R2RML support is achieved by the inclusion of a simple translator which basically translates R2RML syntax to Virtuoso's own Linked Data Views syntax, which can then be executed to create the Linked Data Views themselves.

14.7.3.1. Install R2RML VAD package

First you will need to ensure you have the R2RML VAD package (rdb2rdf_dav.vad) installed.


14.7.3.2. Test with simple test script (basic.sql)

Having installed the R2RML VAD package, to test R2RML functionality, the easiest way is by executing a basic.sql script via the command line isql tool:


CREATE TABLE "R2RML"."TEST"."PRODUCT"(
  id integer primary key,
  name VARCHAR(100)
);

INSERT SOFT "R2RML"."TEST"."PRODUCT" VALUES (1, 'Virtuoso');

SPARQL CLEAR GRAPH <http://temp/product>;
SPARQL CLEAR GRAPH <http://example.com/>;



DB.DBA.TTLP ('
@prefix rr: <http://www.w3.org/ns/r2rml#> .
@prefix exa: <http://example.com/ns#> .
@prefix product: <http://example.com/product#> .

<http://example.com/ns#TriplesMap1>
    a rr:TriplesMap;

    rr:logicalTable 
    [ 
      rr:tableSchema "R2RML"; 
      rr:tableOwner "TEST"; 
      rr:tableName  "PRODUCT" 
    ];

    rr:subjectMap 
    [ 
      rr:template "http://example.com/product/{id}";
      rr:class exa:product;
      rr:graph <http://example.com/>;
    ];

    rr:predicateObjectMap
    [
      rr:predicate product:id;
      rr:objectMap [ rr:column "id" ];
    ];

    rr:predicateObjectMap
    [
      rr:predicate product:name;
      rr:objectMap [ rr:column "name" ];
    ];
.
', 'http://temp/product', 'http://temp/product' )
;



--select DB.DBA.R2RML_TEST ('http://temp/product');

--DB.DBA.OVL_VALIDATE ('http://temp/product', 'http://www.w3.org/ns/r2rml#OVL');

-- Running the validation in order to find error in name of R2RML description graph
--DB.DBA.OVL_VALIDATE ('http://temp/product-nosuch', 'http://www.w3.org/ns/r2rml#OVL');

-- Running the validation in order to find error in name of R2RML metadata graph
--DB.DBA.OVL_VALIDATE ('http://temp/product', 'http://www.w3.org/ns/r2rml#OVL-nosuch');

--select DB.DBA.R2RML_EXECUTE ('http://temp/product');

exec ('sparql ' || DB.DBA.R2RML_MAKE_QM_FROM_G ('http://temp/product'));

--sparql select distinct ?g where { graph ?g { ?s a ?t }};

SPARQL
SELECT * FROM <http://example.com/>
WHERE {?s ?p ?o .};	
  1. First, copy basic.sql into:
    <VIRTUOSO_INSTALL>/bin/
    
  2. Next, open Unix session or Windows Command Prompt and execute:
    
    cd <OPENLINK_INSTALL>/bin
    
    ./isql     (Unix)
    isql.exe   (Windows)
    
    OpenLink Interactive SQL (Virtuoso), version 0.9849b.
    Type HELP; for help and EXIT; to exit.
    SQL>	
    
  3. Then, within isql execute:
    SQL> load basic.sql;	
    
  4. Execution should finish with a simple SPARQL query that will return Linked Data for the test table created at the start of the script:
    s                               p                                                  o
    VARCHAR                         VARCHAR                                            VARCHAR
    ________________________________________________________________________________________________________________
    
    http://example.com/product/1    http://example.com/product#id                      1
    http://example.com/product/1    http://example.com/product#name                    Virtuoso
    http://example.com/product/1    http://www.w3.org/1999/02/22-rdf-syntax-ns#type    http://example.com/ns#product
    3 Rows. -- 0 msec.	
    

Note: Subsequent executions of basic.sql will return an error since the test table will already exist. However, the remainder of the script will execute fine.


14.7.3.3. Examining basic.sql

  1. We start by creating and populating the test table:
    CREATE TABLE "R2RML"."TEST"."PRODUCT"
      (
        id   INTEGER PRIMARY KEY ,
        name VARCHAR(100)
      );
    
    INSERT SOFT "R2RML"."TEST"."PRODUCT" 
      VALUES 
        (
          1, 'Virtuoso'
        );	
    
  2. Next we clear any graphs (temporary or permanent) that are to be used during this process:
    SPARQL CLEAR GRAPH <http://temp/product> ;
    SPARQL CLEAR GRAPH <http://example.com/> ;	
    
  3. Next we use the DB.DBA.TTLP() procedure to insert the R2RML into a temporary graph, <http://temp/product>:
            
    DB.DBA.TTLP 
      (
        ' @prefix       rr:  <http://www.w3.org/ns/r2rml#>    .
          @prefix      exa:  <http://example.com/ns#>         .
          @prefix  product:  <http://example.com/product#>    .
    
          <http://example.com/ns#TriplesMap1>
              a                              rr:TriplesMap  ;
    
              rr:logicalTable 
                [ 
                  rr:tableSchema  "R2RML"    ; 
                  rr:tableOwner   "TEST"     ; 
                  rr:tableName    "PRODUCT" 
                ];
    
              rr:subjectMap 
                [ 
                  rr:template  "http://example.com/product/{id}" ;
                  rr:class     exa:product                       ;
                  rr:graph     <http://example.com/>
                ];
    
              rr:predicateObjectMap
                [
                  rr:predicate  product:id  ;
                  rr:objectMap  
                    [ 
                      rr:column  "id" 
                    ];
                ];
    
              rr:predicateObjectMap
                [
                  rr:predicate  product:name  ;
                  rr:objectMap 
                    [ 
                      rr:column "name" 
                    ];
                ];
             .
        ', 
        'http://temp/product',
        'http://temp/product' 
      );        	
    
  4. Next, there is a series of commented out lines that can be used for sanity checking:
    --SELECT DB.DBA.R2RML_TEST ('http://temp/product');
    
    --DB.DBA.OVL_VALIDATE ('http://temp/product', 'http://www.w3.org/ns/r2rml#OVL');
    
    -- Running the validation in order to find error in name of R2RML description graph
    --DB.DBA.OVL_VALIDATE ('http://temp/product-nosuch', 'http://www.w3.org/ns/r2rml#OVL');
    
    -- Running the validation in order to find error in name of R2RML metadata graph
    --DB.DBA.OVL_VALIDATE ('http://temp/product', 'http://www.w3.org/ns/r2rml#OVL-nosuch');
    
    --SELECT DB.DBA.R2RML_EXECUTE ('http://temp/product');	
    
  5. Next, DB.DBA.R2RML_MAKE_QM_FROM_G() is used to perform the conversion from R2RML into Virtuoso's own Linked Data Views script. The output is then prepended with the keyword 'SPARQL' and a space, and executed using exec() :
    EXEC ('SPARQL ' || DB.DBA.R2RML_MAKE_QM_FROM_G ('http://temp/product'));	
    

    Note: The final triples are placed in the graph defined in the R2RML script itself (<http://example.com/>)

    Alternatively, the destination graph can be specified as an optional second parameter of DB.DBA.R2RML_MAKE_QM_FROM_G():

    DB.DBA.R2RML_MAKE_QM_FROM_G
      (
        (
          IN g VARCHAR 
       [, IN target_graph VARCHAR := NULL]
        )
      )
    	
    
  6. Finally, a simple SPARQL statement is executed to prove data is returned:
    SPARQL
      SELECT * 
      FROM <http://example.com/>
      WHERE {?s ?p ?o .};	
    


14.7.4. Known Limitations

rr:sqlQuery is not currently supported, due to limitations in the optimizer used for Virtuoso's native implementation of Linked Data Views.


14.7.5. Generating an R2RML Linked Data View from ISQL

Using Virtuoso you can programmatically generate Linked Data Views atop Relational Data Sources, using R2RML via the built-in function: R2RML_GENERATE_LINKED_VIEW function. In order to use this function, you need to have the rdb2rdf_dav.vad package installed.

R2RML_GENERATE_LINKED_VIEW 
  (
    in source varchar, 
    in destination_graph varchar, 
    in graph_type int default 0, 
    in clear_source_graph int default 1
  )	

Here is detailed description of the funcion's parameter:

Note: The R2RML mapping script may have a triples like:

[] rr:graph <graph_name> 	

and in this case they take precedence and virtual graph would be defined as in the R2RML. If so, then if destination graph is specified as physical, all virtual graphs found in the R2RML would go in the destination_graph.

14.7.5.1. Usage Example

  1. Ensure the R2RML VAD package rdb2rdf_dav.vad is installed.
  2. To clear out existing mappings execute:
    SQL> SELECT RDF_VIEW_DROP_STMT_BY_GRAPH ('http://example.com');
    VARCHAR
    _______________________________________________________________________________
    
    SPARQL drop silent quad map <http://demo.openlinksw.com/r2rmldemo.n3> .;
    
    
    1 Rows. -- 16 msec.
    
    SQL> SPARQL DROP SILENT QUAD MAP <http://demo.openlinksw.com/r2rmldemo.n3> ;
    
    STATE    MESSAGE
    VARCHAR  VARCHAR
    _______________________________________________________________________________
    
    00000    Quad map <http://demo.openlinksw.com/r2rmldemo.n3> is no longer used in storage <http://www.openlinksw.com/schemas/virtrdf#DefaultQuadStorage
    >
    00000    Quad map <http://demo.openlinksw.com/r2rmldemo.n3> is deleted
    00000    Transaction committed, SPARQL compiler re-configured
    00000    2 RDF metadata manipulation operations done
    
    4 Rows. -- 406 msec.
    
    
    SQL> SPARQL CLEAR <http://demo.openlinksw.com/r2rmldemo.n3>;
    
    callret-0
    VARCHAR
    _______________________________________________________________________________
    
    Clear <http://demo.openlinksw.com/r2rmldemo.n3> -- done
    
    1 Rows. -- 15 msec.
    
    
    SQL> DROP TABLE "R2RML"."TEST"."PRODUCT" ;
    
    Done. -- 0 msec.
    
    SQL> CREATE TABLE "R2RML"."TEST"."PRODUCT"
    (
      "id" INTEGER,
      "name" VARCHAR(100),
      PRIMARY KEY ("id")
    );
    Done. -- 16 msec.	
    
  3. Insert sample data into a Table by executing:
    SQL> INSERT SOFT "R2RML"."TEST"."PRODUCT" VALUES(1, 'Virtuoso');
    Done. -- 0 msec.	
    
  4. Locate or create your R2RML mapping document, for example: .n3 file with the following content:
    @prefix rr: <http://www.w3.org/ns/r2rml#> .
    @prefix exa: <http://example.com/ns#> .
    @prefix product: <http://example.com/product#> .
    
    <http://example.com/ns#TriplesMap1>
        a rr:TriplesMap;
    
        rr:logicalTable 
        [ 
          rr:tableSchema "R2RML"; 
          rr:tableOwner "TEST"; 
          rr:tableName  "PRODUCT" 
        ];
    
        rr:subjectMap 
        [ 
          rr:template "http://example.com/product/{id}";
          rr:class exa:product;
        ];
    
        rr:predicateObjectMap
        [
          rr:predicate product:id;
          rr:objectMap [ rr:column "id" ];
        ];
    
        rr:predicateObjectMap
        [
          rr:predicate product:name;
          rr:objectMap [ rr:column "name" ];
        ];
    .	
    
  5. Generate a Linked Data View from the R2RML document that applies to the sample data (created earlier) by executing the statement:
    SQL> DB.DBA.R2RML_GENERATE_LINKED_VIEW('http://demo.openlinksw.com/r2rmldemo.n3', 'http://example.com', 0);
    STATE    MESSAGE
    VARCHAR  VARCHAR
    _______________________________________________________________________________
    
    00000    IRI class <r2rml:virt02-8513ca7e0ce41d2e38f0c750fd552139> has been defined (inherited from rdfdf:sql-integer-uri-nullable)
    00000    Literal class <r2rml:virt02-daca9ceddea29d53dbbdb6bd0f3dee68> has been defined (inherited from rdfdf:sql-integer-literal-nullable)
    00000    Quad storage <http://www.openlinksw.com/schemas/virtrdf#DefaultQuadStorage> is flagged as being edited
    00000    Quad map <http://demo.openlinksw.com/r2rmldemo.n3> has been created and added to the <http://www.openlinksw.com/schemas/virtrdf#DefaultQuadSt
    orage>
    00000    Quad map <sys:qm-1be5dbd931459cf9e2df2338428f418d> has been created and added to the <http://www.openlinksw.com/schemas/virtrdf#DefaultQuadSt
    orage>
    00000    Quad map <sys:qm-c5f81d7126efa3e7a93f7e903fd5fa93> has been created and added to the <http://www.openlinksw.com/schemas/virtrdf#DefaultQuadSt
    orage>
    00000    Quad map <sys:qm-25c4599111b9f07fbd8fc60ce0b42eaf> has been created and added to the <http://www.openlinksw.com/schemas/virtrdf#DefaultQuadSt
    orage>
    00000    Quad storage <http://www.openlinksw.com/schemas/virtrdf#DefaultQuadStorage> is unflagged and can be edited by other transactions
    00000    Transaction committed, SPARQL compiler re-configured
    00000    9 RDF metadata manipulation operations done
    
    10 Rows. -- 1109 msec.
    SQL>	
    
  6. Verify successful creation of the Linked Data View by executing the following SPARQL query via iSQL or Conductor interface:
    SQL> SPARQL 
    SELECT * 
    FROM <http://example.com> 
    WHERE {?s ?p ?o} ;
    
    s                                p                                                   o
    VARCHAR                          VARCHAR                                             VARCHAR
    _______________________________________________________________________________
    
    http://example.com/product/1     http://example.com/product#id                       1
    http://example.com/product/1     http://example.com/product#name                     Virtuoso
    http://example.com/product/1     http://www.w3.org/1999/02/22-rdf-syntax-ns#type     http://example.com/ns#product
    
    3 Rows. -- 15 msec.	
    


14.7.6. Virtuoso Conductor R2RML Import Wizard

The Virtuoso Conductor can be used for importing existing R2RML scripts into Virtuoso and generate the necessary RDF Linked Data Views for Virtuoso hosting and deployment.

14.7.6.1. Usage Example

  1. Ensure the R2RML rdb2rdf_dav.vad and latest Conductor conductor_dav.vad VAD packages are installed.
  2. Create a test table with sample data:
    SQL> CREATE TABLE "R2RML"."TEST"."PRODUCT"
    (
      "id" INTEGER,
      "name" VARCHAR(100),
      PRIMARY KEY ("id")
    );
    Done. -- 16 msec.
    SQL> INSERT SOFT "R2RML"."TEST"."PRODUCT" VALUES(1, 'Virtuoso');
    Done. -- 0 msec.
    SQL> INSERT SOFT "R2RML"."TEST"."PRODUCT" VALUES(2, 'UDA');
    Done. -- 0 msec.
    SQL>	
    
  3. Grant select privileges on the "R2RML"."TEST"."PRODUCT" table to the SPARQL user to enable execution via SPARQL endpoint:
    SQL> GRANT SELECT ON R2RML.TEST.PRODUCT  TO "SPARQL", "SPARQL_UPDATE"
    Done. -- 1 msec.	
    
  4. Create the following R2RML mapping script for the "R2RML"."TEST"."PRODUCT" table:
    $ cat demo.n3
    @prefix rr: <http://www.w3.org/ns/r2rml#> .
    @prefix exa: <http://example.com/ns#> .
    @prefix product: <http://example.com/product#> .
    
    <http://example.com/ns#TriplesMap1>
        a rr:TriplesMap;
    
        rr:logicalTable 
        [ 
          rr:tableSchema "R2RML"; 
          rr:tableOwner "TEST"; 
          rr:tableName  "PRODUCT" 
        ];
    
        rr:subjectMap 
        [ 
          rr:template "http://example.com/product/{id}";
          rr:class exa:product;
        ];
    
        rr:predicateObjectMap
        [
          rr:predicate product:id;
          rr:objectMap [ rr:column "id" ];
        ];
    
        rr:predicateObjectMap
        [
          rr:predicate product:name;
          rr:objectMap [ rr:column "name" ];
        ];
    .
    $	
    
  5. Got to the Linked Data -> R2RML tab of the Virtuoso Conductor:
    Conductor R2RML Import Wizard
    Figure: 14.7.6.1.1. Conductor R2RML Import Wizard
  6. Select the Choose File button and select the R2RML file to load:
    Conductor R2RML Import Wizard
    Figure: 14.7.6.1.1. Conductor R2RML Import Wizard
  7. Select the Validate button to verify the R2RML mapping script:
    Conductor R2RML Import Wizard
    Figure: 14.7.6.1.1. Conductor R2RML Import Wizard
  8. Select the Generate button to generate the RDF Linked Data Views mappings for the R2RML mapping script:
    Conductor R2RML Import Wizard
    Figure: 14.7.6.1.1. Conductor R2RML Import Wizard
  9. Select the Execute button to create the RDF Linked Data Views mapping the the Quad Store:
    Conductor R2RML Import Wizard
    Figure: 14.7.6.1.1. Conductor R2RML Import Wizard
  10. The RDF Linked Data View creation is complete and status is displayed:
    Conductor R2RML Import Wizard
    Figure: 14.7.6.1.1. Conductor R2RML Import Wizard
  11. The Default Graph Name (transient) specified http://demo.openlinksw.com/r2rml# can now be used to run a SPARQL query against the created Linked Data View. If the Generate RDB2RDF triggers and Enable Data Syncs with Physical Quad Store check boxes are selected the Physical Graph Name (persistent) specified urn:demo.openlinksw.com/r2rml# can be used to run a SPARQL query against the materialized triples in the Quad Store.
    Conductor R2RML Import Wizard
    Figure: 14.7.6.1.1. Conductor R2RML Import Wizard
  12. The results set for the Linked Data View graph are displayed:
    Conductor R2RML Import Wizard
    Figure: 14.7.6.1.1. Conductor R2RML Import Wizard


14.7.7. Generate Transient and/or Persistent Linked Data Views atop Remote Relational Data Sources Using Conductor

This section describes how you can generate R2RML Scripts from Linked Data Views, using the Virtuoso Conductor ODBC or JDBC accessible.

  1. Ensure you have installed Conductor conductor_dav.vad VAD package with version 1.32.38 or higher.
  2. Go to http://<cname>[:<port>]/conductor.
  3. Enter dba credentials.
  4. Go to Linked Data -> Views:
    Generating Transient and/or Persistent Linked Data Views
    Figure: 14.7.7.1. Generating Transient and/or Persistent Linked Data Views
  5. Select Qualifier Demo:
    Generating Transient and/or Persistent Linked Data Views
    Figure: 14.7.7.1. Generating Transient and/or Persistent Linked Data Views
  6. Select table(s) by hatching the check-box to the left of the table name; for example, select the following tables from the Northwind DB: Categories, Customers, Employees, Order_Details, Orders, Products .
    Generating Transient and/or Persistent Linked Data Views
    Figure: 14.7.7.1. Generating Transient and/or Persistent Linked Data Views
  7. Click Generate via Wizard:
    Generating Transient and/or Persistent Linked Data Views
    Figure: 14.7.7.1. Generating Transient and/or Persistent Linked Data Views
  8. Click Prepare to Execute.
  9. The R2RML script for the selected table(s) will be generated and displayed in the R2RML Graph text-area:
    Generating Transient and/or Persistent Linked Data Views
    Figure: 14.7.7.1. Generating Transient and/or Persistent Linked Data Views
    Generating Transient and/or Persistent Linked Data Views
    Figure: 14.7.7.2. Generating Transient and/or Persistent Linked Data Views
    Generating Transient and/or Persistent Linked Data Views
    Figure: 14.7.7.3. Generating Transient and/or Persistent Linked Data Views
  10. As result the following R2RML script should be generated for the Northwind DB collection:
    @prefix rr: <http://www.w3.org/ns/r2rml#> .
    @prefix Demo: <http://demo.openlinksw.com/schemas/Demo/> .
    @prefix demo-stat: <http://demo.openlinksw.com/Demo/stat#> .
    @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
    @prefix void: <http://rdfs.org/ns/void#> .
    @prefix scovo: <http://purl.org/NET/scovo#> .
    @prefix aowl: <http://bblfish.net/work/atom-owl/2006-06-06/> .
    
    
    <#TriplesMapCategories> a rr:TriplesMap; rr:logicalTable [ rr:tableSchema "Demo" ; rr:tableOwner "demo" ; rr:tableName "Categories" ]; 
    rr:subjectMap [ rr:termtype "IRI"  ; rr:template "http://demo.openlinksw.com/Demo/categories/{CategoryID}"; rr:class Demo:Categories; ];
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:categoryid ] ; rr:objectMap [ rr:column "CategoryID" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:categoryname ] ; rr:objectMap [ rr:column "CategoryName" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:description ] ; rr:objectMap [ rr:column "Description" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:picture ] ; rr:objectMap [ rr:column "Picture" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:categories_of_products ] ; rr:objectMap [ rr:parentTriplesMap <#TriplesMapProducts>;  rr:joinCondition [ rr:child "CategoryID" ; rr:parent "CategoryID" ] ; ]; ] .
    
    <#TriplesMapCustomers> a rr:TriplesMap; rr:logicalTable [ rr:tableSchema "Demo" ; rr:tableOwner "demo" ; rr:tableName "Customers" ]; 
    rr:subjectMap [ rr:termtype "IRI"  ; rr:template "http://demo.openlinksw.com/Demo/customers/{CustomerID}"; rr:class Demo:Customers; ];
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:customerid ] ; rr:objectMap [ rr:column "CustomerID" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:companyname ] ; rr:objectMap [ rr:column "CompanyName" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:contactname ] ; rr:objectMap [ rr:column "ContactName" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:contacttitle ] ; rr:objectMap [ rr:column "ContactTitle" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:address ] ; rr:objectMap [ rr:column "Address" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:city ] ; rr:objectMap [ rr:column "City" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:region ] ; rr:objectMap [ rr:column "Region" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:postalcode ] ; rr:objectMap [ rr:column "PostalCode" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:country ] ; rr:objectMap [ rr:column "Country" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:countrycode ] ; rr:objectMap [ rr:column "CountryCode" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:phone ] ; rr:objectMap [ rr:column "Phone" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:fax ] ; rr:objectMap [ rr:column "Fax" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:customers_of_orders ] ; rr:objectMap [ rr:parentTriplesMap <#TriplesMapOrders>;  rr:joinCondition [ rr:child "CustomerID" ; rr:parent "CustomerID" ] ; ]; ] .
    
    <#TriplesMapEmployees> a rr:TriplesMap; rr:logicalTable [ rr:tableSchema "Demo" ; rr:tableOwner "demo" ; rr:tableName "Employees" ]; 
    rr:subjectMap [ rr:termtype "IRI"  ; rr:template "http://demo.openlinksw.com/Demo/employees/{EmployeeID}"; rr:class Demo:Employees; ];
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:employeeid ] ; rr:objectMap [ rr:column "EmployeeID" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:lastname ] ; rr:objectMap [ rr:column "LastName" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:firstname ] ; rr:objectMap [ rr:column "FirstName" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:title ] ; rr:objectMap [ rr:column "Title" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:titleofcourtesy ] ; rr:objectMap [ rr:column "TitleOfCourtesy" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:birthdate ] ; rr:objectMap [ rr:column "BirthDate" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:hiredate ] ; rr:objectMap [ rr:column "HireDate" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:address ] ; rr:objectMap [ rr:column "Address" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:city ] ; rr:objectMap [ rr:column "City" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:region ] ; rr:objectMap [ rr:column "Region" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:postalcode ] ; rr:objectMap [ rr:column "PostalCode" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:country ] ; rr:objectMap [ rr:column "Country" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:countrycode ] ; rr:objectMap [ rr:column "CountryCode" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:homephone ] ; rr:objectMap [ rr:column "HomePhone" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:extension ] ; rr:objectMap [ rr:column "Extension" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:photo ] ; rr:objectMap [ rr:column "Photo" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:notes ] ; rr:objectMap [ rr:column "Notes" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:reportsto ] ; rr:objectMap [ rr:column "ReportsTo" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:employees_of_orders ] ; rr:objectMap [ rr:parentTriplesMap <#TriplesMapOrders>;  rr:joinCondition [ rr:child "EmployeeID" ; rr:parent "EmployeeID" ] ; ]; ] .
    
    <#TriplesMapOrder_Details> a rr:TriplesMap; rr:logicalTable [ rr:tableSchema "Demo" ; rr:tableOwner "demo" ; rr:tableName "Order_Details" ]; 
    rr:subjectMap [ rr:termtype "IRI"  ; rr:template "http://demo.openlinksw.com/Demo/order_details/{OrderID}/{ProductID}"; rr:class Demo:Order_Details; ];
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:unitprice ] ; rr:objectMap [ rr:column "UnitPrice" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:quantity ] ; rr:objectMap [ rr:column "Quantity" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:discount ] ; rr:objectMap [ rr:column "Discount" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:order_details_has_orders ] ; rr:objectMap [ rr:termtype "IRI" ; rr:template "http://demo.openlinksw.com/Demo/orders/{OrderID}" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:order_details_has_products ] ; rr:objectMap [ rr:termtype "IRI" ; rr:template "http://demo.openlinksw.com/Demo/products/{ProductID}" ]; ] .
    
    <#TriplesMapOrders> a rr:TriplesMap; rr:logicalTable [ rr:tableSchema "Demo" ; rr:tableOwner "demo" ; rr:tableName "Orders" ]; 
    rr:subjectMap [ rr:termtype "IRI"  ; rr:template "http://demo.openlinksw.com/Demo/orders/{OrderID}"; rr:class Demo:Orders; ];
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:orderid ] ; rr:objectMap [ rr:column "OrderID" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:orderdate ] ; rr:objectMap [ rr:column "OrderDate" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:requireddate ] ; rr:objectMap [ rr:column "RequiredDate" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:shippeddate ] ; rr:objectMap [ rr:column "ShippedDate" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:freight ] ; rr:objectMap [ rr:column "Freight" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:shipname ] ; rr:objectMap [ rr:column "ShipName" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:shipaddress ] ; rr:objectMap [ rr:column "ShipAddress" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:shipcity ] ; rr:objectMap [ rr:column "ShipCity" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:shipregion ] ; rr:objectMap [ rr:column "ShipRegion" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:shippostalcode ] ; rr:objectMap [ rr:column "ShipPostalCode" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:shipcountry ] ; rr:objectMap [ rr:column "ShipCountry" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:shipcountrycode ] ; rr:objectMap [ rr:column "ShipCountryCode" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:rowguid ] ; rr:objectMap [ rr:column "ROWGUID" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:orders_has_customers ] ; rr:objectMap [ rr:termtype "IRI" ; rr:template "http://demo.openlinksw.com/Demo/customers/{CustomerID}" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:orders_has_employees ] ; rr:objectMap [ rr:termtype "IRI" ; rr:template "http://demo.openlinksw.com/Demo/employees/{EmployeeID}" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:orders_has_shippers ] ; rr:objectMap [ rr:termtype "IRI" ; rr:template "http://demo.openlinksw.com/Demo/shippers/{ShipVia}" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:orders_of_order_details ] ; rr:objectMap [ rr:parentTriplesMap <#TriplesMapOrder_Details>;  rr:joinCondition [ rr:child "OrderID" ; rr:parent "OrderID" ] ; ]; ] .
    
    <#TriplesMapProducts> a rr:TriplesMap; rr:logicalTable [ rr:tableSchema "Demo" ; rr:tableOwner "demo" ; rr:tableName "Products" ]; 
    rr:subjectMap [ rr:termtype "IRI"  ; rr:template "http://demo.openlinksw.com/Demo/products/{ProductID}"; rr:class Demo:Products; ];
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:productid ] ; rr:objectMap [ rr:column "ProductID" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:productname ] ; rr:objectMap [ rr:column "ProductName" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:quantityperunit ] ; rr:objectMap [ rr:column "QuantityPerUnit" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:unitprice ] ; rr:objectMap [ rr:column "UnitPrice" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:unitsinstock ] ; rr:objectMap [ rr:column "UnitsInStock" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:unitsonorder ] ; rr:objectMap [ rr:column "UnitsOnOrder" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:reorderlevel ] ; rr:objectMap [ rr:column "ReorderLevel" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:discontinued ] ; rr:objectMap [ rr:column "Discontinued" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:products_has_categories ] ; rr:objectMap [ rr:termtype "IRI" ; rr:template "http://demo.openlinksw.com/Demo/categories/{CategoryID}" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:products_has_suppliers ] ; rr:objectMap [ rr:termtype "IRI" ; rr:template "http://demo.openlinksw.com/Demo/suppliers/{SupplierID}" ]; ] ;
    rr:predicateObjectMap [ rr:predicateMap [ rr:constant Demo:products_of_order_details ] ; rr:objectMap [ rr:parentTriplesMap <#TriplesMapOrder_Details>;  rr:joinCondition [ rr:child "ProductID" ; rr:parent "ProductID" ] ; ]; ] .