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
- HTML+Microdata Query Results Doc
- HTML+JSON-LD Query Results Doc
- Beautified RDF-Turtle – for Linked Data follow-your-nose exploration