Path length function?

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

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? :sweat_smile:

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!

Hi Daniel,

Thanks for the kind word, appreciate it!

There's one example at Stored Query Service | Stardog Documentation Latest

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

1 Like

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

image

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

I have two helper functions:

a)

// Stored Query: GetClassPath

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

b)

// 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:

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!

Hi Daniel,

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

@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

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).

Complete example:

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))     
}

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:

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

1 Like

That worked wonders!

Biggest thanks and have a nice weekend!

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