SPARQL property paths based on a new property defined in a CONSTRUCT subquery - sparql

Given the following schema, "driver-passenger" lineages can be easily seen:
tp:trip a owl:Class ;
rdfs:label "trip"#en ;
rdfs:comment "an 'asymmetric encounter' where someone is driving another person."#en .
tp:driver a owl:ObjectProperty ;
rdfs:label "driver"#en ;
rdfs:comment "has keys."#en ;
rdfs:domain tp:trip ;
rdfs:range tp:person .
tp:passenger a owl:ObjectProperty ;
rdfs:label "passenger"#en ;
rdfs:comment "has drinks."#en ;
rdfs:domain tp:trip ;
rdfs:range tp:person .
Consider the following data:
<alice> a tp:person .
<grace> a tp:person .
<tim> a tp:person .
<ruth> a tp:person .
<trip1> a tp:trip ;
tp:participants <alice> , <grace> ;
tp:driver <alice> ;
tp:passenger <grace> .
<trip2> a tp:trip ;
tp:participants <alice> , <tim> ;
tp:driver <alice> ;
tp:passenger <tim> .
<trip3> a tp:trip ;
tp:participants <tim> , <grace> ;
tp:driver <tim> ;
tp:passenger <grace> .
<trip4> a tp:trip ;
tp:participants <grace> , <ruth> ;
tp:driver <grace> ;
tp:passenger <ruth> .
<trip5> a tp:trip ;
tp:participants <grace> , <tim> ;
tp:driver <grace> ;
tp:passenger <tim> .
Now let a "driver-passenger descendent" be any tp:passenger at the end of a trip sequence where the tp:passenger of one trip is the tp:driver of the next trip
Ex. <ruth> is a descendent of <alice> according to the following sequence of trips:
<trip2> -> <trip3> -> <trip4>.
Question:
How to get the (ancestor,descendent) pairs of all driver-passenger lineages?
Attempt 1:
I initially tried the following CONSTRUCT subquery to define an object property: tp:drove, which can be easily used in a property path. However, this did not work on my actual data:
SELECT ?originalDriver ?passengerDescendent
WHERE {
?originalDriver tp:drove+ ?passengerDescendent .
{
CONSTRUCT { ?d tp:drove ?p . }
WHERE { ?t a tp:trip .
?t tp:driver ?d .
?t tp:passenger ?p .}
}
}
Attempt 2:
I tried to create property path which expresses an ancestor as the driver of a passenger, but I don't think I've properly understood how this is supposed to work:
(tp:driver/^tp:passenger)+
Regarding MWE: Is there some kind of RDF sandbox that would allow me to create an MWE by defining a simple ontology like tp above, along with some sample data? The following "playgrounds" are available but none of them seem to support defining a toy ontology: SPARQL Playground, SPARQL Explorer.
Notes on related content:
This question is directly related to a previous question, but no longer requires saving the paths themselves, a feature not directly supported by SPARQL 1.1.
This answer by Joshua Taylor seems relevant, but doesn't address the identification of specific types of paths, such as the lineages defined above.

This one seems to do the trick:
select ?driver ?passenger where {
?driver (^tp:driver/tp:passenger)+ ?passenger .
filter( ?driver != ?passenger)
}
The filter condition can be removed if you want to also see relationships that lead back to the same person.

Related

Extract synonyms and label from Turtle file using SPARQL

I am in a learning phase of SPARQL. I am working with a Turtle file to extract some information. The condition is: if the exact synonym has a substring 'stroke' or 'Stroke', the query should return all the synonyms and rdfs:label.
I am using below query but getting no output:
prefix oboInOwl: <http://www.geneontology.org/formats/oboInOwl#>
prefix obo: <http://purl.obolibrary.org/obo/>
prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
Select * where {
?s ?p ?o .
rdfs:label <http://www.geneontology.org/formats/oboInOwl#hasExactSynonym> "stroke"^^xsd:string
}
Below is the sample Turtle file:
### https://ontology.aaaa.com/aaaa/meddra_10008196
:meddra_10008196
rdf:type owl:Class ;
<http://www.geneontology.org/formats/oboInOwl#hasDbXref> "DOID:6713" , "EFO:0000712" , "EFO:0003763" , "HE:A10008190" ;
<http://www.geneontology.org/formats/oboInOwl#hasExactSynonym>
"(cva) cerebrovascular accident" ,
"Acute Cerebrovascular Accident" ,
"Acute Cerebrovascular Accidents" ,
"Acute Stroke" ,
"Acute Strokes" ;
rdfs:label "Cerebrovascular disorder"#en ;
:hasSocs "Nervous system disorders [meddra:10029205]" , "Vascular disorders [meddra:10047065]" ;
:uid "6e46da69b727e4e924c31027cdf47b8a" .
I am expecting this output:
(cva) cerebrovascular accident
Acute Cerebrovascular Accident
Acute Cerebrovascular Accidents
Acute Stroke
Acute Strokes
Cerebrovascular disorder
With this triple pattern, you are querying for rdfs:label as subject, not as predicate:
rdfs:label <http://www.geneontology.org/formats/oboInOwl#hasExactSynonym> "stroke"^^xsd:string
What you are asking with this is: "Does the resource rdfs:label have the property oboInOwl:hasExactSynonym with the string value 'stroke'?"
But you want to ask this about the class (e.g., :meddra_10008196), not rdfs:label:
?class oboInOwl:hasExactSynonym "stroke" .
Finding matches
As you don’t want to find only exact string matches, you can use CONTAINS:
?class oboInOwl:hasExactSynonym ?matchingSynonym .
FILTER( CONTAINS(?matchingSynonym, "stroke") ) .
As you want to ignore case, you can query lower-cased synonyms with LCASE:
?class oboInOwl:hasExactSynonym ?matchingSynonym .
FILTER( CONTAINS(LCASE(?matchingSynonym), "stroke") ) .
Displaying results
To display the label and all synonyms in the same column, you could use a property path with | (AlternativePath):
?class rdfs:label|oboInOwl:hasExactSynonym ?labelOrSynonym .
Full query
# [prefixes]
SELECT ?class ?labelOrSynonym
WHERE {
?class rdfs:label|oboInOwl:hasExactSynonym ?labelOrSynonym .
FILTER EXISTS {
?class oboInOwl:hasExactSynonym ?matchingSynonym .
FILTER( CONTAINS(LCASE(?matchingSynonym), "stroke") ) .
}
}

How to store conditional data in RDF

I want to store some facts in pure RDF data (ttl) like :
:Person :hasGender :Male, :Female ;
:drink :Liquor, :softDrinks .
if (:someone :hasGender :Male) then
:someone :drink :Liquor ;
else
:someone :drink :softDrinks .
:Susan a :Person ;
:hasGender :Female .
and then read this rdf data (ttl) by Sparql or other app (rdf4j or rdflib) , and get :
:Susan :drink :softDrinks .
I want to use pure RDF as much as possible to store data, rather than OWL, N3, RDF-star or SHACL, but I can reconstruct a new N3 or shacl file from these rdf data, and then get infered result.
I wonder if this is possible in RDF, an how can I modify this RDF data? Thanks.
I got It done. here is data.ttl :
#prefix : <http://example.org/#> .
:Person :drink :someLiquid .
:someLiquid :hasRule :rule1, :rule2.
:rule1 :forGender :Male ;
:beverage :liquor .
:rule2 :forGender :Female ;
:beverage :softDrinks .
:Susan a :Person ;
:hasGender :Female .
:John a :Person ;
:hasGender :Male .
here is the sparql query:
PREFIX : <http://example.org/#>
SELECT ?person ?gender ?beverage
WHERE {
:Person :drink :someLiquid .
:someLiquid :hasRule ?rule .
?rule :forGender ?forGender ;
:beverage ?beverage .
?person a :Person;
:hasGender ?gender .
FILTER ( ?gender = ?forGender )
}
and I get the result:
1 person, gender, beverage
2 :Susan, :Female, :softDrinks
3 :John, :Male, :liquor

Classes and subclasses in OWL for SPARQL queries with inference

I have a question with OWL and SPARQL that I can't resolve. I have defined several classes, but for the question in question only 3 are the important ones: People, Men and Women; and its definitions would be the following:
<#People> a owl:Class ;
rdfs:label "People"#en .
<#Men> a owl:Class ;
rdfs:subClassOf <#People> ;
rdfs:label "Men"#en .
<#Women> a owl:Class ;
rdfs:subClassOf <#People> ;
rdfs:label "Women"#en .
And then a data in RDF for example:
<rdf:Description rdf:about="Registration#1">
<rdfs:label>ARCHEOLOGY GRADUATE</rdfs:label>
<ex:BranchKnowledge>ARTS AND HUMANITIES</ex:BranchKnowledge>
<ex:Degree>ARCHEOLOGY GRADUATE</ex:Degree>
<ex:Men rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">63</ex:Men>
<ex:Women rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">99</ex:Women>
<dcterms:coverage>2015/2016</dcterms:coverage>
</rdf:Description>
If I wanted to obtain the number of Men and Women of each data, I get it with the following query:
SELECT ?X ?degree ?branch ?men ?women
WHERE {
?X ex:Degree ?degree .
?X ex:BranchKnowledge ?branch .
?X ex:Men ?men .
?X ex:Women ?women
}
If I now want to get the total number of Men and Women inferring that both are People, I had thought that since both are subclasses of People, I could make the following query:
SELECT ?X ?degree ?branch ?people
WHERE {
?X ex:Degree ?degree .
?X ex:BranchKnowledge ?branch .
?X ex:People ?people
}
However I don't get any results.
Have I misconstructed the relationship between classes and subclasses for what I want to do or what would be the problem? (I'm working on a Virtuoso server).
You should be aware of the fact that you're using punning, i.e. the same URI for Men and Women as OWL classes and OWL data properties. I don't understand why you're doing this. Why don't you introduce properties like numberOfMen, numberOfWomen and numberOfPeople?
Next, you're using ex:People as a property, thus, you would have to define that ex:Men and ex:Women are sub-properties of ex:People.
<#People> a owl:DatatypeProperty ;
rdfs:label "People"#en .
<#Men> a owl:DatatypeProperty ;
rdfs:subPropertyOf <#People> ;
rdfs:label "Men"#en .
<#Women> a owl:DatatypeProperty ;
rdfs:subPropertyOf <#People> ;
rdfs:label "Women"#en .
And then you have to enable reasoning in the triple store if supported or use SPARQL 1.1 property paths.
But, more important, this would only lead to two rows, which means you have to do the aggregation in your SPARQL query by using the sum function, e.g.:
SELECT ?X ?degree ?branch (sum(?_people) as ?people)
WHERE {
?X ex:Degree ?degree .
?X ex:BranchKnowledge ?branch .
?p rdfs:subPropertyOf* ex:People .
?X ?p ?_people
}
GROUP BY ?X ?degree ?branch
Note, this query only works if there is just a single degree and branch, otherwise it would sum duplicate values.

Numeric properties that are returned as string by SPARQL

I'm having a problem with SPARQL when dealing with numeric data types.
I have an ontology (http://cabas.ugr.es/ontology/ugr) in which I have defined a pair of properties that represent the number of students who are of a particular sex:
<http://cabas.ugr.es/ontology/ugr#hombres>
a owl:DatatypeProperty, owl:FunctionalProperty, rdf:Property ;
rdfs:label
"hombres"#es,
"men"#en ;
rdfs:comment
"Número de estudiantes hombres."#es,
"Number of male students."#en ;
rdfs:range xsd:nonNegativeInteger ;
rdfs:isDefinedBy <http://cabas.ugr.es/ontology/ugr#> ;
owl:sameAs <http://cabas.ugr.es/ontology/ugr#hombres> ;
owl:inverseOf <http://cabas.ugr.es/ontology/ugr#mujeres> ;
ns1:term_status "stable" .
<http://cabas.ugr.es/ontology/ugr#mujeres>
a owl:DatatypeProperty, owl:FunctionalProperty, rdf:Property ;
rdfs:label
"mujeres"#es,
"women"#en ;
rdfs:comment
"Número de estudiantes mujeres."#es,
"Number of female students."#en ;
rdfs:range xsd:nonNegativeInteger ;
rdfs:isDefinedBy <http://cabas.ugr.es/ontology/ugr#> ;
owl:sameAs <http://cabas.ugr.es/ontology/ugr#mujeres> ;
owl:inverseOf <http://cabas.ugr.es/ontology/ugr#hombres> ;
ns1:term_status "stable" .
I have a SPARQL endpoint mounted on Virtuoso (http://cabas.ugr.es:8890/sparql), in which I enter for example the following query:
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX ugr: <http://cabas.ugr.es/ontology/ugr#>
SELECT ?X ?titulacion ?rama ?hombres ?mujeres
WHERE {
?X ugr:Titulación ?titulacion .
?X ugr:RamaConocimiento ?rama .
?X ugr:hombres ?hombres .
?X ugr:mujeres ?mujeres
}
(Which would correspond with this link)
It returns all the records, but the fields "hombres" and "mujeres" returns them to me as if it were a string instead of a numeric value, so for example it is impossible to apply a filter like FILTER (?hombres > 500). Any idea what I'm wrong about?
By the way, the ontology and the resource with the values are accessible through these links:
Ontology:
Turtle Format:
http://cabas.ugr.es/ontology/ugr
http://cabas.ugr.es/ontology/ugr.ttl
RDF/XML Format:
http://cabas.ugr.es/ontology/ugr.rdf
Resource:
Turtle Format:
http://cabas.ugr.es/resources/MatriculasGrado1516
http://cabas.ugr.es/resources/matriculas_grado_1516.ttl
RDF/XML Format:
http://cabas.ugr.es/resources/matriculas_grado_1516.rdf
In order to treat the numbers as numbers, you need to define them as such.
Right now you define them as strings:
<http://cabas.ugr.es/resources/MatriculasGrado1516#21>
ns0:hombres "91" ;
ns0:mujeres "68" .
To define them as integers, you need to set their type to xsd:integer:
<http://cabas.ugr.es/resources/MatriculasGrado1516#21>
ns0:hombres "91"^^xsd:integer ;
ns0:mujeres "68"^^xsd:integer .
Strings can also be cast to integer in queries, if needed. For example:
FILTER(xsd:integer(?hombres) > 500)

SPARQL: query for a complete list

I have such a query:
CONSTRUCT {
?p a :IndContainer .
?p :contains ?ind .
} WHERE{
:ClassContainer_1 :contains ?class .
?ind a ?class .
BIND (IRI(...) AS ?p) .
}
An individual ClassContainer_1 relates to some classes. I get this classes and try to find individuals for these classes. Then I try to create an IndContainer that should store found individuals (dots are used only for simplification). So, I want to:
Create individual of IndContainer only when individuals for all bindings of ?class have been found;
Create individuals of IndContainer for all possible sets of individuals from ?ind (i.e. when some of ?class has a nuber of individuals).
Is it possible to create such a SPARQL query? Or it is necessary to use some rule engine?
EDIT (add illustration):
Positive example. Have:
test:ClassContainer_1
rdf:type test:ClassContainer ;
test:contains test:Type1 ;
test:contains test:Type2 ;
.
test:Type1_1
rdf:type test:Type1 ;
.
test:Type1_2
rdf:type test:Type1 ;
.
test:Type2_1
rdf:type test:Type2 ;
.
Want to receive:
test:IndContainer_1
rdf:type test:IndContainer ;
test:contains test:Type1_1 ;
test:contains test:Type2_1 ;
.
test:IndContainer_2
rdf:type test:IndContainer ;
test:contains test:Type1_2 ;
test:contains test:Type2_1 ;
.
Negative example: the same as positive except that there is no individuals of class Type2 and so no individuals of IndContainer should be generated.
EDIT 2 (problem essence):
We may look at this problem from the perspective of combination composing. We have two positions (in my example) in each combination. The number of positions is determined by the number of classes each ClassContainer depends on. Each position must be filled in with one individual of a class that correspond to that position. So in my example first position must be filled with one individual of Type1 class, the second - with Type2 class (but the order does not matter). We have two individuals for the first class and one individual for the second class. To get the number of combinations we may use the rule of product from combinatorics - 2*1 = 2, i.e. {Type1_1,Type2_1} - is the first combination and {Type1_2,Type2_1} - is the second combination. For each combination it is necessary to generate IndContainer individual.
If I understand your question correctly, you want a "container" for each class that is contained in a "class container" that contains the individuals that belong to that class. That's not too hard to do, as long as you can construct the IRI of the container from the IRI of the class. Here's some sample data with two classes, A and B, and a few instances (some of just A, some of just B, and some of A and B):
#prefix : <urn:ex:> .
:container a :ClassContainer ;
:contains :A, :B .
:w a :A . # an :A
:x a :A . # another :A
:y a :B . # a :B
:z a :A, :B . # both an :A and a :B
You query is already pretty close. Here's one that works, along with its result:
prefix : <urn:ex:>
construct {
?indContainer a :IndContainer ;
:contains ?ind .
}
where {
:container a :ClassContainer ;
:contains ?class .
?ind a ?class .
bind(IRI(concat(str(?class),"-container")) as ?indContainer)
}
#prefix : <urn:ex:> .
:B-container a :IndContainer ;
:contains :y , :z .
:A-container a :IndContainer ;
:contains :w , :x , :z .