Search code examples
filtersparqlprotege

SPARQL - find and/or filter relations between 3 persons


I have an ontology (built in Protege) with several Family members. There can be good (yellow) or bad (orange) relationships between persons like in this image:

good and bad family relationships

All good and bad relations are symmetric!

I'm trying to find all "triangle" relationships between all persons. The problem is I get too many results, and I can't find a way to reduce the result with the FILTER function.

My SPARQL query:

SELECT *
WHERE {
{
     ?p1 g:hasGoodRel ?p2 . 

     OPTIONAL {?p1 g:hasBadRel  ?p3 . }
     OPTIONAL {?p2 g:hasBadRel  ?p3 . }
    }
}

The result is this:

:D  :C  :E  
:D  :D  :E  
:C  :D  :E  <-- 
:C  :D  :A  
:C  :B  :E  
:C  :B  :A  <--
:B  :C

I just want this 2 results (triangles)

:A  :B  :C  
:C  :D  :E

I tried different Filter combinations like this

 FILTER(?p1 != ?p2) 
 FILTER(?p1 != ?p3)
 FILTER(?p1 < ?p2)

But never got my desired result... I hope some one can point me in the right direction.

//EDIT Here is the example as turtle

@prefix : <http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xml: <http://www.w3.org/XML/1998/namespace> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@base <http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186> .

<http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186> rdf:type owl:Ontology .

#################################################################
#    Object Properties
#################################################################

###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#hasBadRel
:hasBadRel rdf:type owl:ObjectProperty ;
           rdfs:subPropertyOf owl:topObjectProperty ;
           rdf:type owl:SymmetricProperty .


###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#hasGoodRel
:hasGoodRel rdf:type owl:ObjectProperty ;
            rdfs:subPropertyOf owl:topObjectProperty ;
            rdf:type owl:SymmetricProperty .


#################################################################
#    Classes
#################################################################

###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#Person
:Person rdf:type owl:Class .


#################################################################
#    Individuals
#################################################################

###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#A
:A rdf:type owl:NamedIndividual ,
            :Person ;
   :hasBadRel :B ,
              :C ,
              :G .


###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#B
:B rdf:type owl:NamedIndividual ,
            :Person ;
   :hasBadRel :A ;
   :hasGoodRel :C ,
               :G .


###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#C
:C rdf:type owl:NamedIndividual ,
            :Person ;
   :hasBadRel :A ,
              :E ;
   :hasGoodRel :B ,
               :D .


###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#D
:D rdf:type owl:NamedIndividual ,
            :Person ;
   :hasBadRel :E ;
   :hasGoodRel :C .


###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#E
:E rdf:type owl:NamedIndividual ,
            :Person ;
   :hasBadRel :C ,
              :D .


###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#F
:F rdf:type owl:NamedIndividual ,
            :Person ;
   :hasBadRel :C ,
              :D .


###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#G
:G rdf:type owl:NamedIndividual ,
            :Person ;
   :hasBadRel :A ;
   :hasGoodRel :B .


###  Generated by the OWL API (version 4.2.8.20170104-2310) https://github.com/owlcs/owlapi

### Update 2 ###

Here are the results of my second test based on this graph:

Test graph

Sparql query by AKSW

:A  :B  :E  
:A  :B  :F  
:A  :C  :E  
:B  :D  :E
:C  :D  :E

and the query by Stanislav Kralin

:A  :B  :E  
:A  :B  :F
:A  :C  :E  
:C  :D  :E  

The difference is only :B :D :E

So both queries are working as intended. Many thanks again.

If someone wants to test this graph, here is the ontology

@prefix : <http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xml: <http://www.w3.org/XML/1998/namespace> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@base <http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186> .

<http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186> rdf:type owl:Ontology .

#################################################################
#    Object Properties
#################################################################

###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#hasBadRel
:hasBadRel rdf:type owl:ObjectProperty ;
           rdfs:subPropertyOf owl:topObjectProperty .


###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#hasGoodRel
:hasGoodRel rdf:type owl:ObjectProperty ;
            rdfs:subPropertyOf owl:topObjectProperty .


#################################################################
#    Classes
#################################################################

###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#Person
:Person rdf:type owl:Class .


#################################################################
#    Individuals
#################################################################

###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#A
:A rdf:type owl:NamedIndividual ,
            :Person ;
   :hasBadRel :E ;
   :hasGoodRel :B ,
               :C .


###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#B
:B rdf:type owl:NamedIndividual ,
            :Person ;
   :hasBadRel :D ,
              :E ;
   :hasGoodRel :A .


###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#C
:C rdf:type owl:NamedIndividual ,
            :Person ;
   :hasGoodRel :E .


###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#D
:D rdf:type owl:NamedIndividual ,
            :Person ;
   :hasBadRel :B ;
   :hasGoodRel :C .


###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#E
:E rdf:type owl:NamedIndividual ,
            :Person ;
   :hasBadRel :A ,
              :B ,
              :D .


###  http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#F
:F rdf:type owl:NamedIndividual ,
            :Person ;
   :hasBadRel :A ,
              :B .


###  Generated by the OWL API (version 4.2.8.20170104-2310) https://github.com/owlcs/owlapi

Solution

  • According to this comment, relationships should be "heterogeneous", i. e. both good and bad relations should be present in a triangle. Thus, your query could be:

    PREFIX pref: <http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#> 
    
    SELECT DISTINCT ?s1 ?s2 ?s3
        WHERE { 
               {
               ?s1 pref:hasGoodRel|^pref:hasGoodRel ?s2 . 
               ?s1 pref:hasGoodRel|^pref:hasGoodRel ?s3 . 
               ?s2 pref:hasBadRel|^pref:hasBadRel ?s3 . 
               }
               UNION
               {
               ?s1 pref:hasBadRel|^pref:hasBadRel ?s2 . 
               ?s1 pref:hasBadRel|^pref:hasBadRel ?s3 . 
               ?s2 pref:hasGoodRel|^pref:hasGoodRel ?s3 .
               }
               FILTER (str(?s2) < str(?s3))
              }
    

    The idea is that there are only two kinds of 'situations': 'two good, one bad' and 'two bad, one good'.

    In every 'situation', ?s1 is bound univocally, whereas ?s2 and ?s3 are not. Then the FILTER clause forces univocal bindings for ?s2 and ?s3.


    Without using UNION, it looks difficult to figure out appropriate FILTER condition (check that the condition from AKSW's answer is inappropriate in this case). However it is possible.

    PREFIX pref: <http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#> 
    
    SELECT DISTINCT ?s1 ?s2 ?s3 WHERE { 
        ?s1 pref:hasGoodRel|^pref:hasGoodRel ?s2 . 
        ?s1 pref:hasBadRel|^pref:hasBadRel ?s3 . 
        ?s2 pref:hasBadRel|^pref:hasBadRel|pref:hasGoodRel|^pref:hasGoodRel ?s3 . 
        BIND ((str(?s1) > str(?s2)) AS ?x) .
        BIND ((str(?s2) > str(?s3)) AS ?y) .
        BIND ((str(?s1) < str(?s3)) AS ?z) .
        FILTER (( ?x || ?y ) && ( ?x || ?z ) && ( ?y || ?z ))
        } ORDER BY ASC(?s1)
    

    Here below complete RDF test data. A correct query should return 12 results: exactly one result for every letter A–L.

    @prefix  pref: <http://www.semanticweb.org/anato/ontologies/2017/7/untitled-ontology-186#> 
    
    pref:A1 pref:hasGoodRel pref:A2 .
    pref:A1 pref:hasBadRel pref:A3 .
    pref:A2 pref:hasBadRel pref:A3 .
    
    pref:B1 pref:hasBadRel pref:B2 .
    pref:B1 pref:hasGoodRel pref:B3 .
    pref:B2 pref:hasBadRel pref:B3 .
    
    pref:C1 pref:hasGoodRel pref:C2 .
    pref:C1 pref:hasBadRel pref:C3 .
    pref:C2 pref:hasBadRel pref:C3 .
    
    pref:D1 pref:hasBadRel pref:D2 .
    pref:D1 pref:hasGoodRel pref:D3 .
    pref:D2 pref:hasBadRel pref:D3 .
    
    pref:E1 pref:hasBadRel pref:E2 .
    pref:E1 pref:hasBadRel pref:E3 .
    pref:E2 pref:hasGoodRel pref:E3 .
    
    pref:F1 pref:hasBadRel pref:F2 .
    pref:F1 pref:hasBadRel pref:F3 .
    pref:F2 pref:hasGoodRel pref:F3 .
    
    #################
    
    pref:G1 pref:hasBadRel pref:G2 .
    pref:G1 pref:hasGoodRel pref:G3 .
    pref:G2 pref:hasGoodRel pref:G3 .
    
    pref:H1 pref:hasGoodRel pref:H2 .
    pref:H1 pref:hasBadRel pref:H3 .
    pref:H2 pref:hasGoodRel pref:H3 .
    
    pref:I1 pref:hasBadRel pref:I2 .
    pref:I1 pref:hasGoodRel pref:I3 .
    pref:I2 pref:hasGoodRel pref:I3 .
    
    pref:J1 pref:hasGoodRel pref:J2 .
    pref:J1 pref:hasBadRel pref:J3 .
    pref:J2 pref:hasGoodRel pref:J3 .
    
    pref:K1 pref:hasGoodRel pref:K2 .
    pref:K1 pref:hasGoodRel pref:K3 .
    pref:K2 pref:hasBadRel pref:K3 .
    
    pref:L1 pref:hasGoodRel pref:L2 .
    pref:L1 pref:hasGoodRel pref:L3 .
    pref:L2 pref:hasBadRel pref:L3 .
    

    To be honest, I have generated all possible FILTER conditions of particular kind using Wolfram Cloud Mathematica and then check them semi-automatically.

    Do[Print[BooleanMinimize[BooleanFunction[i, {x,y,z}]]], {i, 1, 255, 1}]