npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@landmaes/shacl-rule-1-2-parser

v1.0.2

Published

A Traqula-based parser for SHACL-RULE syntax

Downloads

326

Readme

Query Rewriting SPARQL 1.2 over RDF 1.1

We will be using SPARQL CONSTRUCT queries to express the mapping between RDF 1.1 and RDF 1.2. img.png

Query rewrites like this are called GAV, LAV and GLAV in literature. See: Principles of Data Integration - AnHai Doan - Alon Halevy - Zachary Ives NOTE: in the construct template/ mapping head, you cannot share non-existential atoms (bnodes) between triples. (unless you bind to them through your bgp (they map to a source bnode))

There is a working draft for a spec describing the mapping between RDF1.1 and RDF 1.2: RDF1.2 interoperability

Example data expressed in Turtle 1.2

Sparql 1.2 rewrite

The intention is that you have a SPARQL 1.2 query and mapping using construct queries. The body of this mapping should not contain any SPARQL 1.2 syntax, but the head can contain triple terms. Using this approach means that you cannot rewrite recursive triple terms for arbitrary depth, you will need a construct for every depth you want to support. What our rewriter will do is map each triple pattern in your BGP to a list of unions for each construct over some selects. When you are solving the variables of your mapping head, it should be noted that triple term binding appears outside the SUB-SELECTS, thereby creating a toplevel query that can use SPARQL 1.2 construct, but does not contain any BGPs, and a bunch of subselects that only use SPARQL1.1 and do contain the BGPs.

RDF 1.2 examples

:me :name "jitse" ~ :t {| :statedBy :govBE |}

-- RDF 1.2 spec ->

:me :name "jitse" .
< :me :name "jitse" ~ :t > :satatedBy :govBE

-- RDF 1.2 spec ->

:me :name "jitse"
:t rdf:reifies <<( :me :name "jitse" )>>
:t :statedBy :govBE

Interop spec/ RDF reification

-- to RDF1.1 ->

:me :name "jitse" .
:t :rdf:reifies _:temp .
:t :statedBy :govBE .

_:temp a rdftripleTerm .
_:temp rdf:ttSubject :me .
_:temp rdf:ttPredicate :name .
_:temp rdf:ttObject "jitse" .

construct to go back:

CONSTRUCT {
    ?t rdf:reifies <<( ?s ?p ?o )>>
} WHERE {
    ?t rdf:reifies [
        a rdf:tripleTerm ;
        rdf:ttSubject ?s ;
        rdf:ttPredicate ?p ;
        rdf:ttObject ?o ;
    ]
}
CONSTRUCT {
    ?s ?p ?o .
} WHERE {
    ?s ?p ?o .
    # Next filter is not needed since in 1.1 the function does not exist
    FILTER ( !isTripleTerm(?o)) .
    FILTER ( ?p != "rdf:reifies" && NOT EXISTS {
        ?sRoot rdf:reifies ?s .
    })
}

Construct to go to: (Because 2 ways, can do GLAV)

CONSTRUCT {
    ?t rdf:reifies [
        a rdf:tripleTerm ;
        rdf:ttSubject ?s ;
        rdf:ttPredicate ?p ;
        rdf:ttObject ?o ;
    ]
} WHERE {
    ?t rdf:reifies <<( ?s ?p ?o )>>
}

Singleton Property

-- to RDF 1.1 ->

:me :name "jitse"
:me :name#1 "jitse"
:name#1 rdf:singletonProperyOf :name ;
        :statedBy :govBE .

Construct to go back:

CONSTRUCT {
    ?p rdf:reifies <<( ?s ?trueProp ?o )>>
} WHERE {
    ?s ?p ?o .
    ?p rdf:singletonPropertyOf ?trueProp .
}

Named Graphs

Either:

  1. trust the graph has only one triple,
  2. use one subject multiple times to reify many triples,
  3. Annotate the graph has only one triple (could also use a count subquery?)

-- to RDF1.1 ->

:me :name "jitse"
_:temp { :me :name "jitse" }
_:temp :statedBy :govBE .

Construct to go back:

CONSTRUCT {
    ?t rdf:reifies <<( ?s ?p ?o )>> ; ?p1 ?o1 .
} WHERE {
    GRAPH ?t { ?s ?p ?o } .
    ?t ?p1 ?o1 .
    OPTIONAL { ?t a some:reificationGraph }
}

Construct to go back with a check for only one triple

CONSTRUCT {
    ?t rdf:reifies <<( ?s ?p ?o )>> ; ?p1 ?o1 .
} WHERE {
    {
        SELECT ?t WHERE {
            GRAPH ?t { ?s ?p ?o }
        } GROUP BY (?t) having (count(*) = 1)
    }
    GRAPH ?t { ?s ?p ?o } .
    ?t ?p1 ?o1 .
}

N-ary

(used by Wikidata under the prefixes, p(property), ps(property statement) and wdt(property direct))

-- to RDF 1.1 ->

:me :name "jitse" .
:me :nameP _:temp .
_:temp :statedBy :govBE .
_:temp :namePs "jitse" .

# Made up properties...
:nameP :hasDirectProp :name .
:nameP :hasPropertyStatement ?ps .

Construct to go back:

CONSTRUCT {
    ?rel rdf:reifies <<( ?s ?trueProp ?o )>> ; ?p1 ?o1 .
} WHERE {
    ?s ?p ?rel .
    ?rel ?p1 ?o1 ;
         ?ps ?o .

     ?p :hasDirectProp :name ;
        :hasPropertyStatement ?ps ;
}

Matchers

You match recursive triples: ?rel rdf:reifies <<( :me :name ?name )>> => (?rel, rdf:reifies, (:me, :name, ?name)) '' ('' = DefaultGraph)

Why you need a solver:

Take rewrite head: ?t rdf:reifies <<( ?s ?p ?o )>> With query: ?s1 ?s1 <<( ?s1 ?p1 ?o1 )>> Your solver will now be able to say conclude:

?t -> rdf:reifies
?s -> rdf:reifies
?p -> p1
?o -> o1

Quirks documented

Empty groups emit a single binding that does not bind to anything (proof):

  • SELECT * {} gives 1 binding (query)
  • SELECT * { {} UNION {} } gives 2 bindings (query)

Therefore, a mapping that does not match under a union does NOT produce the empty group. It does not produce results. To visualize this, one can also use the group: { FILTER(false) } (query)