The queries operate against the unified RDF graph produced by the
loaders in R/rdf-graph.R and, for query execution, the
combined N-Triples file produced by
scripts/025-export-ntriples.R.
Design choices
The package supports two kinds of question:
- Within-framework questions (such as “which task statements in NICE involve cloud or container technologies?”).
- Across-framework questions (such as “how do role and element counts vary by jurisdiction or sector?”).
The cybed: base vocabulary is the selection path for both. A single
helper call targeting cybed:OrganizingUnit returns
comparable parent-level bindings across every framework in the corpus;
targeting cybed:Role restricts to the frameworks that
declare roles (NICE v2.2.0, DCWF v5.1, ECSF v1, CyQUAL 1.2.0, CCSSF
2022, OTCCF v1.1, SCyWF 1.5); targeting cybed:RoleElement
returns atomic content nodes (parents, Subpoints, and Examples).
What these queries surface
Findings the package’s analytical layer produces directly from the corpus graph:
- Element density per framework varies by roughly 13x with Cyber.org K-12 / CSTA pedagogical Examples included (DCWF v5.1 at 54.8 elements per top-level unit, Cyber.org K-12 v1.0 at 4.2). Without Examples the spread is roughly 38x (NICE v2.2.0 at 41.7, Cyber.org K-12 v1.0 at 1.1). Per-unit density is a comparison aid across heterogeneous denominators, not a quality claim.
- US frameworks (NICE, DCWF, Cyber.org K-12, CSTA) hold about 43 percent of the corpus by element count, well above the EU-level frameworks (ECSF, DigComp). That gap reflects design philosophy (ECSF is profile-level by intent, DigComp is a citizen self-assessment instrument), and some of the national frameworks added after the original release are built on NICE by their own account, so jurisdiction alone understates US-authored content.
- A small number of NICE work roles concentrate disproportionate
competency specification. See the
cross-framework-analysisvignette’s “Largest organizing units” recipe and the README’s live figures for the current ranking.
See the cross-framework-analysis
vignette for the worked R that produces them.
Why single-BGP queries with R-side joins
The librdf C library that rdflib wraps
exhibits poor performance and silent zero-row results on conjunctive
triple patterns at this graph’s scale. Multi-pattern SPARQL joins via
shared variables hang for many minutes. Multi-property selects on a
single subject silently return no rows. Single basic graph patterns (one
triple match per SPARQL call) execute fast and correctly.
The package’s discipline is therefore:
- SPARQL queries are single basic graph patterns. One triple match per call.
- Joins, multi-property assembly, and aggregation happen in R via dplyr.
This is implemented by the helpers in
R/sparql-helpers.R:
-
sparql_pairs(rdf, predicate)returns subject-object pairs for all triples with a given predicate. -
sparql_subjects(rdf, predicate, object)returns subjects of triples whose predicate and object are fixed.
Domain-level helpers compose these primitives:
-
framework_metadata(rdf)returns a tibble of (framework, name, jurisdiction, sector, specificity). -
organizing_unit_framework_bindings(rdf)returns (unit, unit_name, framework, framework_name) for every framework’s top-level enumerated unit. The cross-framework cut. -
role_framework_bindings(rdf)returns (role, role_name, framework, framework_name) restricted to frameworks wherecybed:Roleis asserted (NICE, DCWF, ECSF, CyQUAL, CCSSF, OTCCF). -
element_framework_bindings(rdf)returns (element, framework, framework_name) for everycybed:RoleElement, including Subpoints and Examples. -
example_framework_bindings(rdf)returns (example, framework, framework_name) for thecybed:Examplesubset (Cyber.org K-12 and CSTA Clarification scaffolding). -
role_element_bindings(rdf)returns (role, element) for everycybed:hasElementtriple. Excludes Examples (which are reachable only viacybed:hasExample).
Each domain helper makes one to four single-BGP queries and joins the results via dplyr left-joins or semi-joins.
Query families
Family A: Structural
-
A1. Framework metadata inventory. One row per
framework with jurisdiction, sector, specificity. Uses
framework_metadata(). -
A2. Organizing units per framework and elements per
framework. Aggregated in R from
organizing_unit_framework_bindings()andelement_framework_bindings(). For workforce-restricted aggregations, swap inrole_framework_bindings(). -
A3. Element density. Elements per top-level unit
per framework, joining
role_element_bindings()withorganizing_unit_framework_bindings(). Surfaces the cross-framework structural-density spread (with-examples count), from the granular workforce frameworks at the high end to the high-level interoperability and self-assessment frameworks at the low end. Current per-framework figures are incybedtools::framework_summaryand the density chart in thecross-framework-analysisvignette. -
A4. Missing required properties. Quality control.
Surface RoleElement subjects without
cybed:elementText(usesparql_subjects()and dplyranti_join).
Family B: Cross-framework pivots
-
B1. Element volume by jurisdiction. Join
element_framework_bindings()toframework_metadata()$jurisdictionand aggregate. -
B2. Element volume by sector. Same shape, on
framework_metadata()$sector. -
B3. Element volume by specificity. Same shape, on
framework_metadata()$specificity. -
B4. Framework-vs-framework structural comparison.
Filter
role_framework_bindings()to two frameworks and compare role counts, element-per-role distributions.
The runner (scripts/040-run-sparql.R) implements the
named analyses (q10 through q15) that map onto these families and write
one CSV per analysis to data/processed/query-results/.
Implementation notes
- The package’s analytical queries live in R, not in
.rqfiles. Theinst/queries/directory is reserved for user-supplied custom queries. See itsREADME.md. - Direct SPARQL access remains available via
rdflib::rdf_query(rdf, query_text)for users who need it. Stick to single basic graph patterns for reliability. - For aggregation, never use
COUNT,GROUP BY, orHAVINGin SPARQL. librdf’s SPARQL 1.1 aggregate support is unreliable. Aggregate in dplyr. - A future release may add an Apache Jena Fuseki backend for full SPARQL 1.1 support. This would relax the single-BGP discipline for users running against a Fuseki endpoint.
