Usecase — OpenLink Structured Data Inclusion-Engine (OSDI) & HTML-based Structured Data Islands

Our Challenge and Solution

Our challenge was to automatically generate HTML-based Structured Data Islands and include them in the Web Pages for our Software Applications (the Universal Data Access [UDA] Suite of Data Connectors [including ODBC Drivers, JDBC Drivers, OLE DB Providers, ADO.NET Providers, and others], Virtuoso Universal Server, and others).

We solved this by using a template and data injection approach driven by RDF and SPARQL.

SPASQL for UDA

SPARQL

PREFIX  schema:  <http://schema.org/>
PREFIX  opllic:  <http://www.openlinksw.com/ontology/licenses#>
PREFIX  oplsof:  <http://www.openlinksw.com/ontology/software#>
PREFIX  oplpro:  <http://www.openlinksw.com/ontology/products#>
PREFIX  oplofr:  <http://www.openlinksw.com/ontology/offers#>

INSERT 
  {
    GRAPH <urn:mdata:websites:google:seo>
      {
        ?Page   schema:offers        ?Offer .
      # ?Offer  schema:url           ?Offer ;            
        ?Offer  schema:name          ?OfferName ;
                schema:image         ?image ;
                schema:validFrom     ?ValidFrom ;
                schema:validThrough  ?ValidTo ;
                schema:price         ?OfferPrice
       }
  }
WHERE
  {
    GRAPH  <urn:opl:shop:offering:sponging:cache:official> 
      {
        ?Offer      a                          ?type ;
                    schema:category            ?category ;
                    schema:name                ?OfferName ;
                    schema:image               ?image ;
                    schema:validFrom           ?ValidFrom ;
                    schema:validThrough        ?ValidTo .
        FILTER (      ?ValidFrom <= bif:curutcdatetime() 
                 AND    ?ValidTo >= bif:curutcdatetime()
               ) .
        FILTER ( CONTAINS(STR(?type),"UDA")
               ) 
        ?Offer      schema:priceSpecification  ?pricespec .
        ?pricespec  a                          schema:UnitPriceSpecification  ;
                    schema:validFrom           ?PriceValidFrom ;
                    schema:validThrough        ?PriceValidTo ;
                    schema:priceCurrency       ?PriceSpecCurrency .
        FILTER (      ?PriceValidFrom <= bif:curutcdatetime() 
                 AND    ?PriceValidTo >= bif:curutcdatetime()
               ) . 
        ?pricespec  schema:price               ?OfferPrice .
        OPTIONAL
          { ?pricespec  oplofr:hasRetailPriceSpecification [ schema:price ?OfferRetailPrice ] } .

        ?Offer      schema:itemOffered ?License .

        ?License    opllic:productLicenseOf        ?SoftwareAppRelease . 
        ?License    oplsof:hasOperatingSystemType  [ schema:name ?LicenseOSType ] . 
      }

    GRAPH ?g 
      {
        ?SoftwareApp  oplpro:hasRelease  ?SoftwareAppRelease . 
        ?Page         schema:mainEntity  ?SoftwareApp  .
        FILTER ( !CONTAINS ( STR ( ?Page ), "Wiki" ) )
      } 
     
  } ;

SPASQL for Virtuoso

SPARQL

PREFIX  schema:  <http://schema.org/>
PREFIX  opllic:  <http://www.openlinksw.com/ontology/licenses#>
PREFIX  oplsof:  <http://www.openlinksw.com/ontology/software#>
PREFIX  oplpro:  <http://www.openlinksw.com/ontology/products#>
PREFIX  oplofr:  <http://www.openlinksw.com/ontology/offers#>

INSERT
  {
    GRAPH <urn:mdata:websites:google:seo> 
      {
        ?Page   schema:offers        ?Offer .
      # ?Offer  schema:url           ?Offer ;            
        ?Offer  schema:name          ?OfferName ;
                schema:image         ?image ;
                schema:validFrom     ?ValidFrom ;
                schema:validThrough  ?ValidTo ;
                schema:price         ?OfferPrice
      }
  }
WHERE
  {
    GRAPH  <urn:opl:shop:offering:sponging:cache:official>  
      {
        ?Offer  a                    schema:Offer ;
                a                    oplofr:Virtuoso8Offer ;
                schema:category      ?OfferCategory ;
                schema:name          ?OfferName ;
                schema:image         ?image ;
                schema:itemOffered   ?License ;
                schema:validFrom     ?ValidFrom ;
                schema:validThrough  ?ValidTo .
                FILTER (      ?ValidFrom <= bif:curutcdatetime() 
                         AND  ?ValidTo   >= bif:curutcdatetime()
                       ) .
                ?License opllic:productLicenseOf ?SoftwareAppRelease . 
      }
      {
        SELECT                                         ?SoftwareApp 
                                                       ?SoftwareAppRelease 
                <http://virtuoso.openlinksw.com/>  AS  ?Page 
        WHERE 
          { 
            GRAPH ?g 
              {
                ?SoftwareApp  oplpro:hasRelease  ?SoftwareAppRelease 
              }
          }
      } 
  } ;

SQL Update commands for OSDI Configuration

---  Data Merge Configuration

incleng..config_unset ( null, null, 'inline_links' ) ;
incleng..config_unset ( null, null, 'inline_headers' ) ;
incleng..config_unset ( null, null, 'inline_jsonld' ) ;
incleng..config_unset ( null, null, 'inline_rdfa' ) ;
incleng..config_unset ( null, null, 'inline_rdfa_schema_only' ) ;
incleng..config_unset ( null, null, 'inline_html5md' ) ;
incleng..config_unset ( null, null, 'inline_jsonld_schema_only' ) ;
incleng..config_unset ( null, null, 'inline_ttl_schema_only' ) ;
incleng..config_unset ( null, null, 'inline_ttl' ) ;
incleng..config_unset ( null, null, 'inline_html5md_schema_only' ) ;
incleng..config_unset ( null, null, 'sparql_custom_query' ) ;
incleng..config_unset ( null, null, 'custom_query' ) ;
incleng..config_unset ( null, null, 'addthis' ) ;
incleng..config_unset ( null, null, 'debug_level' ) ;
incleng..config_unset ( null, null, 'sparql_timeout' ) ;
incleng..config_unset ( null, null, 'search_graphs' ) ;
 
-- Query Timeouts

incleng..config_set ( null, null, 'sparql_timeout', 30000000 ) ;

-- Apply New OSDI Configuraton Settings 

incleng..config_set ( null, null, 'inline_links',   0 ) ;
incleng..config_set ( null, null, 'inline_headers', 0 ) ;
incleng..config_set ( null, null, 'addthis',        0 ) ;

-- incleng..config_set ( null, null, 'inline_ttl',     1 ) ;
-- incleng..config_set ( null, null, 'inline_jsonld',  1 ) ;
-- incleng..config_set ( null, null, 'inline_html5md', 1 ) ;
-- incleng..config_set ( null, null, 'inline_rdfa',    1 ) ;


-- JSON-LD Stuctured Data Island Instructions

incleng..config_set
  ( null, 
    null, 
    'inline_jsonld_schema_only', 
    'CONSTRUCT { <{url}>  schema:offers        ?Offer . 
                 ?Offer   schema:url           ?Offer ;
                          schema:name          ?OfferName ; 
                          schema:image         ?image ; 
                          schema:validFrom     ?ValidFrom ; 
                          schema:validThrough  ?ValidTo ; 
                          schema:price         ?OfferPrice
               } 
     FROM  <urn:mdata:websites:google:seo> 
     WHERE { <{url}>  schema:offers        ?Offer . 
             ?Offer   schema:url           ?Offer ;
                      schema:name          ?OfferName ; 
                      schema:image         ?image ; 
                      schema:validFrom     ?ValidFrom ; 
                      schema:validThrough  ?ValidTo ; 
                      schema:price         ?OfferPrice
           }'
  );


-- HTML5+Microdata Stuctured Data Island Instructions

incleng..config_set
  ( null, 
    null, 
    'inline_html5md_schema_only', 
    'CONSTRUCT { <{url}>  schema:offers        ?Offer . 
                 ?Offer   schema:url           ?Offer ;
                          schema:name          ?OfferName ; 
                          schema:image         ?image ; 
                          schema:validFrom     ?ValidFrom ; 
                          schema:validThrough  ?ValidTo ; 
                          schema:price         ?OfferPrice
               } 
     FROM  <urn:mdata:websites:google:seo> 
     WHERE  { <{url}>  schema:offers        ?Offer . 
              ?Offer   schema:url           ?Offer ;
                       schema:name          ?OfferName ; 
                       schema:image         ?image ; 
                       schema:validFrom     ?ValidFrom ; 
                       schema:validThrough  ?ValidTo ; 
                       schema:price         ?OfferPrice
            }'
  );

-- RDF-Turtle  Stuctured Data Island Instructions

incleng..config_set
  ( null, 
    null, 
    'inline_ttl_schema_only' , 
    'CONSTRUCT { <{url}>  schema:offers        ?Offer . 
                 ?Offer   schema:url           ?Offer ;
                          schema:name          ?OfferName; 
                          schema:image         ?image ; 
                          schema:validFrom     ?ValidFrom ; 
                          schema:validThrough  ?ValidTo ; 
                          schema:price         ?OfferPrice
               } 
     FROM  <urn:mdata:websites:google:seo> 
     WHERE  { <{url}>  schema:offers        ?Offer . 
              ?Offer   schema:url           ?Offer ;    
                       schema:name          ?OfferName ; 
                       schema:image         ?image ; 
                       schema:validFrom     ?ValidFrom ; 
                       schema:validThrough  ?ValidTo ; 
                       schema:price         ?OfferPrice
            }'
  );

-- RDFa  Stuctured Data Island Instructions

incleng..config_set
  ( null, 
    null, 
    'inline_rdfa_schema_only', 
    'CONSTRUCT { <{url}>  schema:offers        ?Offer . 
                 ?Offer   schema:url           ?Offer ;
                          schema:name          ?OfferName ; 
                          schema:image         ?image ; 
                          schema:validFrom     ?ValidFrom ; 
                          schema:validThrough  ?ValidTo ; 
                          schema:price         ?OfferPrice
               } 
     FROM <urn:mdata:websites:google:seo> 
     WHERE { <{url}>   schema:offers        ?Offer . 
             ?Offer    schema:url           ?Offer ;
                       schema:name          ?OfferName ; 
                       schema:image         ?image ; 
                       schema:validFrom     ?ValidFrom ; 
                       schema:validThrough  ?ValidTo ; 
                       schema:price         ?OfferPrice 
           }'
  );

SELECT incleng..config_flush_cache() ;

Template Engine Configuration

OSDI works on the basis of template-markers within SPARQL queries. These markers, <{url}>, are substituted with the URL of a Web Page for which a description is retrieved from Virtuoso using SPARQL (in this case a CONSTRUCT, which offers full control of the Semantic Web structure).

Thus, given a template, for JSON-LD –

incleng..config_set
  ( null, 
    null, 
    'inline_jsonld_schema_only', 
    'CONSTRUCT { <{url}>  schema:offers        ?Offer . 
                 ?Offer   schema:url           ?Offer ;
                          schema:name          ?OfferName ; 
                          schema:image         ?image ; 
                          schema:validFrom     ?ValidFrom ; 
                          schema:validThrough  ?ValidTo ; 
                          schema:price         ?OfferPrice
               } 
     FROM <urn:mdata:websites:google:seo> 
     WHERE { <{url}>  schema:offers        ?Offer . 
             ?Offer   schema:url           ?Offer ;
                      schema:name          ?OfferName ; 
                      schema:image         ?image ; 
                      schema:validFrom     ?ValidFrom ; 
                      schema:validThrough  ?ValidTo ; 
                      schema:price         ?OfferPrice
           }'
  );

– or HTML5+Microdata –

incleng..config_set
  ( null, 
    null, 
    'inline_html5md_schema_only', 
    'CONSTRUCT { <{url}>  schema:offers        ?Offer . 
                 ?Offer   schema:url           ?Offer ;
                          schema:name          ?OfferName ; 
                          schema:image         ?image ; 
                          schema:validFrom     ?ValidFrom ; 
                          schema:validThrough  ?ValidTo ; 
                          schema:price         ?OfferPrice
                } 
     FROM <urn:mdata:websites:google:seo> 
     WHERE { <{url}>  schema:offers        ?Offer . 
             ?Offer   schema:url           ?Offer ;
                      schema:name          ?OfferName ; 
                      schema:image         ?image ; 
                      schema:validFrom     ?ValidFrom ; 
                      schema:validThrough  ?ValidTo ; 
                      schema:price         ?OfferPrice
           }'
  );

– or RDF-Turtle –

incleng..config_set
  ( null, 
    null, 
    'inline_ttl_schema_only', 
    'CONSTRUCT { <{url}>  schema:offers  ?Offer . 
                 ?Offer   schema:url  ?Offer ;
                          schema:name ?OfferName ; 
                          schema:image ?image ; 
                          schema:validFrom ?ValidFrom ; 
                          schema:validThrough ?ValidTo ; 
                          schema:price ?OfferPrice
               } 
     FROM <urn:mdata:websites:google:seo> 
     WHERE { <{url}>  schema:offers        ?Offer . 
             ?Offer   schema:url           ?Offer ;
                      schema:name          ?OfferName ; 
                      schema:image         ?image ; 
                      schema:validFrom     ?ValidFrom ; 
                      schema:validThrough  ?ValidTo ; 
                      schema:price         ?OfferPrice
           }'
  );

– you can manually replace the <{url}> placeholder with an actual URL (e.g., <http://virtuoso.openlinksw.com/>, <http://uda.openlinksw.com/jdbc-odbc-mt/>, <http://uda.openlinksw.com/odbc-oracle-st/>, or <http://uda.openlinksw.com/odbc-db2-ee/>) to verify that the injection (or merge process) is actually happening.

Examples

PREFIX  schema:  <http://schema.org/>
PREFIX  opllic:  <http://www.openlinksw.com/ontology/licenses#>
PREFIX  oplsof:  <http://www.openlinksw.com/ontology/software#>
PREFIX  oplpro:  <http://www.openlinksw.com/ontology/products#>
PREFIX  oplofr:  <http://www.openlinksw.com/ontology/offers#>

DESCRIBE ?License ?Offer ?SoftwareApp ?Page 
WHERE {
	   		 GRAPH  <urn:opl:shop:offering:sponging:cache:official>  
	   		      {
	   			 	 ?Offer a schema:Offer ;
	   			     a oplofr:Virtuoso8Offer ;
	   			     schema:category ?OfferCategory ;
	   			     schema:name ?OfferName ;
	   				 schema:image ?image ;
	   			     schema:itemOffered ?License ;
	   			     schema:validFrom ?ValidFrom ;
	   			     schema:validThrough ?ValidTo .
	   			 	 FILTER(?ValidFrom <= bif:curutcdatetime() and ?ValidTo >= bif:curutcdatetime()) .

	   				 ?License opllic:productLicenseOf ?SoftwareAppRelease . 

	   			}
			
	           {
	                 SELECT ?SoftwareApp ?SoftwareAppRelease 
	   			         <http://virtuoso.openlinksw.com/> as ?Page 
	   					 WHERE { GRAPH ?g {
	   					 					?SoftwareApp oplpro:hasRelease ?SoftwareAppRelease 
	   										}
	   							}
	          } 
} 

Live Result Pages for the query above

  1. HTML+Microdata Query Results Doc
  2. HTML+JSON-LD Query Results Doc
  3. Beautified RDF-Turtle – for Linked Data follow-your-nose exploration

Screenshot – Special Offer for License of Virtuoso Multi-Model RDBMS and Data Virtualization Platform

Screenshot – Special Offer for License of Lite Edition Data Connector

Screenshot – Special Offer for License of Enterprise Edition Data Connector

Related