0

Assume we have an OWL file which contains follows the following properties with domains and ranges:

Domain   Property    Range
----------------------------
tour     hascountry  country
country  hascity     city
city     hasward     ward
ward     hashouse    house

Using SPARQL, how can I get results "between" the Tour and House classes? That is, properties whose domains and ranges such there's a "path" from Tour to the domain and from the range to House. With just these two classes, how could we find results like the following? It seems like some kind of loop might be necessary, but I don't know how to do that in SPARQL.

|tour    -------- (hascountry) ----- country|
|country -------- (hascity)    ----- city   |
|city    -------- (hasward)    ----- ward   |
|ward    -------- (hashouse)   ----- house  |
Joshua Taylor
  • 80,876
  • 9
  • 135
  • 306

1 Answers1

2

First, it's always easier to work with some real data. We can't write real SPARQL queries against data that we don't have. In the future, please be sure to provide some sample that we can work with. For now, here's some sample data that describes the domains and ranges of the properties that you mentioned. Also note that properties don't connect classes; properties connect individuals. Properties can have domains and ranges, and that provides us with a way to infer additional information about the individuals that are related by the property. Anyhow, here's the data:

@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix : <https://stackoverflow.com/q/29737549/1281433/> .

:hascountry rdfs:domain :tour    ; rdfs:range :country .
:hascity    rdfs:domain :country ; rdfs:range :city .
:hasward    rdfs:domain :city    ; rdfs:range :ward .
:hashouse   rdfs:domain :ward    ; rdfs:range :house .

Now, note that you could get from :tour to :country if you follow the rdfs:domain property backward to :hascountry, and then follow the rdfs:range property forward to :country. In SPARQL, you can write that as a property path:

:tour ^rdfs:domain/rdfs:range :country

If you can follow chains of that property path, you can find all the properties that are "between" :tour and :house:

prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
prefix : <https://stackoverflow.com/q/29737549/1281433/>

select ?domain ?property ?range  where {
  #-- find ?properties that have a domain
  #-- and range...
  ?property rdfs:domain ?domain ;
            rdfs:range ?range .

  #-- where there's a ^rdfs:domain to rdfs:range
  #-- chain from :tour to ?domain...
  :tour (^rdfs:domain/rdfs:range)* ?domain .

  #-- and from ?range to :house.
  ?range (^rdfs:domain/rdfs:range)* :house .
}

-------------------------------------
| domain   | property    | range    |
=====================================
| :ward    | :hashouse   | :house   |
| :city    | :hasward    | :ward    |
| :country | :hascity    | :city    |
| :tour    | :hascountry | :country |
-------------------------------------

Getting the results "in order"

If you want the properties "in order" from the start class to the end class, you can compute the distance from the start class to each property and order by that. You can do that using the technique in my answer to Is it possible to get the position of an element in an RDF Collection in SPARQL?. Here's what it looks like as a SPARQL query:

prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
prefix : <https://stackoverflow.com/q/29737549/1281433/>

select ?domain ?property ?range
       (count(?mid) as ?dist)
where {
  #-- find ?properties that have a domain
  #-- and range...
  ?property rdfs:domain ?domain ;
            rdfs:range ?range .

  #-- where there's a ^rdfs:domain to rdfs:range
  #-- chain from :tour to ?domain...
  :tour (^rdfs:domain/rdfs:range)* ?domain .

  #-- and from ?range to :house.
  ?range (^rdfs:domain/rdfs:range)* :house .

  #-- then, compute the "distance" from :tour
  #-- to the property.  This is based on binding
  #-- ?mid to each class in between them and
  #-- taking the number of distinct ?mid values
  #-- as the distance.
  :tour (^rdfs:domain/rdfs:range)* ?mid .
  ?mid (^rdfs:domain/rdfs:range)* ?domain .
}
group by ?domain ?property ?range
order by ?dist

--------------------------------------------
| domain   | property    | range    | dist |
============================================
| :tour    | :hascountry | :country | 1    |
| :country | :hascity    | :city    | 2    |
| :city    | :hasward    | :ward    | 3    |
| :ward    | :hashouse   | :house   | 4    |
--------------------------------------------

I included ?dist in the select just so we could see the values. You don't have to select it in order to sort by it. You can do this too:

select ?domain ?property ?range {
  #-- ...
}
group by ?domain ?property ?range 
order by count(?mid)

-------------------------------------
| domain   | property    | range    |
=====================================
| :tour    | :hascountry | :country |
| :country | :hascity    | :city    |
| :city    | :hasward    | :ward    |
| :ward    | :hashouse   | :house   |
-------------------------------------
Community
  • 1
  • 1
Joshua Taylor
  • 80,876
  • 9
  • 135
  • 306
  • ty so much. Very detail. Thats what i need :). Can we get result in sequence like class relation? (tour->country--> city-->Ward). I run above query statement, i get result : ward--> city-->tour-->country – Van Lanh Luu Apr 20 '15 at 05:02
  • @VanLanhLuu Yes, you can do it. It's a bit more complicated, but not by much. I've updated my answer. – Joshua Taylor Apr 20 '15 at 12:14
  • Nice job. I was actually looking to get an individual(s) separated by a certain degree, say for ex. :danbry foaf:knows/foaf:knows/foaf:name ?name has 2 degree of separation. – Prathamesh dhanawade Sep 26 '17 at 16:57