Search code examples
sparqlrdfowldirected-acyclic-graphs

Is there a way to store SPARQL queries as instances in an RDF file?


I am creating a database which encodes directed acyclic graphs (DAGs), where nodes are RDF instances and edges are object properties. Ultimately, I would like to create SPARQL queries which match graph patterns in my set of DAGs. There are many (~200) subtree patterns that I am interested in and would like to somehow store these queries to execute later.

The SPARQL below is a toy example of selecting instances of DAGs having a set of nodes and connections:

SELECT ?dag
WHERE {
?dag :has_node ?n1 ;
     :has_node ?n2 ;
     :has_node ?n3 .
?n1 rdfs:type :Type1 ;
    :parent_of ?n2 .
?n2 rdfs:type :Type2 ;
    :parent_of ?n3 .
?n3 rdfs:type :Type3 .

}

Is it possible to store SPARQL queries like the one above as instances in RDF? If so, can I refer to the instance in a SPARQL query and have it translated into SPARQL? (See below)

SELECT ?dag
WHERE {
?dag :has_graph_pattern ?graphPattern .
}

The ?graphPattern variable would be an instance that would encode the same thing as the first SPARQL query.

Thanks for any feedback!


Solution

  • As suggested in @Roos' answer, I was able to encode my subgraphs in SHACL rules and use them to create triples relating DAGs and subgraphs.

    In the example below, there are two DAGs—DAG1 and DAG2—whose nodes can have three possible node labels: A, B, or C. For this example, I want to find DAGs with the C<--B-->C pattern, and then create a new triple linking that DAG with an instance representing this subgraph:

    ex:DAG ex:has_subgraph ex:subGraph1
    

    This pattern I'm matching is highlighted in yellow in the image below:

    DAGs in toy Dataset

    The subgraph pattern can be encoded into a SHACL rule shown below in TTL. Be sure to have sh:declare statements preceding rules, as this may cause issues with the SHACL rule inferencer you use. This is stored in a file called dagShapes.ttl:

    @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
    @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
    @prefix sh: <http://www.w3.org/ns/shacl#> .
    @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
    @prefix ex: <http://example.com/ns#> .
    
    ex:
            sh:declare [
                    sh:prefix "ex" ;
                    sh:namespace "http://example.com/ns#"^^xsd:anyURI ;
            ] .
    
    ex:DAGSubGraphRule1
            a sh:NodeShape ;
            sh:targetClass ex:DAG ;
            sh:rule [
                    a sh:SPARQLRule ;
                    sh:prefixes ex: ;
                    sh:construct """ 
                            CONSTRUCT {
                                    $this ex:has_subgraph ex:subGraph1 .
                            }
                            WHERE {
                                    $this ex:has_node ?n1 ;
                                          ex:has_node ?n2 ;
                                          ex:has_node ?n3 .
    
                                    ?n1 a ex:nodeType_B ;
                                            ex:parent_of ?n2 ;
                                            ex:parent_of ?n3 .
                                    ?n2 a ex:nodeType_C .
                                    ?n3 a ex:nodeType_C .
    
                            }
                            """ ;
            ] ;
    .
    
    

    The DAGs in this example are encoded in their own file called dagData.ttl like this:

    @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
    @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
    @prefix sh: <http://www.w3.org/ns/shacl#> .
    @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
    @prefix ex: <http://example.com/ns#> .
    
    ex:DAG1
            a ex:DAG ;
            ex:has_node ex:d1_n1 ;
            ex:has_node ex:d1_n2 ;
            ex:has_node ex:d1_n3 ;
            ex:has_node ex:d1_n4 ;
            ex:has_node ex:d1_n5 .
    
    ex:d1_n1 a ex:nodeType_A ;
            ex:parent_of ex:d1_n2 .
    ex:d1_n2 a ex:nodeType_A ;
            ex:parent_of ex:d1_n3 .
    ex:d1_n3 a ex:nodeType_B ;
            ex:parent_of ex:d1_n4 ;
            ex:parent_of ex:d1_n5 .
    ex:d1_n4 a ex:nodeType_C .
    ex:d1_n5 a ex:nodeType_C .
    
    ex:DAG2
            a ex:DAG ;
            ex:has_node ex:d2_n1 ;
            ex:has_node ex:d2_n2 ;
            ex:has_node ex:d2_n2 ;
            ex:has_node ex:d2_n2 ;
            ex:has_node ex:d2_n2 .
    
    ex:d1_n1 a ex:nodeType_A ;
            ex:parent_of ex:d1_n2 .
    ex:d1_n2 a ex:nodeType_A ;
            ex:parent_of ex:d1_n3 .
    ex:d1_n3 a ex:nodeType_B ;
            ex:parent_of ex:d1_n4 .
    ex:d1_n4 a ex:nodeType_C ;
            ex:parent_of ex:d1_n5 .
    ex:d1_n5 a ex:nodeType_C .
    
    

    The DAG file and the subgraph shapes file are then passed to a SHACL rule inference tool. The inferencer is run like this in the command line:

    shaclinfer -datafile dagData.ttl -shapesfile dagShapes.ttl
    

    The output from the inferencer shows that DAG1 was correctly associated with my subgraph pattern!

    @prefix dash:    <http://datashapes.org/dash#> .
    @prefix ex:      <http://example.com/ns#> .
    @prefix graphql: <http://datashapes.org/graphql#> .
    @prefix owl:     <http://www.w3.org/2002/07/owl#> .
    @prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
    @prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
    @prefix sh:      <http://www.w3.org/ns/shacl#> .
    @prefix swa:     <http://topbraid.org/swa#> .
    @prefix tosh:    <http://topbraid.org/tosh#> .
    @prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
    
    ex:DAG1  ex:has_subgraph  ex:subGraph1 .