# Path length function?

**URL:** https://community.stardog.com/t/path-length-function/3237
**Category:** Support
**Created:** [September 2, 2021, 5:50pm UTC](https://community.stardog.com/t/path-length-function/3237 "2021-09-02T17:50:25Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![lapidus](https://yyz2.discourse-cdn.com/flex030/user_avatar/community.stardog.com/lapidus/32/1076_2.png) [@lapidus](https://community.stardog.com/u/lapidus)
#### Post date: [September 2, 2021, 5:50pm UTC](https://community.stardog.com/t/path-length-function/3237/1 "2021-09-02T17:50:25Z")

</div>

First of all ... it's amazing to see that Stardog always has a helpful article whatever one googles for ...

I googled "using path queries in subqueries" and landed on "Path queries as subqueries": [Stored Query Service](https://www.stardog.com/labs/blog/stored-query-service/)

Follow-up question ...

It says at the bottom of that post:

> "We added one obvious function — `length` — which simply returns the length of a path. One can use it to get the average path length"

Where do I find this function? 😅

I am trying to get an end result like:

startClass, someSuperClass, length (distance)  
a, b, 1  
a, c, 2  
a, d, 3  
a, e, 4

Thank you!

---

<div class="post-metadata">

### Author: ![pavel](https://yyz2.discourse-cdn.com/flex030/user_avatar/community.stardog.com/pavel/32/18_2.png) [@pavel](https://community.stardog.com/u/pavel)
#### Post date: [September 2, 2021, 5:59pm UTC](https://community.stardog.com/t/path-length-function/3237/2 "2021-09-02T17:59:00Z")

</div>

Hi Daniel,

Thanks for the kind word, appreciate it!

There's one example at [Stored Query Service | Stardog Documentation Latest](https://docs.stardog.com/query-stardog/stored-query-service#path-subqueries:)

```auto
prefix sqs: <tag:stardog:api:sqs:>
prefix stardog: <tag:stardog:api:>

SELECT ?start (avg(stardog:length(?path)) as ?avg_length) {
    SERVICE <query://paths> {
        [] sqs:vars ?start, ?path
    }
} GROUP BY ?start

```

That groups all paths by the start node and computes their average length.

Best,  
Pavel

---

<div class="post-metadata">

### Author: ![lapidus](https://yyz2.discourse-cdn.com/flex030/user_avatar/community.stardog.com/lapidus/32/1076_2.png) [@lapidus](https://community.stardog.com/u/lapidus)
#### Post date: [September 2, 2021, 6:11pm UTC](https://community.stardog.com/t/path-length-function/3237/3 "2021-09-02T18:11:19Z")

</div>

Thank you, that solved it!  
And again, outstanding documentation and articles. 🌟

![image](https://canada1.discourse-cdn.com/flex030/uploads/stardog/original/2X/0/0e9add689135e66ed36324999e8a9ca70cb07620.png)

---

<div class="post-metadata">

### Author: ![lapidus](https://yyz2.discourse-cdn.com/flex030/user_avatar/community.stardog.com/lapidus/32/1076_2.png) [@lapidus](https://community.stardog.com/u/lapidus)
#### Post date: [September 3, 2021, 10:08am UTC](https://community.stardog.com/t/path-length-function/3237/4 "2021-09-03T10:08:30Z")

</div>

@pavel: One more follow up if I may ...

I have two helper functions:

a)

```auto
// Stored Query: GetClassPath

PATHS START ?start END ?end VIA {
    ?start rdfs:subClassOf ?end .
}

```

b)

```auto
// Stored Query: GetClassDistance

prefix sqs: <tag:stardog:api:sqs:>
SELECT ?start ?end (stardog:length(?path) as ?length) {
        service <query://GetClassPath> {
        [] sqs:vars ?path ; sqs:var:start ?start ; sqs:var:end ?end .   
    }
}

```

... and a final query that makes use of the above:

```auto
SELECT ?start ?classSubject ?depth {
        service <query://GetClassDistance> {
        [] sqs:vars ?path ; sqs:var:start ?start ; sqs:var:end ?classSubject ; sqs:var:length ?depth . 
      }
    
        # Which classes do we want to target?
        FILTER(?start IN (:country))  
      }

```

My desired response is:

start, classSubject, depth  
:country, :country, 0  
:country, :place, 1  
:country, :object, 2

(edited)

But I don't get :country as a classSubject unless I do `?start rdfs:subClassOf* ?end .` in the first helper function (note the star).

But if I do that the length function doesn't work. All lengths are set to 1.

So question:  
a) Can I include the starting class in this chain with the length calculation intact?  
b) If it's not possible, is there a way of adding it by UNION/Values to an outer query that uses this?

Happy to demo on a 5-min video call.

Thanks!

---

<div class="post-metadata">

### Author: ![pavel](https://yyz2.discourse-cdn.com/flex030/user_avatar/community.stardog.com/pavel/32/18_2.png) [@pavel](https://community.stardog.com/u/pavel)
#### Post date: [September 3, 2021, 11:36am UTC](https://community.stardog.com/t/path-length-function/3237/5 "2021-09-03T11:36:57Z")

</div>

Hi Daniel,

What's in your data? I just quickly checked on this simplest snippet:

```auto
@prefix : <urn:> .

:country rdfs:subClassOf :place .
:place rdfs:subClassOf :object .

```

and got the expected result. `:country` cannot be a `classSubject` because it's not a super class of anything. It's not a `classSubject` in your desired response either. Am I missing something?

Cheers,  
Pavel

---

<div class="post-metadata">

### Author: ![lapidus](https://yyz2.discourse-cdn.com/flex030/user_avatar/community.stardog.com/lapidus/32/1076_2.png) [@lapidus](https://community.stardog.com/u/lapidus)
#### Post date: [September 3, 2021, 11:55am UTC](https://community.stardog.com/t/path-length-function/3237/6 "2021-09-03T11:55:15Z")

</div>

Argh, apologies. Typo! My desired response (or any workaround solution) should include :country together with the other ones:

start, classSubject, depth  
:country, :country, 0  
:country, :place, 1  
:country, :object, 2

The idea is to be able to operate over :country, :place, :object when asking for SHACL shape in the next step (outside this example code).

---

<div class="post-metadata">

### Author: ![lapidus](https://yyz2.discourse-cdn.com/flex030/user_avatar/community.stardog.com/lapidus/32/1076_2.png) [@lapidus](https://community.stardog.com/u/lapidus)
#### Post date: [September 3, 2021, 11:57am UTC](https://community.stardog.com/t/path-length-function/3237/7 "2021-09-03T11:57:21Z")

</div>

Complete example:

```auto
prefix sqs: <tag:stardog:api:sqs:>

SELECT ?start ?classSubject ?depth ?property ?datatype ?minCount ?maxCount ?class {
    
    # Is there any SHACL shape with class as target?
    # And does that SHACL container have any Property shape objects?
    ?nodeShape sh:targetClass ?classSubject;
               sh:property ?propertyShape .
      
    # What property is the PropertyShape targeting?  
    ?propertyShape sh:path ?property .
 
    # What specific SHACL constraints are defined for that property?  
    OPTIONAL { ?propertyShape sh:minCount ?minCount . }
    OPTIONAL { ?propertyShape sh:maxCount ?maxCount . }
    OPTIONAL { ?propertyShape sh:class ?class . }

    # Start of subquery – runs first to narrow down classes of relevance for shape inheritance
   {
    
    # What are the parent classes?
    # And what is the distance from the classSubject 
    # to the top of the class chain (:meta_root)

      SELECT ?start ?classSubject ?depth {
        service <query://GetClassDistance> {
        [] sqs:vars ?path ; sqs:var:start ?start ; sqs:var:end ?classSubject ; sqs:var:length ?depth . 
      }
    
        # Which classes do we want shape for?
        FILTER(?start IN (:country))  
      }
      
    } 

   # Which properties do we want shape for?
   #FILTER(?property in (:p_wikidata_id))     
}

```

---

<div class="post-metadata">

### Author: ![pavel](https://yyz2.discourse-cdn.com/flex030/user_avatar/community.stardog.com/pavel/32/18_2.png) [@pavel](https://community.stardog.com/u/pavel)
#### Post date: [September 3, 2021, 12:15pm UTC](https://community.stardog.com/t/path-length-function/3237/8 "2021-09-03T12:15:15Z")

</div>

Oh I see. Right, you cannot really force a path query to return paths of zero length.

Here's one workaround, modify your `GetClassDistance` as follows:

```auto
prefix sqs: <tag:stardog:api:sqs:>
SELECT ?start ?end ?length {
    {
       service <query://GetClassPath> {
         [] sqs:vars ?path ; sqs:var:start ?start ; sqs:var:end ?end .   
       }
       bind (stardog:length(?path) as ?length)
    }
    union {
        ?start a owl:Class 
        bind(?start as ?end)
        bind(0 as ?length)
    }
}

```

and make sure each class has a declaration axiom, eg. `:country a owl:Class` (they need to be there anyway according to the OWL spec but this workaround relies on them explicitly).

Cheers,  
Pavel

---

<div class="post-metadata">

### Author: ![lapidus](https://yyz2.discourse-cdn.com/flex030/user_avatar/community.stardog.com/lapidus/32/1076_2.png) [@lapidus](https://community.stardog.com/u/lapidus)
#### Post date: [September 3, 2021, 12:19pm UTC](https://community.stardog.com/t/path-length-function/3237/9 "2021-09-03T12:19:55Z")

</div>

That worked wonders!

Biggest thanks and have a nice weekend!

---

<div class="post-metadata">

### Author: ![system](https://canada1.discourse-cdn.com/flex030/uploads/stardog/original/2X/e/ed66b48a616f505106a4acde3c9cee7e6da9bc67.svg) [@system](https://community.stardog.com/u/system)
#### Post date: [September 17, 2021, 12:20pm UTC](https://community.stardog.com/t/path-length-function/3237/10 "2021-09-17T12:20:09Z")

</div>

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.
