How to direct RDF4J to use SPIN RDF triples when sp:text is present in imported RDF file? - sparql

I'm using RDF4J server and workbench version 2.2.2. I'm using a SPIN-capable repository in RDF4J, and I'm reading an RDF file I've created in TobBraid Composer Free Edition (TBC FE) containing many spin:construct rules. TBD FE is set to include the SPARQL source code for my constructors via the sp:text property. I've observed that, when the sp:text triple is present making the SPARQL source code available to RDF4J, RDF4J appears to use the SPARQL source instead of using the RDF tokenized representation of the same query. My primary and secondary questions are:
How can I direct RDF4J to use the RDF representation of a SPIN constructor when both the sp:text and the RDF representation are present in the imported RDF file?
If RDF4J uses the sp:text representation of a SPIN constructor, how can it be directed to use the prefix definitions present at the head of the imported RDF file? I have 69 constructors so far and counting. Although I can embed prefix definitions in each constructor, it seems wasteful to do so.
When I deprive RDF4J of the sp:text SPARQL source code representation, the constructors run as expected using prefixes defined at the head of the imported RDF file. I can deprive RDF4J of the source code by executing a query to delete to the source code before I instantiate the class for which I have defined a constructor. I've used this SPARQL update query to accomplish that end:
PREFIX sp: <http://spinrdf.org/sp#>
DELETE {?s sp:text ?o .} WHERE {?s sp:text ?o .}
I'd like to keep the SPARQL source code around for display purposes in a GUI I'm building that communicates with the RDF4J server via SPARQL queries. My awkward interim fix is to substitute another custom data property for the sp:text property using the SPARQL update query used in step 5 below. This property substitution successfully prevents RDF4J from finding the SPARQL source code for the SPIN constructors. RDF4J then interprets the tokenized RDF representation of the rule instead. Is there a cleaner way to force RDF4J to execute SPIN constructors from the RDF instead of from the SPARQL source code? I'm thinking there must be some way to signal RDF4J to interpret the RDF representation instead of the SPARQL source code, but I don't know how.
And now the gory details to make this situation reproducible...
Create a SPIN-capable repository in RDF4J using RDF4J workbench. Mine is named TakeRDF4J4SPIN (Repositories → New Repository)
Clear the repository using RDF4J workbench (Modify → Clear)
Load pub7.rdf using RDF4J workbench... (Modify → Add w/ use base URI as context identifier unchecked)
Note that this RDF file defines all relevant prefixes at the head of the file:
<?xml version="1.0"?>
<rdf:RDF
xmlns:fn="http://www.w3.org/2005/xpath-functions#"
xmlns:soo="http://www.disa.mil/dso/a2i/ontologies/PBSM/Sharing/SpectrumOperationsOntology#"
xmlns:spolicy="http://www.disa.mil/dso/a2i/ontologies/PBSM/Sharing/spolicy#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:spin="http://spinrdf.org/spin#"
xmlns:sp="http://spinrdf.org/sp#"
xmlns:smf="http://topbraid.org/sparqlmotionfunctions#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:arg="http://spinrdf.org/arg#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:pub7="http://www.disa.mil/dso/a2i/ontologies/PBSM/Interface/Pub7#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:spl="http://spinrdf.org/spl#"
xml:base="http://www.disa.mil/dso/a2i/ontologies/PBSM/Interface/Pub7">
Also note that the SPIN constructor attached to the class pub7:Pub7TestClass is:
CONSTRUCT {
?this spin:hasConstraintViolation _:b0 .
_:b0 a spin:ConstraintViolation ;
rdfs:label "Test message." ;
spin:violationRoot ?this ;
spin:violationLevel spin:Error ;
spolicy:sPActionRecordHasTimestamp ?timestamp .
}
WHERE {
BIND(now() AS ?timestamp) .
}
Attempt to create an instance of pub7:Pub7TestClass...
I use this SPARQL update query in RDF4J workbench's Modify → SPARQL Update to create the instance...
PREFIX inst: <http://www.disa.mil/dso/a2i/ontologies/PBSM/Sharing/Instantiations#>
PREFIX pub7: <http://www.disa.mil/dso/a2i/ontologies/PBSM/Interface/Pub7#>
INSERT DATA {
inst:aPub7TestClassInstance_test1 a pub7:Pub7TestClass .
}
Which should result in an prefix undefined error reported in RDF4J workbench due to attempting to execute the above constructor based on the sp:text SPARQL source code representation:
org.eclipse.rdf4j.query.UpdateExecutionException: org.eclipse.rdf4j.repository.RepositoryException: org.eclipse.rdf4j.sail.SailException: org.eclipse.rdf4j.query.MalformedQueryException: org.eclipse.rdf4j.query.parser.sparql.ast.VisitorException: QName 'spin:hasConstraintViolation' uses an undefined prefix
Now hide the SPIN constructors' sp:text from RDF4J using the following SPARQL update query:
.
PREFIX sp: <http://spinrdf.org/sp#>
PREFIX soo: <http://www.disa.mil/dso/a2i/ontologies/PBSM/Sharing/SpectrumOperationsOntology#>
DELETE {
?originalPolicyURI sp:systemProperty ?policySourceCodeString .
?originalPolicyURI sp:text ?policySourceCodeString .
}
INSERT {
?originalPolicyURI soo:policySourceCode ?policySourceCodeString .
}
WHERE {
?originalPolicyURI sp:text ?policySourceCodeString .
}
Now re-run the SPARQL update query shown in step 4 to instantiate the test class. The update query should run without errors this time.
Now use RDF4J Workbench's Explore → Explore function to look at the instantiation/individual we just created, inst:aPub7TestClassInstance_test1. One should see that the instantiation has a constraint violation notice via the spin:hasConstratintViolation property, providing evidence that the test constructor did indeed run this time absent the sp:text representation, forced to use the RDF tokenized representation of the SPIN constructor.

I have a partial, inelegant work-around to address my secondary question about including prefixes in the spin rules. As an initialization step, I run the following SPARQL update query that adds needed prefixes to each of the sp:text strings. Of course, this work-around is limited since the query embeds the prefix mappings I'm using. If other prefixes were required, then the query below would need to be edited to embed the new query definitions.
Here's the SPARQL Update query I'm using as an initialization step after my class/rule/property definition ontologies have been loaded into RDF4J but before I load my instantiations ontology is loaded. (Timing is critical since the instantiations ontology begins invoking SPIN constructors.)
PREFIX sp: <http://spinrdf.org/sp#>
DELETE {
?rule sp:text ?ruleText .
}
INSERT {
?rule sp:text ?newRuleText .
}
WHERE {
?rule sp:text ?ruleText .
BIND (IF (CONTAINS(?ruleText, "fn:"), "PREFIX fn: <http://www.w3.org/2005/xpath-functions#>\n","")
AS ?prefixComponent00) .
BIND (IF (CONTAINS(?ruleText, "owl:"), "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n","")
AS ?prefixComponent01) .
BIND (IF (CONTAINS(?ruleText, "spin:"), "PREFIX spin: <http://spinrdf.org/spin#>\n","")
AS ?prefixComponent02) .
BIND (IF (CONTAINS(?ruleText, "spl:"), "PREFIX spl: <http://spinrdf.org/spl#>\n","")
AS ?prefixComponent03) .
BIND (IF (CONTAINS(?ruleText, "sp:"), "PREFIX sp: <http://spinrdf.org/sp#>\n","")
AS ?prefixComponent04) .
BIND (IF (CONTAINS(?ruleText, "par:"), "PREFIX par: <http://parliament.semwebcentral.org/parliament#>\n","")
AS ?prefixComponent05) .
BIND (IF (CONTAINS(?ruleText, "rdf:"), "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n","")
AS ?prefixComponent06) .
BIND (IF (CONTAINS(?ruleText, "rdfs:"), "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n","")
AS ?prefixComponent07) .
BIND (IF (CONTAINS(?ruleText, "time:"), "PREFIX time: <http://www.w3.org/2006/time#>\n","")
AS ?prefixComponent08) .
BIND (IF (CONTAINS(?ruleText, "xsd:"), "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n","")
AS ?prefixComponent09) .
BIND (IF (CONTAINS(?ruleText, "geo:"), "PREFIX geo: <http://www.opengis.net/ont/geosparql#>\n","")
AS ?prefixComponent10) .
BIND (IF (CONTAINS(?ruleText, "geof:"), "PREFIX geof: <http://www.opengis.net/def/function/geosparql/>\n:","")
AS ?prefixComponent11) .
BIND (IF (CONTAINS(?ruleText, "inst:"), "PREFIX inst: <http://www.disa.mil/dso/a2i/ontologies/PBSM/Sharing/Instantiations#>\n","")
AS ?prefixComponent12) .
BIND (IF (CONTAINS(?ruleText, "pub7:"), "PREFIX pub7: <http://www.disa.mil/dso/a2i/ontologies/PBSM/Interface/Pub7#>\n","")
AS ?prefixComponent13) .
BIND (IF (CONTAINS(?ruleText, "pub8:"), "PREFIX pub8: <http://www.disa.mil/dso/a2i/ontologies/PBSM/Interface/Pub8#>\n","")
AS ?prefixComponent14) .
BIND (IF (CONTAINS(?ruleText, "soo:"), "PREFIX soo: <http://www.disa.mil/dso/a2i/ontologies/PBSM/Sharing/SpectrumOperationsOntology#>\n","")
AS ?prefixComponent15) .
BIND (IF (CONTAINS(?ruleText, "spolicy:"), "PREFIX spolicy: <http://www.disa.mil/dso/a2i/ontologies/PBSM/Sharing/spolicy#>\n","")
AS ?prefixComponent16) .
BIND (IF (CONTAINS(?ruleText, "sharing:"), "PREFIX sharing: <http://www.disa.mil/dso/a2i/ontologies/PBSM/Sharing/sharing#>\n","")
AS ?prefixComponent17) .
BIND (IF (CONTAINS(?ruleText, "dd1494:"), "PREFIX dd1494: <http://www.disa.mil/dso/a2i/ontologies/PBSM/Sharing/dd1494#>\n","")
AS ?prefixComponent18) .
BIND (IF (CONTAINS(?ruleText, "Nuvio:"), "PREFIX Nuvio: <http://cogradio.org/ont/Nuvio.owl#>\n","")
AS ?prefixComponent19) .
BIND (IF (CONTAINS(?ruleText, "CRO2:"), "PREFIX CRO2: <http://cogradio.org/ont/CRO2.owl#>\n","")
AS ?prefixComponent20) .
BIND (IF (CONTAINS(?ruleText, "olo:"), "PREFIX olo: <http://purl.org/ontology/olo/core#>\n","")
AS ?prefixComponent21) .
BIND (CONCAT(
?prefixComponent00,
?prefixComponent01,
?prefixComponent02,
?prefixComponent03,
?prefixComponent04,
?prefixComponent05,
?prefixComponent06,
?prefixComponent07,
?prefixComponent08,
?prefixComponent09,
?prefixComponent10,
?prefixComponent11,
?prefixComponent12,
?prefixComponent13,
?prefixComponent14,
?prefixComponent15,
?prefixComponent16,
?prefixComponent17,
?prefixComponent18,
?prefixComponent19,
?prefixComponent20,
?prefixComponent21
) AS ?prefixes ) .
BIND (CONCAT(?prefixes, "\n", ?ruleText) AS ?newRuleText) .
}
It is possible that the query above could include a prefix definition based on a false positive if the prefix definition happened to be contained in an embedded string or comment. For example, the literal "Greg’s solo: five measures"^^xsd:string would cause the above query to include the definition of the olo: prefix, perhaps needlessly. However, inclusion of non-required prefixes had no major impact on performance and no impact on correctness. One could do better by using regular expression in place of the simple CONTAINS matches in the query above.

Related

Converting a bnode to a string in graphdb

I have an RDF file where the resources are identified with nodeID's instead of URIs. I have imported them into Ontotext graphdb, and would like to generate URIs based on the nodeID (which I preserved during import). For example, I am trying to map this triple
_:C00456 rdf:type skos:Concept
to this:
<https://example.com/data/C00456> rdf:type skos:Concept
Unfortunately, if ?s is a BNODE, STR(?s) is an empty string in graphdb. xsd:string(?s), ditto. IRI(?s), you guessed it. Is there any function that will expose the form of the bnode as a string, so that I can build a URI from it? I went through the list of functions in the sparql 1.1 specification and could not see any.
PS It would have been nice if graphdb would just convert the nodeIDs to URIs during import (I specified a prefix for relative names), but it went by the book and turned them into bnodes. If I overlooked something, I'll be glad to be set right.
You can use the spif:buildString function to convert a BNode to String and then to IRI.
Here is a sample query:
PREFIX spif: <http://spinrdf.org/spif#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
CONSTRUCT {?sIRI ?p ?o} WHERE {
?s ?p ?o .
FILTER (isBlank(?s))
BIND (IRI(spif:buildString("http://my/namespace/{?1}", ?s)) as ?sIRI)
} LIMIT 10
The function is documented here:
https://graphdb.ontotext.com/documentation/10.0/sparql-functions-reference.html?highlight=buildstring#sparql-spin-functions-and-magic-predicates

RDF + OWL reasoning

Let's suppose I have an RDF data about Socrates. The data is shown below
subject, predicate, object
man, being, mortal
Socrates, being, man
To check whether Socrates is mortal I have a request
SELECT *
FROM RDFData t1
JOIN RDFData t2
ON t1.subject = t2.object
Then I have a filter on "Socrates" and "mortal" and if result is not empty, then Socrates is mortal.
It works fine, but my teacher asks to add OWL information.
For example, if we have the next data
subject, predicate, object
man, being, mortal
Socrates, being, Greek
Greek, being, man
My approach does not work, because we have additional step in the chain.
I need to add an OWL static data here and implement a request for arbitrary number of steps in the chain.
What are my next steps?
If we turn your example data into actual RDF (using Turtle syntax), you'd get something like this:
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
#prefix : <http://example.org/> .
:socrates rdf:type :Greek .
:Man rdfs:subClassOf :Mortal .
:Greek rdfs:subClassOf :Man .
If you upload this data into an RDF database (such as RDF4J, Jena, GraphDB, Stardog, Blazegraph, or a host of other options), you can use the following SPARQL query:
ASK WHERE { :socrates rdf:type/rdfs:subClassOf* :Mortal }
This checks if :socrates is of a type that is either :Mortal, or a (direct or indirect) subclass of :Mortal. It returns true if Socrates is a mortal, false otherwise. You don't even need a reasoner for this, you can just use the expressivity of the query language.
If your RDF database supports basic RDFS reasoning, you can simplify your query even further:
ASK WHERE { :socrates rdf:type :Mortal }

A simple SPIN rule doesn't work in RDF4J

I've just started using the triple store RDF4J (I am using its workbench, version 2.3.1, run on Windows 10 with Tomcat 9.0)
I want to use the SPIN rules in RDF4J. Therefore, I created a new repository (In memory with RDFS+SPIN support).
I wanted to start with the SPIN example in RDF4J documentation concerning how to add SPIN rules. That is, I added the data (in Turtle, and imported to RDF4J)
#prefix ex: <http://example.org/>.
ex:John a ex:Father ;
ex:parentOf ex:Lucy .
ex:Lucy a ex:Person .
And the rule:
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
#prefix sp: <http://spinrdf.org/sp#>.
#prefix spin: <http://spinrdf.org/spin#>.
#prefix ex: <http://example.org/>.
ex:Person a rdfs:Class ;
spin:rule [
a sp:Construct ;
sp:text """PREFIX ex: <http://example.org/>
CONSTRUCT { ?this ex:childOf ?parent . }
WHERE { ?parent ex:parentOf ?this . }"""
] .
And as instructed in the documentation, I exposed the query (with the checkbox 'Include inferred statements' checked),
PREFIX ex: <http://example.org/>
SELECT ?child
WHERE { ?child ex:childOf ?parent }
However, no result returned:
Could someone, please tell me am I doing something wrong, why the SPIN rule doesn't work in my RDF4J workbench, have I missed something?
(reposting my comment as an answer for future readers)
The SPIN reasoner currently assumes that all data is in the default context, I think. Make sure that your data was not added to a named graph.

How to get composer of a symphony in dbpedia?

This is my query
select *
{
?symphonies_by_composer <http://www.w3.org/2004/02/skos/core#broader> <http://dbpedia.org/resource/Category:Symphonies_by_composer> .
?symphony <http://purl.org/dc/terms/subject> ?symphonies_by_composer .
}
I run it over Dbpedia end point http://dbpedia.org/sparql/
it gives me many symphonies. i want to construct my triples, adding my own property, which is mo:composedBy like this:
PREFIX mo: <http:blablabla.com/mo#>
construct
{
?symphony mo:composedBy ?composer .
?symphony a mo:Symphony
}
{
?symphonies_by_composer <http://www.w3.org/2004/02/skos/core#broader> <http://dbpedia.org/resource/Category:Symphonies_by_composer> .
?symphony <http://purl.org/dc/terms/subject> ?symphonies_by_composer .
}
but i don't know how to get the binding for the ?composer variable.
Do you know how ?
(I'm aware that there might be no way to get it, if you think there is no way, kindly just let me know and i will pass, unfortunately, those data)
There seems to be no explicit relation in DBPedia connecting these symphonies to an actual resource that represents the composer.
A possible workaround is to extract the name of the composer from the prefLabel of the category, by snipping off the first bit ("Symphonies by"):
PREFIX mo: <http://example.com/mo#>
PREFIX dct: <http://purl.org/dc/terms/>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
CONSTRUCT
{
?symphony mo:composedBy ?composer_name .
?symphony a mo:Symphony
}
WHERE
{
?symphonies_by_composer skos:broader <http://dbpedia.org/resource/Category:Symphonies_by_composer> ;
skos:prefLabel ?label .
?symphony dct:subject ?symphonies_by_composer .
BIND(SUBSTR(STR(?label), (STRLEN("Symphonies by ") + 1)) AS ?composer_name)
}
This will give you back the name of each composer as a literal value.
A second possible step is to try and reconstruct the actual IRI of the resource identifying the composer, from the name. For example, in the case of "Hans Werner Henze", the actual resource identifying the person is http://dbpedia.org/resource/Hans_Werner_Henze, so a simple further string operations or two, replacing spaces and concatenating with the dbpedia base IRI, will resolve this. However, this is brittle, as there is no guarantee that the resource exists, and even if it does, whether it actually identifies the composer (there might be more than one Hans Werner Henze, for instance).
Of course, you can expand this further by doing followup queries to verify that the resource exists and is the correct one, but it will require some additional trial and error. If the goal is simply the name of the composer, the first example query should work fine for most instances.

PROTÉGÉ SPARQL QUERY TAB: cannot query for ontology-specific classes

I am using SPARQL Query tab in Protege 5 to query an OWL ontology I have been constructing. I succeded in many kinds of queries, but when I use some specific class of my ontology inside the very same queries (that are apparently well formed) they return no results. Following, two of the problematic queries - assuming "Event" as one of the concepts of the ontology (http://www.semanticweb.org/ontologies/2014/5/MyOnto#Event):
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX onto: <http://www.semanticweb.org/ontologies/2014/5/MyOnto#>
SELECT ?a WHERE { ?a rdfs:subClassOf onto:Event }
and, with the same prefixes
SELECT ?a WHERE { ?a rdfs:range onto:Event }
Both return no results. However, if I substitute "onto:Event" for, let's say, ?b, both return a long list of results - inclunding Event as a match for ?b.
Is it something I'm misusing or forgetting (although I've seen this pattern in several links on internet with people claiming to have got results) or is it a limitation of SPARQL or some issue of the Protege tab?
The problem is that, in fact, although the URI of the ontology is:
<http://www.semanticweb.org/ontologies/2014/5/MyOnto#>
in the OWL document, the prefix used before class names is the IRI:
<http://www.semanticweb.org/ontologies/2014/2/untitled-ontology-662#>
Thus, replacing the old onto: by
PREFIX onto: <http://www.semanticweb.org/ontologies/2014/2/untitled-ontology-662#>
solves the issue.
(Thanks to #Csongor from Protégé Project mail list, who found the answer.)
P.S.: It's also worthy to note that it can't be taken for granted that all the terms in the ontology will be <current_ontology_URI#term> - e.g. if one includes some terms in the ontology and then changes ontology URI, these terms will be identified as <previous_ontology_URI#term> and the new ones as <current_ontology_URI#term> (which was exactly the cause of the problem above).
I had the same problem, whenever using an ontology specific class there were no results although there should have been. My ontology (pizza.owl) was loaded from a local file.
I found that it is required to add the file name in the PREFIX.
PREFIX : <http://www.co-ode.org/ontologies/pizza/pizza.owl#>
After that I got the information that I expected.