1

Given are Objects :A, :B and :C which have properties assigned, whereas these properties are not scalar themselves but are also objects with key and value properties.

@prefix x: <http://example.com/example#> 

x:A x:hasProp x:Prop1 .
x:Prop1 x:Key "1" .
x:Prop1 x:Value "AA" .
x:B x:hasProp x:Prop2 .
x:Prop2 x:Key "1" .
x:Prop2 x:Value "AA" .
x:C x:hasProp x:Prop3 .
x:C x:hasProp x:Prop4 .
x:Prop3 x:Key "1" .
x:Prop3 x:Value "AA" .
x:Prop4 x:Key "2" .
x:Prop4 x:Value "BB" .

How can I assert that :A and :B have the same properties whereas :A and :C not? I'm new to SPARQL and I have no idea... I tried something like :

prefix x: <http://example.com/example#> 

select ?another ?k ?v
{x:A x:hasProp ?p .
?p ?k ?v .
?another x:hasProp ?p2 .   
?p2 ?k ?v .
}

but I think it's a wrong way. It also returns :C.

How can one easily compare two sets in SPARQL?


Additional question: The query from the answer 1 works fine but only if :A used directly. Using a variable in place of :A makes :C also to qualify. Why?

I mean: insert some condition to find :A

prefix : <http://example.com/example#> 

INSERT DATA {:A rdfs:label "A"}

and then use a variable in place of :A

prefix : <http://example.com/example#> 

select ?other ?k ?v {
  #-- Find ?other such that :A and ?other have
  #-- some property in common,
  ?a rdfs:label "A"
  ?a     :hasProp [ :Key ?k ; :Value ?v ] .
  ?other :hasProp [ :Key ?k ; :Value ?v ] .

  #-- but remove any ?other such that:
  filter not exists { 
#-- (i) :A has a property that ?other doesn't; 
  {
    ?a :hasProp [ :Key ?kk ; :Value ?vv ] .
    filter not exists { ?other :hasProp [ :Key ?kk ; :Value ?vv ] .
     }
  }
  union
  #-- or (ii) ?other has a property that :A doesn't.
  {
    ?other :hasProp [ :Key ?kk ; :Value ?vv ] .
    filter not exists {
         ?a :hasProp [ :Key ?kk ; :Value ?vv ] .
   }
 }
}  
}

Update:

prefix : <http://example.com/example#> 

INSERT DATA {
:A rdfs:label "A" .
:A :hasProp :Prop1 .
:Prop1 :Key "1" .
:Prop1 :Value "AA" .

:B :hasProp :Prop2 .
:Prop2 :Key "1" .
:Prop2 :Value "AA" .

:C :hasProp :Prop3 .
:C :hasProp :Prop4 .
:Prop3 :Key "1" .
:Prop3 :Value "AA" .

:Prop4 :Key "2" .
:Prop4 :Value "BB" .
}

Query using :A

prefix : <http://example.com/example#> 

select ?other ?k ?v {
   :A    :hasProp [ :Key ?k ; :Value ?v ] .
   ?other :hasProp [ :Key ?k ; :Value ?v ] .
   filter not exists { 
     { :A :hasProp [ :Key ?kk ; :Value ?vv ] .
       filter not exists { ?other :hasProp [ :Key ?kk ; :Value ?vv ] .
       }
     }
     union
     {
      ?other :hasProp [ :Key ?kk ; :Value ?vv ] .
      filter not exists { :A :hasProp [ :Key ?kk ; :Value ?vv ] .
      }
   }
  }
 }

Answer:

 -------------------  
 |other|  k  | v  
 |A    | "1" | "AA"  
 |B    | "1" | "AA"  
 -------------------  

Query using variable ?a:

 prefix : <http://example.com/example#> 

select ?other ?k ?v {
   ?a rdfs:label "A" .
   ?a    :hasProp [ :Key ?k ; :Value ?v ] .
   ?other :hasProp [ :Key ?k ; :Value ?v ] .
   filter not exists { 
     { ?a :hasProp [ :Key ?kk ; :Value ?vv ] .
       filter not exists { ?other :hasProp [ :Key ?kk ; :Value ?vv ] .
       }
     }
     union
     {
      ?other :hasProp [ :Key ?kk ; :Value ?vv ] .
      filter not exists { ?a :hasProp [ :Key ?kk ; :Value ?vv ] .
      }
    }
   }
 }

returns

other k v
A "1" "AA"
B "1" "AA"
C "1" "AA"

Andrej
  • 118
  • 7
  • What I'm trying to do: find all objects, that are similar to :A in that they have EXACTLY the same SET of properties in terms of Key/Value. :B must qualify and :C not because :C has one property more (Key=2, Value = BB). I understand that my query should also return :C, that's why I'm asking for the "right" one according to the requirements. – Andrej Jul 12 '14 at 20:59
  • Yes, exactly as much and exactly the same key/value pairs – Andrej Jul 12 '14 at 21:14
  • OK, I just updated my answer. – Joshua Taylor Jul 12 '14 at 21:16
  • You may also find [Set difference in SPARQL](http://stackoverflow.com/q/21391444/1281433) useful. – Joshua Taylor Jul 12 '14 at 21:27

1 Answers1

2

Why your query doesn't return what you want…

It always helps to show the output that you're actually getting, because then you can point out which parts you're not expecting. In this case, your query returns:

---------------------------
| another | k      | v    |
===========================
| :C      | :Value | "AA" |
| :C      | :Key   | "1"  |
| :B      | :Value | "AA" |
| :B      | :Key   | "1"  |
| :A      | :Value | "AA" |
| :A      | :Key   | "1"  |
---------------------------

This makes sense in your case because C does have the kind of data you're asking about. It's this portion of your data:

x:A x:hasProp x:Prop1 .
x:Prop1 x:Key "1" .
x:Prop1 x:Value "AA" .
…
x:C x:hasProp x:Prop3 .
…
x:Prop3 x:Key "1" .
x:Prop3 x:Value "AA" .

A query that returns what you want

Here's how I'd write a query that gets you what you want. It's easy enough to find things that have some property in common with :A. From those, you need to filter out any ?other such that either :A has a property that ?other doesn't, or such that ?other has a property that :A doesn't,

prefix : <http://example.com/example#> 

select ?other ?k ?v {
  #-- Find ?other such that :A and ?other have
  #-- some property in common,
  :A     :hasProp [ :Key ?k ; :Value ?v ] .
  ?other :hasProp [ :Key ?k ; :Value ?v ] .

  #-- but remove any ?other such that:
  filter not exists { 
    #-- (i) :A has a property that ?other doesn't; 
    {
      :A :hasProp [ :Key ?kk ; :Value ?vv ] .
      filter not exists {
        ?other :hasProp [ :Key ?kk ; :Value ?vv ] .
      }
    }
    union
    #-- or (ii) ?other has a property that :A doesn't.
    {
      ?other :hasProp [ :Key ?kk ; :Value ?vv ] .
      filter not exists {
        :A :hasProp [ :Key ?kk ; :Value ?vv ] .
      }
    }
  }
}
----------------------
| other | k   | v    |
======================
| :B    | "1" | "AA" |
| :A    | "1" | "AA" |
----------------------

Enumerating the equivalence classes

In fact, you can use a generalization of this to list the different equivalence classes in the data.

prefix : <http://example.com/example#> 

select distinct ?x ?y {
  ?x :hasProp [] .
  ?y :hasProp [] .

  filter not exists { 
    {
      ?x :hasProp [ :Key ?kk ; :Value ?vv ] .
      filter not exists {
        ?y :hasProp [ :Key ?kk ; :Value ?vv ] .
      }
    }
    union
    {
      ?y :hasProp [ :Key ?kk ; :Value ?vv ] .
      filter not exists {
        ?x :hasProp [ :Key ?kk ; :Value ?vv ] .
      }
    }
  }
}
order by ?x ?y
-----------
| x  | y  |
===========
| :A | :A |
| :A | :B |
| :B | :A |
| :B | :B |
| :C | :C |
-----------
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
  • It works fine in that form, but it's not working for me using a variable in place of :A. Why? – Andrej Jul 13 '14 at 10:47
  • I posted 2 different queries. Which one do you mean when you say "that form"? – Joshua Taylor Jul 13 '14 at 12:18
  • I meant the first query. I've extended my initial question to show this. – Andrej Jul 13 '14 at 13:03
  • Are you sure that your insert worked correctly? The query (without the bit about `rdfs:label`) works just fine on your original data. If you've got a problem with a different situation, I'd advise that you try to construct a minimal working example where you can observe the problem. – Joshua Taylor Jul 13 '14 at 21:26
  • I updated my question to show the data and the two queries. The second one returns also :C. – Andrej Jul 14 '14 at 08:15
  • @Andrej You've already accepted an answer to this question, which means that fewer people are going to be looking at it. You should take the portion that you've edited in and ask a **new question**. Be sure to include details about the triple store and SPARQL engine that you're using. I'm not sure offhand why the second query returns what it does, but as written now, this question really conflates two different issues, and the primary one (how to compare resources by sets of properties) has been answered. – Joshua Taylor Jul 14 '14 at 12:43
  • I agree with you and I'll create a new question – Andrej Jul 14 '14 at 13:06