Suppose you've got data like this (three books, three authors, two companies (two of the authors work at the same company), and two locations (one for each company)):
@prefix : <https://stackoverflow.com/q/23066695/1281433/>.
:book1 a :Book; :createdBy :author1 .
:book2 a :Book; :createdBy :author2 .
:book3 a :Book; :createdBy :author3 .
:author1 a :Author; :worksFor :company1.
:author2 a :Author; :worksFor :company2.
:author3 a :Author; :worksFor :company2.
:company1 a :Company; :locatedIn :location1.
:company2 a :Company; :locatedIn :location2.
:location1 a :Location.
:location2 a :Location.
Then you can use a query like this to get the results that relate books to locations:
prefix : <https://stackoverflow.com/q/23066695/1281433/>
select ?book ?location where {
?book a :Book; (:|!:)* ?location .
?location a :Location .
}
-----------------------
| book | location |
=======================
| :book1 | :location1 |
| :book2 | :location2 |
| :book3 | :location2 |
-----------------------
The trick here is in using a property path that amounts to a wildcard. Every IRI is either :
or it isn't, so the path :|!:
matches every property. This means that the path (:|!:)*
is a wildcard path. By requiring that ?book
and ?location
have types :Book
and :Location
, respectively, we've found paths from books to locations. See SPARQL: property paths without specified predicates and sparql complete tree from subject for more about property paths with wildcards.
There's a possible problem here, of course. RDF graphs are directed graphs, and anything can be related to anything else. This means that if you have a relation like :location2 :northOf :location1
, then there's now a path from :book3
to :location1
. That might not have been what you were expecting.