Maxim Veksler
Maxim Veksler

Reputation: 30232

SPARQL query to walk path from given root

What SPARQL syntax allows specifying root node, while consuming relation paths out of this node to a given limit.

For example

Let Graph1 be an anatomical mapping of body parts.

k_anatomy:K403 a k:Anatomy ;
    rdfs:label "Finger" ;
    k_anatomy:has_parent k_anatomy:K393 .

k_anatomy:K393 a k:Anatomy ;
    rdfs:label "Hand" ;
    k_anatomy:has_parent k_anatomy:K370 .

k_anatomy:K370 a k:Anatomy ;
    rdfs:label "Free Upper Limb" ;
    k_anatomy:has_parent k_anatomy:K359 .


k_anatomy:K359 a k:Anatomy ;
    rdfs:label "Upper Limb" ;
    k_anatomy:has_parent k_anatomy:K358 .


k_anatomy:K358 a k:Anatomy ;
    rdfs:label "Limb" ;
    k_anatomy:has_parent k_anatomy:K2 .


k_anatomy:K2 a k:Anatomy ;
    rdfs:label "Body_by_region" ;
    k_anatomy:has_parent k_anatomy:K1 .


k_anatomy:K1 a k:Anatomy ;
    rdfs:label "Body" ;
    k_anatomy:has_parent k_anatomy:K0 .

Let root be k_anatomy:K403 (rdfs:label "Finger").

Question

What SPARQL query given IRI = k_anatomy:K403 and relation k_anatomy:has_parent will compose the following result:

| BodyPart          | PartOf            | 
-----------------------------------------
| Finger            | Hand              |
| Hand              | Free Upper Limb   |
| Free Upper Limb   | Upper Limb        |
| Upper Limb        | Limb              |
| Limb              | Body_by_region    |
| Body_by_region    | Body              |

Upvotes: 1

Views: 293

Answers (1)

Jeen Broekstra
Jeen Broekstra

Reputation: 22052

There's no need to specify a root node for this. This query:

SELECT ?BodyPart ?PartOf
WHERE { 
    ?S rdfs:label ?BodyPart;
       k_anatomy:has_parent [ rdfs:label ?PartOf ]. 
} 

will do the trick.

I just ran it on a local RDF4J Console. Output:

Evaluating SPARQL query...
+-------------------------------------+-------------------------------------+
| BodyPart                            | PartOf                              |
+-------------------------------------+-------------------------------------+
| "Finger"                            | "Hand"                              |
| "Hand"                              | "Free Upper Limb"                   |
| "Free Upper Limb"                   | "Upper Limb"                        |
| "Upper Limb"                        | "Limb"                              |
| "Limb"                              | "Body_by_region"                    |
| "Body_by_region"                    | "Body"                              |
+-------------------------------------+-------------------------------------+
6 result(s) (36 ms)

One caveat: the order of the solutions here is arbitrary, it just happens to align nicely with your expected results because the RDF4J engine in this case just spits results back out in the order in which they occurred in the original file - but that's not guaranteed to happen every time, and certainly not across triplestore implementations.

Update if your store contains more data than just the input file you provided, then to get the result you want you will need a slightly more complex query. Specifically, you will need to use a so-called "property path" expression. This allows you specify that you want arbitrary-length paths starting from a certain point. Like this:

SELECT ?BodyPart ?PartOf
WHERE { 
     k_anatomy:K393 k_anatomy:has_parent* ?S .
     ?S rdfs:label ?BodyPart;
        k_anatomy:has_parent [ rdfs:label ?PartOf ].  
} 

You still want body parts with their parents (so the last part of the query is identical to the original query), but now we add an extra constraint by means of a property path to say that values for ?S must be, through a path of length zero or more, linked to K393 via a has_parent relation.

Result:

Evaluating SPARQL query...
+-------------------------------------+-------------------------------------+
| BodyPart                            | PartOf                              |
+-------------------------------------+-------------------------------------+
| "Hand"                              | "Free Upper Limb"                   |
| "Free Upper Limb"                   | "Upper Limb"                        |
| "Upper Limb"                        | "Limb"                              |
| "Limb"                              | "Body_by_region"                    |
| "Body_by_region"                    | "Body"                              |
+-------------------------------------+-------------------------------------+
5 result(s) (6 ms)

As you can see, this does not list 'Finger' as a result, as expected - though of course this will only work properly if the has_parent relations do not form a cycle.

Upvotes: 1

Related Questions