Search code examples
sparqlrdfvirtuoso

How to count references in SPARQL?


I want to find the number of times that each subject returned by this query is the object of another triple.

I can use the following example to select everything I want without the count that I want as well.

SELECT DISTINCT
    ?subject
    ?displayId
    ?version
    ?name
    ?description
    ?type
WHERE {
    { ?subject a sbol2:ComponentDefinition } UNION 
    { ?subject a sbol2:ModuleDefinition } UNION 
    { ?subject a sbol2:Collection } UNION 
    { ?subject a sbol2:Sequence } UNION 
    { ?subject a sbol2:Model } .

    ?subject a ?type
    OPTIONAL { ?subject sbol2:displayId ?displayId . }
    OPTIONAL { ?subject sbol2:version ?version . }
    OPTIONAL { ?subject dcterms:title ?name . }
    OPTIONAL { ?subject dcterms:description ?description . }
}

LIMIT 50

I can add in the following criteria to limit my query to things that are only related to an example object, in this case, the object with URI <http://localhost:7777/public/test/U49845/1>.

{ ?use sbol2:definition <http://localhost:7777/public/test/U49845/1> .    
    { ?subject sbol2:module ?use } UNION 
    { ?subject sbol2:component ?use } UNION 
    { ?subject sbol2:functionalComponent ?use } 
} UNION 
{ ?subject sbol2:model <http://localhost:7777/public/test/U49845/1> } UNION 
{ ?subject sbol2:sequence <http://localhost:7777/public/test/U49845/1> } .

Is there a way to get the number of items that the second query would return for every ?subject in the first query?


Solution

  • General Cleanup

    Alternative objects or subjects

    First, note that this union block

    { ?subject a sbol2:ComponentDefinition } UNION 
    { ?subject a sbol2:ModuleDefinition } UNION 
    { ?subject a sbol2:Collection } UNION 
    { ?subject a sbol2:Sequence } UNION 
    { ?subject a sbol2:Model } .
    

    can be much shorter using values:

    values ?type { sbol2:ComponentDefinition sbol2:ModuleDefinition
                   sbol2:Collection sbol2:Sequence sbol2:Model }
    ?subject a ?type
    

    Alternative properties

    Similarly, if you've got a bunch of alternative properties, just use a property path. This union:

    { ?subject sbol2:module ?use } UNION 
    { ?subject sbol2:component ?use } UNION 
    { ?subject sbol2:functionalComponent ?use }
    

    is equivalent to

    ?subject sbol2:module|sbol2:component|sbol2:functionalComponent ?use
    

    The specific issue

    So, turning the query you came up with into legal SPARQL by adding the required group by, you end up with:

    SELECT DISTINCT
        ?subject ?displayId ?version ?name ?description ?type
        (COUNT(DISTINCT ?user) as ?uses)
    WHERE {
        #-- Find users of the subject.  
        ?user ((sbol2:module|sbol2:component|sbol2:functionalComponent)
                /sbol2:definition)|sbol2:model|sbol2:sequence ?subject .
    
        #-- Find subjects
        values ?stype { sbol2:ComponentDefinition sbol2:ModuleDefinition
                       sbol2:Collection sbol2:Sequence sbol2:Model }
        ?subject a ?stype .
    
        #-- Find other types and properties of the ?subject
        ?subject a ?type .
        OPTIONAL { ?subject sbol2:displayId ?displayId . }
        OPTIONAL { ?subject sbol2:version ?version . }
        OPTIONAL { ?subject dcterms:title ?name . }
        OPTIONAL { ?subject dcterms:description ?description . }
    }
    GROUP BY ?subject ?displayId ?version ?name ?description ?type
    ORDER BY DESC(?uses)