From b6425520aa10b160ae0e94501d61fd91999a2f4e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 13:51:17 +0000 Subject: [PATCH 1/8] feat: add demo queries workspace published to Jena (Matdata-eu/MatGUI#133) Add 107 demo queries in demo-queries/ covering ERA RINF, EU Publications Office authority tables, EuroVoc and Cellar, data.europa.eu, GeoSPARQL on Apache Jena (incl. the apache-jena-fuseki-geosparql examples), Wikidata, DBpedia, OpenStreetMap (QLever), UniProt, Getty, AGROVOC and LOV. scripts/demo_queries.py validates the queries and converts them to the RDF model of MatGUI's SPARQL workspaces. A GitHub workflow validates and smoke-tests them, and on main replaces the default graph of the matgui-demo Jena dataset. The site registers the demo workspace and adds a Demo queries link in the footer. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01WZxQ3LgrC1zQFfHqfAoZPn --- .github/workflows/demo-queries.yml | 104 ++++ README.md | 25 + .../agriculture/agrovoc-narrower-concepts.rq | 13 + .../agriculture/agrovoc-translations.rq | 10 + demo-queries/agriculture/search-agrovoc.rq | 12 + .../cultural-heritage/aat-hierarchy-graph.rq | 20 + demo-queries/cultural-heritage/aat-search.rq | 16 + .../tgn-places-in-belgium.rq | 19 + .../authority-tables/data-themes.rq | 11 + .../authority-tables/frequencies.rq | 10 + .../authority-tables/hvd-categories.rq | 15 + .../authority-tables/licences.rq | 10 + .../authority-tables/theme-usage.rq | 12 + .../data-europa-eu/datasets/catalogues.rq | 14 + .../dataset-with-distributions-graph.rq | 34 ++ .../datasets/datasets-by-keyword.rq | 15 + .../distribution-formats-of-transport-data.rq | 15 + .../high-value-datasets-per-category.rq | 15 + .../top-publishers-of-transport-data.rq | 15 + .../datasets/transport-datasets.rq | 15 + .../dbpedia/abstract-of-a-resource.rq | 11 + .../dbpedia/ask-is-brussels-a-capital.rq | 9 + .../dbpedia/castles-in-belgium-map.rq | 18 + .../football-clubs-by-stadium-capacity.rq | 15 + .../most-populated-european-capitals.rq | 15 + .../dbpedia/ontology-class-hierarchy.rq | 15 + .../programming-languages-influence-graph.rq | 23 + .../dbpedia/railway-companies-in-europe.rq | 16 + .../explore/classes-and-instance-counts.rq | 8 + .../explore/describe-an-operational-point.rq | 12 + .../era-rinf/explore/era-ontology-classes.rq | 14 + .../properties-of-operational-points.rq | 12 + .../era-rinf/explore/skos-concept-schemes.rq | 12 + .../lines-and-tracks/energy-supply-systems.rq | 14 + .../era-rinf/lines-and-tracks/etcs-levels.rq | 14 + .../longest-sections-of-line.rq | 15 + .../lines-and-tracks/longest-tunnels.rq | 14 + .../maximum-speed-distribution.rq | 12 + .../network-graph-luxembourg.rq | 21 + .../network-length-per-country.rq | 13 + .../maps/operational-points-in-belgium.rq | 14 + .../maps/operational-points-near-brussels.rq | 18 + .../maps/sections-of-line-in-switzerland.rq | 14 + .../operational-points/border-points.rq | 15 + .../operational-point-types.rq | 13 + .../operational-points-per-country.rq | 12 + .../operational-points/search-by-name.rq | 15 + .../operational-points/stations-in-belgium.rq | 19 + demo-queries/geosparql/area-and-length.rq | 18 + demo-queries/geosparql/buffer-and-envelope.rq | 24 + .../geosparql/distance-between-cities.rq | 19 + demo-queries/geosparql/geometry-properties.rq | 19 + demo-queries/geosparql/nearest-station.rq | 21 + demo-queries/geosparql/spatial-relations.rq | 33 ++ demo-queries/geosparql/transform-crs.rq | 18 + demo-queries/geosparql/wkt-shapes-on-a-map.rq | 14 + demo-queries/getting-started/aggregation.rq | 23 + .../construct-a-small-graph.rq | 22 + .../getting-started/dates-and-numbers.rq | 18 + .../demo-queries-per-endpoint.rq | 14 + demo-queries/getting-started/hello-world.rq | 11 + .../getting-started/list-demo-queries.rq | 17 + .../getting-started/string-functions.rq | 13 + .../life-sciences/enzymes-with-ec-number.rq | 15 + .../life-sciences/protein-by-gene-name.rq | 16 + .../life-sciences/proteins-per-organism.rq | 15 + .../life-sciences/reviewed-human-proteins.rq | 14 + .../life-sciences/taxonomy-lineage.rq | 11 + .../charging-stations-per-operator.rq | 16 + .../level-crossings-in-the-netherlands.rq | 15 + .../railway-lines-in-luxembourg.rq | 13 + .../railway-stations-in-brussels.rq | 13 + .../all-labels-of-a-concept.rq | 11 + .../authority-tables/concept-status.rq | 13 + .../authority-tables/corporate-bodies.rq | 12 + .../countries-multilingual.rq | 14 + .../authority-tables/country-notations.rq | 13 + .../authority-tables/currencies.rq | 12 + .../authority-tables/describe-a-concept.rq | 3 + .../authority-tables/file-types.rq | 12 + .../authority-tables/languages.rq | 12 + .../authority-tables/list-authority-tables.rq | 12 + .../eu-law/acts-about-rail-transport.rq | 23 + .../eu-law/acts-amending-an-act.rq | 19 + .../eu-law/eurovoc-subjects-of-an-act.rq | 15 + .../eu-law/gdpr-in-all-languages.rq | 14 + .../eu-law/latest-regulations.rq | 18 + .../eu-law/legal-acts-per-year.rq | 16 + .../eurovoc/concept-hierarchy-graph.rq | 19 + .../eurovoc/eurovoc-domains.rq | 11 + .../eurovoc/search-concepts.rq | 15 + .../eurovoc/translations.rq | 11 + .../vocabularies/vocabularies-and-prefixes.rq | 14 + .../vocabularies/vocabularies-by-tag.rq | 13 + demo-queries/wikidata/eu-agencies.rq | 15 + demo-queries/wikidata/eu-member-states.rq | 21 + demo-queries/wikidata/family-tree-graph.rq | 22 + .../wikidata/high-speed-lines-in-europe.rq | 17 + .../wikidata/largest-cities-in-europe.rq | 18 + .../wikidata/longest-railway-tunnels.rq | 16 + .../wikidata/nobel-laureates-per-country.rq | 16 + .../wikidata/official-languages-of-europe.rq | 19 + .../wikidata/painters-born-in-belgium.rq | 20 + .../population-history-of-brussels.rq | 18 + demo-queries/wikidata/power-plants-map.rq | 17 + .../railway-stations-in-belgium-map.rq | 14 + .../rivers-flowing-through-belgium.rq | 17 + .../wikidata/sparql-endpoints-in-wikidata.rq | 13 + .../wikidata/unesco-world-heritage-map.rq | 15 + demo-queries/workspace.json | 47 ++ scripts/demo_queries.py | 476 ++++++++++++++++++ src/index.html | 2 + src/main.js | 39 ++ 113 files changed, 2339 insertions(+) create mode 100644 .github/workflows/demo-queries.yml create mode 100644 demo-queries/agriculture/agrovoc-narrower-concepts.rq create mode 100644 demo-queries/agriculture/agrovoc-translations.rq create mode 100644 demo-queries/agriculture/search-agrovoc.rq create mode 100644 demo-queries/cultural-heritage/aat-hierarchy-graph.rq create mode 100644 demo-queries/cultural-heritage/aat-search.rq create mode 100644 demo-queries/cultural-heritage/tgn-places-in-belgium.rq create mode 100644 demo-queries/data-europa-eu/authority-tables/data-themes.rq create mode 100644 demo-queries/data-europa-eu/authority-tables/frequencies.rq create mode 100644 demo-queries/data-europa-eu/authority-tables/hvd-categories.rq create mode 100644 demo-queries/data-europa-eu/authority-tables/licences.rq create mode 100644 demo-queries/data-europa-eu/authority-tables/theme-usage.rq create mode 100644 demo-queries/data-europa-eu/datasets/catalogues.rq create mode 100644 demo-queries/data-europa-eu/datasets/dataset-with-distributions-graph.rq create mode 100644 demo-queries/data-europa-eu/datasets/datasets-by-keyword.rq create mode 100644 demo-queries/data-europa-eu/datasets/distribution-formats-of-transport-data.rq create mode 100644 demo-queries/data-europa-eu/datasets/high-value-datasets-per-category.rq create mode 100644 demo-queries/data-europa-eu/datasets/top-publishers-of-transport-data.rq create mode 100644 demo-queries/data-europa-eu/datasets/transport-datasets.rq create mode 100644 demo-queries/dbpedia/abstract-of-a-resource.rq create mode 100644 demo-queries/dbpedia/ask-is-brussels-a-capital.rq create mode 100644 demo-queries/dbpedia/castles-in-belgium-map.rq create mode 100644 demo-queries/dbpedia/football-clubs-by-stadium-capacity.rq create mode 100644 demo-queries/dbpedia/most-populated-european-capitals.rq create mode 100644 demo-queries/dbpedia/ontology-class-hierarchy.rq create mode 100644 demo-queries/dbpedia/programming-languages-influence-graph.rq create mode 100644 demo-queries/dbpedia/railway-companies-in-europe.rq create mode 100644 demo-queries/era-rinf/explore/classes-and-instance-counts.rq create mode 100644 demo-queries/era-rinf/explore/describe-an-operational-point.rq create mode 100644 demo-queries/era-rinf/explore/era-ontology-classes.rq create mode 100644 demo-queries/era-rinf/explore/properties-of-operational-points.rq create mode 100644 demo-queries/era-rinf/explore/skos-concept-schemes.rq create mode 100644 demo-queries/era-rinf/lines-and-tracks/energy-supply-systems.rq create mode 100644 demo-queries/era-rinf/lines-and-tracks/etcs-levels.rq create mode 100644 demo-queries/era-rinf/lines-and-tracks/longest-sections-of-line.rq create mode 100644 demo-queries/era-rinf/lines-and-tracks/longest-tunnels.rq create mode 100644 demo-queries/era-rinf/lines-and-tracks/maximum-speed-distribution.rq create mode 100644 demo-queries/era-rinf/lines-and-tracks/network-graph-luxembourg.rq create mode 100644 demo-queries/era-rinf/lines-and-tracks/network-length-per-country.rq create mode 100644 demo-queries/era-rinf/maps/operational-points-in-belgium.rq create mode 100644 demo-queries/era-rinf/maps/operational-points-near-brussels.rq create mode 100644 demo-queries/era-rinf/maps/sections-of-line-in-switzerland.rq create mode 100644 demo-queries/era-rinf/operational-points/border-points.rq create mode 100644 demo-queries/era-rinf/operational-points/operational-point-types.rq create mode 100644 demo-queries/era-rinf/operational-points/operational-points-per-country.rq create mode 100644 demo-queries/era-rinf/operational-points/search-by-name.rq create mode 100644 demo-queries/era-rinf/operational-points/stations-in-belgium.rq create mode 100644 demo-queries/geosparql/area-and-length.rq create mode 100644 demo-queries/geosparql/buffer-and-envelope.rq create mode 100644 demo-queries/geosparql/distance-between-cities.rq create mode 100644 demo-queries/geosparql/geometry-properties.rq create mode 100644 demo-queries/geosparql/nearest-station.rq create mode 100644 demo-queries/geosparql/spatial-relations.rq create mode 100644 demo-queries/geosparql/transform-crs.rq create mode 100644 demo-queries/geosparql/wkt-shapes-on-a-map.rq create mode 100644 demo-queries/getting-started/aggregation.rq create mode 100644 demo-queries/getting-started/construct-a-small-graph.rq create mode 100644 demo-queries/getting-started/dates-and-numbers.rq create mode 100644 demo-queries/getting-started/demo-queries-per-endpoint.rq create mode 100644 demo-queries/getting-started/hello-world.rq create mode 100644 demo-queries/getting-started/list-demo-queries.rq create mode 100644 demo-queries/getting-started/string-functions.rq create mode 100644 demo-queries/life-sciences/enzymes-with-ec-number.rq create mode 100644 demo-queries/life-sciences/protein-by-gene-name.rq create mode 100644 demo-queries/life-sciences/proteins-per-organism.rq create mode 100644 demo-queries/life-sciences/reviewed-human-proteins.rq create mode 100644 demo-queries/life-sciences/taxonomy-lineage.rq create mode 100644 demo-queries/openstreetmap/charging-stations-per-operator.rq create mode 100644 demo-queries/openstreetmap/level-crossings-in-the-netherlands.rq create mode 100644 demo-queries/openstreetmap/railway-lines-in-luxembourg.rq create mode 100644 demo-queries/openstreetmap/railway-stations-in-brussels.rq create mode 100644 demo-queries/publications-office/authority-tables/all-labels-of-a-concept.rq create mode 100644 demo-queries/publications-office/authority-tables/concept-status.rq create mode 100644 demo-queries/publications-office/authority-tables/corporate-bodies.rq create mode 100644 demo-queries/publications-office/authority-tables/countries-multilingual.rq create mode 100644 demo-queries/publications-office/authority-tables/country-notations.rq create mode 100644 demo-queries/publications-office/authority-tables/currencies.rq create mode 100644 demo-queries/publications-office/authority-tables/describe-a-concept.rq create mode 100644 demo-queries/publications-office/authority-tables/file-types.rq create mode 100644 demo-queries/publications-office/authority-tables/languages.rq create mode 100644 demo-queries/publications-office/authority-tables/list-authority-tables.rq create mode 100644 demo-queries/publications-office/eu-law/acts-about-rail-transport.rq create mode 100644 demo-queries/publications-office/eu-law/acts-amending-an-act.rq create mode 100644 demo-queries/publications-office/eu-law/eurovoc-subjects-of-an-act.rq create mode 100644 demo-queries/publications-office/eu-law/gdpr-in-all-languages.rq create mode 100644 demo-queries/publications-office/eu-law/latest-regulations.rq create mode 100644 demo-queries/publications-office/eu-law/legal-acts-per-year.rq create mode 100644 demo-queries/publications-office/eurovoc/concept-hierarchy-graph.rq create mode 100644 demo-queries/publications-office/eurovoc/eurovoc-domains.rq create mode 100644 demo-queries/publications-office/eurovoc/search-concepts.rq create mode 100644 demo-queries/publications-office/eurovoc/translations.rq create mode 100644 demo-queries/vocabularies/vocabularies-and-prefixes.rq create mode 100644 demo-queries/vocabularies/vocabularies-by-tag.rq create mode 100644 demo-queries/wikidata/eu-agencies.rq create mode 100644 demo-queries/wikidata/eu-member-states.rq create mode 100644 demo-queries/wikidata/family-tree-graph.rq create mode 100644 demo-queries/wikidata/high-speed-lines-in-europe.rq create mode 100644 demo-queries/wikidata/largest-cities-in-europe.rq create mode 100644 demo-queries/wikidata/longest-railway-tunnels.rq create mode 100644 demo-queries/wikidata/nobel-laureates-per-country.rq create mode 100644 demo-queries/wikidata/official-languages-of-europe.rq create mode 100644 demo-queries/wikidata/painters-born-in-belgium.rq create mode 100644 demo-queries/wikidata/population-history-of-brussels.rq create mode 100644 demo-queries/wikidata/power-plants-map.rq create mode 100644 demo-queries/wikidata/railway-stations-in-belgium-map.rq create mode 100644 demo-queries/wikidata/rivers-flowing-through-belgium.rq create mode 100644 demo-queries/wikidata/sparql-endpoints-in-wikidata.rq create mode 100644 demo-queries/wikidata/unesco-world-heritage-map.rq create mode 100644 demo-queries/workspace.json create mode 100644 scripts/demo_queries.py diff --git a/.github/workflows/demo-queries.yml b/.github/workflows/demo-queries.yml new file mode 100644 index 0000000..b47571d --- /dev/null +++ b/.github/workflows/demo-queries.yml @@ -0,0 +1,104 @@ +name: Demo queries + +# Validates the demo queries in demo-queries/, runs them against their public +# endpoints, and (on main) uploads them to the matgui-demo Jena dataset that +# backs the "MatGUI demo queries" workspace on https://matgui.matdata.eu. +# +# Required repository secrets for publishing: +# JENA_USERNAME / JENA_PASSWORD - credentials allowed to write to the dataset +# Optional repository variable: +# JENA_DATASET_URL - defaults to https://jena.matdata.eu/matgui-demo + +on: + push: + branches: ["**"] + paths: + - "demo-queries/**" + - "scripts/demo_queries.py" + - ".github/workflows/demo-queries.yml" + pull_request: + paths: + - "demo-queries/**" + - "scripts/demo_queries.py" + - ".github/workflows/demo-queries.yml" + schedule: + # Nightly re-publish resets any changes made to the demo dataset + - cron: "17 3 * * *" + workflow_dispatch: + inputs: + smoke_test: + description: "Run every query against its endpoint" + type: boolean + default: true + +permissions: + contents: read + +concurrency: + group: demo-queries-${{ github.ref }} + cancel-in-progress: true + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # git history is used for the dcterms:created timestamps + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install dependencies + run: pip install "rdflib>=7,<8" + + - name: Validate query syntax and metadata + run: python scripts/demo_queries.py validate + + - name: Build workspace RDF + run: python scripts/demo_queries.py build --out build/matgui-demo.ttl + + - uses: actions/upload-artifact@v4 + with: + name: matgui-demo-workspace + path: build/matgui-demo.ttl + + smoke-test: + # Public endpoints are sometimes slow or down, so failures are reported in the + # job summary but do not block publishing. + if: github.event_name != 'schedule' && (github.event_name != 'workflow_dispatch' || inputs.smoke_test) + needs: validate + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Run every query against its endpoint + run: python scripts/demo_queries.py smoke-test --timeout 90 + + publish: + if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' + needs: validate + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - uses: actions/download-artifact@v4 + with: + name: matgui-demo-workspace + path: build + + - name: Upload to Jena + env: + JENA_DATASET_URL: ${{ vars.JENA_DATASET_URL || 'https://jena.matdata.eu/matgui-demo' }} + JENA_USERNAME: ${{ secrets.JENA_USERNAME }} + JENA_PASSWORD: ${{ secrets.JENA_PASSWORD }} + run: python scripts/demo_queries.py publish --file build/matgui-demo.ttl diff --git a/README.md b/README.md index 134dc48..8f63eff 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,31 @@ Modify `src/main.css` to customize the appearance of the interface. The MatGUI instance can be configured in `src/main.js`. See the [MatGUI documentation](https://matgui-doc.matdata.eu) for available options. +## Demo Queries + +The site ships with a read-only **"MatGUI demo queries"** workspace: over 100 ready-to-run queries for ERA RINF, the EU Publications Office (authority tables, EuroVoc, Cellar), data.europa.eu, GeoSPARQL on Apache Jena, Wikidata, DBpedia, OpenStreetMap (QLever), UniProt, Getty, AGROVOC and LOV. Open them with the **Demo queries** link in the footer or from the query browser. + +The queries are plain `.rq` files in [`demo-queries/`](demo-queries). Each sub-directory is a workspace folder; folder labels, default endpoints and endpoint aliases are defined in [`demo-queries/workspace.json`](demo-queries/workspace.json). Every file starts with a small header: + +```sparql +#+ name: Operational points per country +#+ description: Counts operational points per member state. +#+ endpoint: era # optional when the folder has a default; alias or full URL +PREFIX era: +SELECT ... +``` + +[`scripts/demo_queries.py`](scripts/demo_queries.py) converts the files into the RDF model of MatGUI's SPARQL workspaces (`yasgui:Workspace`, `yasgui:WorkspaceFolder`, `yasgui:ManagedQuery`, `yasgui:ManagedQueryVersion`): + +```bash +pip install rdflib +python scripts/demo_queries.py validate # syntax + metadata checks +python scripts/demo_queries.py build # writes build/matgui-demo.ttl +python scripts/demo_queries.py smoke-test # runs every query against its endpoint +``` + +The [Demo queries workflow](.github/workflows/demo-queries.yml) validates and smoke-tests the queries on every change, and on `main` (plus nightly) replaces the default graph of the `matgui-demo` dataset on [jena.matdata.eu](https://jena.matdata.eu) using the Graph Store Protocol. It needs the `JENA_USERNAME` and `JENA_PASSWORD` repository secrets; the dataset URL can be overridden with the `JENA_DATASET_URL` repository variable. + ## Credits - Original YASGUI by [Zazuko](https://github.com/zazuko/yasgui) and [TriplyDB](https://github.com/triplydb/Yasgui) diff --git a/demo-queries/agriculture/agrovoc-narrower-concepts.rq b/demo-queries/agriculture/agrovoc-narrower-concepts.rq new file mode 100644 index 0000000..378cc43 --- /dev/null +++ b/demo-queries/agriculture/agrovoc-narrower-concepts.rq @@ -0,0 +1,13 @@ +#+ name: Narrower concepts of 'cereals' +#+ description: Walks down the SKOS hierarchy from 'cereals' (c_1474) and returns the English labels. +PREFIX skos: +PREFIX skosxl: + +SELECT ?concept ?label ?parentLabel WHERE { + ?concept skos:broader+ ; + skos:broader ?parent ; + skosxl:prefLabel/skosxl:literalForm ?label . + ?parent skosxl:prefLabel/skosxl:literalForm ?parentLabel . + FILTER(LANG(?label) = "en" && LANG(?parentLabel) = "en") +} +ORDER BY ?parentLabel ?label diff --git a/demo-queries/agriculture/agrovoc-translations.rq b/demo-queries/agriculture/agrovoc-translations.rq new file mode 100644 index 0000000..3000d15 --- /dev/null +++ b/demo-queries/agriculture/agrovoc-translations.rq @@ -0,0 +1,10 @@ +#+ name: Translations of an AGROVOC concept +#+ description: Preferred labels of the concept 'maize' (c_12332) in all available languages. +PREFIX skos: +PREFIX skosxl: + +SELECT ?language ?label WHERE { + skosxl:prefLabel/skosxl:literalForm ?label . + BIND(LANG(?label) AS ?language) +} +ORDER BY ?language diff --git a/demo-queries/agriculture/search-agrovoc.rq b/demo-queries/agriculture/search-agrovoc.rq new file mode 100644 index 0000000..c9cbbc4 --- /dev/null +++ b/demo-queries/agriculture/search-agrovoc.rq @@ -0,0 +1,12 @@ +#+ name: Search AGROVOC concepts +#+ description: AGROVOC concepts whose English label contains 'wheat', using SKOS-XL labels. +PREFIX skos: +PREFIX skosxl: + +SELECT ?concept ?label WHERE { + ?concept a skos:Concept ; + skosxl:prefLabel/skosxl:literalForm ?label . + FILTER(LANG(?label) = "en" && CONTAINS(LCASE(STR(?label)), "wheat")) +} +ORDER BY ?label +LIMIT 100 diff --git a/demo-queries/cultural-heritage/aat-hierarchy-graph.rq b/demo-queries/cultural-heritage/aat-hierarchy-graph.rq new file mode 100644 index 0000000..923b5d8 --- /dev/null +++ b/demo-queries/cultural-heritage/aat-hierarchy-graph.rq @@ -0,0 +1,20 @@ +#+ name: AAT hierarchy below 'bridges' (graph) +#+ description: CONSTRUCTs the narrower hierarchy of the AAT concept 'bridges (built works)' (aat:300007836). Use the Graph plugin. +PREFIX skos: +PREFIX gvp: +PREFIX xl: +PREFIX aat: +PREFIX tgn: +PREFIX rdfs: + +CONSTRUCT { + ?concept skos:broader ?parent ; + rdfs:label ?term . + aat:300007836 rdfs:label ?topTerm . +} +WHERE { + aat:300007836 gvp:prefLabelGVP/xl:literalForm ?topTerm . + ?concept gvp:broaderPreferred+ aat:300007836 ; + gvp:broaderPreferred ?parent ; + gvp:prefLabelGVP/xl:literalForm ?term . +} diff --git a/demo-queries/cultural-heritage/aat-search.rq b/demo-queries/cultural-heritage/aat-search.rq new file mode 100644 index 0000000..6324522 --- /dev/null +++ b/demo-queries/cultural-heritage/aat-search.rq @@ -0,0 +1,16 @@ +#+ name: Search the Art & Architecture Thesaurus +#+ description: AAT concepts whose preferred English term contains 'bridge', with their parent. +PREFIX skos: +PREFIX gvp: +PREFIX xl: +PREFIX aat: +PREFIX tgn: + +SELECT ?concept ?term ?parentTerm WHERE { + ?concept skos:inScheme aat: ; + gvp:prefLabelGVP/xl:literalForm ?term . + FILTER(CONTAINS(LCASE(STR(?term)), "bridge")) + OPTIONAL { ?concept gvp:broaderPreferred/gvp:prefLabelGVP/xl:literalForm ?parentTerm } +} +ORDER BY ?term +LIMIT 100 diff --git a/demo-queries/cultural-heritage/tgn-places-in-belgium.rq b/demo-queries/cultural-heritage/tgn-places-in-belgium.rq new file mode 100644 index 0000000..7beacd3 --- /dev/null +++ b/demo-queries/cultural-heritage/tgn-places-in-belgium.rq @@ -0,0 +1,19 @@ +#+ name: TGN: inhabited places in Belgium +#+ description: Places in the Getty Thesaurus of Geographic Names that lie in Belgium (tgn:1000063), with coordinates. +PREFIX skos: +PREFIX gvp: +PREFIX xl: +PREFIX aat: +PREFIX tgn: +PREFIX wgs: +PREFIX foaf: + +SELECT ?place ?name ?lat ?long WHERE { + ?place gvp:broaderPartitive+ tgn:1000063 ; + gvp:placeTypePreferred aat:300008347 ; + gvp:prefLabelGVP/xl:literalForm ?name ; + foaf:focus ?focus . + ?focus wgs:lat ?lat ; + wgs:long ?long . +} +LIMIT 200 diff --git a/demo-queries/data-europa-eu/authority-tables/data-themes.rq b/demo-queries/data-europa-eu/authority-tables/data-themes.rq new file mode 100644 index 0000000..4f253ac --- /dev/null +++ b/demo-queries/data-europa-eu/authority-tables/data-themes.rq @@ -0,0 +1,11 @@ +#+ name: Data themes +#+ description: The DCAT-AP data themes (authority table data-theme) in English, French and German. +PREFIX skos: + +SELECT ?theme ?en ?fr ?de WHERE { + ?theme skos:inScheme . + OPTIONAL { ?theme skos:prefLabel ?en . FILTER(LANG(?en) = "en") } + OPTIONAL { ?theme skos:prefLabel ?fr . FILTER(LANG(?fr) = "fr") } + OPTIONAL { ?theme skos:prefLabel ?de . FILTER(LANG(?de) = "de") } +} +ORDER BY ?theme diff --git a/demo-queries/data-europa-eu/authority-tables/frequencies.rq b/demo-queries/data-europa-eu/authority-tables/frequencies.rq new file mode 100644 index 0000000..abdf8f0 --- /dev/null +++ b/demo-queries/data-europa-eu/authority-tables/frequencies.rq @@ -0,0 +1,10 @@ +#+ name: Update frequencies +#+ description: The frequency authority table used for dct:accrualPeriodicity. +PREFIX skos: + +SELECT ?frequency ?label WHERE { + ?frequency skos:inScheme ; + skos:prefLabel ?label . + FILTER(LANG(?label) = "en") +} +ORDER BY ?label diff --git a/demo-queries/data-europa-eu/authority-tables/hvd-categories.rq b/demo-queries/data-europa-eu/authority-tables/hvd-categories.rq new file mode 100644 index 0000000..d9c82ac --- /dev/null +++ b/demo-queries/data-europa-eu/authority-tables/hvd-categories.rq @@ -0,0 +1,15 @@ +#+ name: High-value dataset categories +#+ description: The high-value dataset categories vocabulary with its hierarchy (top categories and sub-categories). +PREFIX skos: + +SELECT ?category ?label ?parentLabel WHERE { + ?category skos:inScheme ; + skos:prefLabel ?label . + FILTER(LANG(?label) = "en") + OPTIONAL { + ?category skos:broader ?parent . + ?parent skos:prefLabel ?parentLabel . + FILTER(LANG(?parentLabel) = "en") + } +} +ORDER BY ?parentLabel ?label diff --git a/demo-queries/data-europa-eu/authority-tables/licences.rq b/demo-queries/data-europa-eu/authority-tables/licences.rq new file mode 100644 index 0000000..c10fbdc --- /dev/null +++ b/demo-queries/data-europa-eu/authority-tables/licences.rq @@ -0,0 +1,10 @@ +#+ name: Licences +#+ description: The licence authority table used to describe the licence of datasets and distributions. +PREFIX skos: + +SELECT ?licence ?label WHERE { + ?licence skos:inScheme ; + skos:prefLabel ?label . + FILTER(LANG(?label) = "en") +} +ORDER BY ?label diff --git a/demo-queries/data-europa-eu/authority-tables/theme-usage.rq b/demo-queries/data-europa-eu/authority-tables/theme-usage.rq new file mode 100644 index 0000000..6ee269e --- /dev/null +++ b/demo-queries/data-europa-eu/authority-tables/theme-usage.rq @@ -0,0 +1,12 @@ +#+ name: Datasets per data theme +#+ description: Joins the data-theme code list with the catalogue: how many datasets use each theme? +PREFIX skos: +PREFIX dcat: + +SELECT ?theme ?label (COUNT(?dataset) AS ?datasets) WHERE { + ?theme skos:inScheme . + OPTIONAL { ?theme skos:prefLabel ?label . FILTER(LANG(?label) = "en") } + ?dataset dcat:theme ?theme . +} +GROUP BY ?theme ?label +ORDER BY DESC(?datasets) diff --git a/demo-queries/data-europa-eu/datasets/catalogues.rq b/demo-queries/data-europa-eu/datasets/catalogues.rq new file mode 100644 index 0000000..1bb3ba3 --- /dev/null +++ b/demo-queries/data-europa-eu/datasets/catalogues.rq @@ -0,0 +1,14 @@ +#+ name: Data catalogues +#+ description: The catalogues harvested by data.europa.eu with their title and country. +PREFIX dcat: +PREFIX dct: +PREFIX foaf: + +SELECT ?catalogue ?title ?spatial WHERE { + ?catalogue a dcat:Catalog ; + dct:title ?title . + FILTER(LANGMATCHES(LANG(?title), "en") || LANG(?title) = "") + OPTIONAL { ?catalogue dct:spatial ?spatial } +} +ORDER BY ?title +LIMIT 500 diff --git a/demo-queries/data-europa-eu/datasets/dataset-with-distributions-graph.rq b/demo-queries/data-europa-eu/datasets/dataset-with-distributions-graph.rq new file mode 100644 index 0000000..344430b --- /dev/null +++ b/demo-queries/data-europa-eu/datasets/dataset-with-distributions-graph.rq @@ -0,0 +1,34 @@ +#+ name: A dataset and its distributions (graph) +#+ description: CONSTRUCTs one railway dataset with its publisher and distributions. Open the result in the Graph plugin. +PREFIX dcat: +PREFIX dct: +PREFIX foaf: + +CONSTRUCT { + ?dataset a dcat:Dataset ; + dct:title ?title ; + dct:publisher ?publisher ; + dcat:distribution ?distribution ; + dcat:theme ?theme . + ?publisher foaf:name ?publisherName . + ?distribution dct:format ?format ; + dcat:accessURL ?accessURL . +} +WHERE { + { + SELECT ?dataset WHERE { + ?dataset a dcat:Dataset ; + dcat:keyword "railway"@en ; + dcat:distribution ?d . + } + LIMIT 1 + } + ?dataset dct:title ?title . + OPTIONAL { ?dataset dcat:theme ?theme } + OPTIONAL { ?dataset dct:publisher ?publisher . OPTIONAL { ?publisher foaf:name ?publisherName } } + OPTIONAL { + ?dataset dcat:distribution ?distribution . + OPTIONAL { ?distribution dct:format ?format } + OPTIONAL { ?distribution dcat:accessURL ?accessURL } + } +} diff --git a/demo-queries/data-europa-eu/datasets/datasets-by-keyword.rq b/demo-queries/data-europa-eu/datasets/datasets-by-keyword.rq new file mode 100644 index 0000000..344b5a0 --- /dev/null +++ b/demo-queries/data-europa-eu/datasets/datasets-by-keyword.rq @@ -0,0 +1,15 @@ +#+ name: Datasets by keyword +#+ description: Datasets tagged with a keyword (exact match, several spellings). Replace the VALUES to search for something else. +PREFIX dcat: +PREFIX dct: +PREFIX foaf: + +SELECT DISTINCT ?dataset ?title ?publisherName WHERE { + VALUES ?keyword { "railway"@en "railway" "rail transport"@en "railways"@en } + ?dataset a dcat:Dataset ; + dcat:keyword ?keyword ; + dct:title ?title . + FILTER(LANGMATCHES(LANG(?title), "en") || LANG(?title) = "") + OPTIONAL { ?dataset dct:publisher/foaf:name ?publisherName } +} +LIMIT 200 diff --git a/demo-queries/data-europa-eu/datasets/distribution-formats-of-transport-data.rq b/demo-queries/data-europa-eu/datasets/distribution-formats-of-transport-data.rq new file mode 100644 index 0000000..b035806 --- /dev/null +++ b/demo-queries/data-europa-eu/datasets/distribution-formats-of-transport-data.rq @@ -0,0 +1,15 @@ +#+ name: Distribution formats of transport data +#+ description: Which file formats are used by the distributions of transport datasets? +PREFIX dcat: +PREFIX dct: +PREFIX foaf: +PREFIX theme: + +SELECT ?format (COUNT(DISTINCT ?distribution) AS ?distributions) WHERE { + ?dataset dcat:theme theme:TRAN ; + dcat:distribution ?distribution . + ?distribution dct:format ?format . +} +GROUP BY ?format +ORDER BY DESC(?distributions) +LIMIT 25 diff --git a/demo-queries/data-europa-eu/datasets/high-value-datasets-per-category.rq b/demo-queries/data-europa-eu/datasets/high-value-datasets-per-category.rq new file mode 100644 index 0000000..c37c44c --- /dev/null +++ b/demo-queries/data-europa-eu/datasets/high-value-datasets-per-category.rq @@ -0,0 +1,15 @@ +#+ name: High-value datasets per category +#+ description: Counts datasets declared as high-value datasets (Implementing Regulation (EU) 2023/138) per HVD category. +PREFIX dcat: +PREFIX dct: +PREFIX foaf: +PREFIX skos: +PREFIX dcatap: + +SELECT ?category ?label (COUNT(DISTINCT ?dataset) AS ?datasets) WHERE { + ?dataset a dcat:Dataset ; + dcatap:hvdCategory ?category . + OPTIONAL { ?category skos:prefLabel ?label . FILTER(LANG(?label) = "en") } +} +GROUP BY ?category ?label +ORDER BY DESC(?datasets) diff --git a/demo-queries/data-europa-eu/datasets/top-publishers-of-transport-data.rq b/demo-queries/data-europa-eu/datasets/top-publishers-of-transport-data.rq new file mode 100644 index 0000000..61e81d7 --- /dev/null +++ b/demo-queries/data-europa-eu/datasets/top-publishers-of-transport-data.rq @@ -0,0 +1,15 @@ +#+ name: Top publishers of transport data +#+ description: Organisations publishing the most datasets in the Transport theme. +PREFIX dcat: +PREFIX dct: +PREFIX foaf: +PREFIX theme: + +SELECT ?publisher (SAMPLE(?name) AS ?publisherName) (COUNT(DISTINCT ?dataset) AS ?datasets) WHERE { + ?dataset dcat:theme theme:TRAN ; + dct:publisher ?publisher . + OPTIONAL { ?publisher foaf:name ?name } +} +GROUP BY ?publisher +ORDER BY DESC(?datasets) +LIMIT 25 diff --git a/demo-queries/data-europa-eu/datasets/transport-datasets.rq b/demo-queries/data-europa-eu/datasets/transport-datasets.rq new file mode 100644 index 0000000..626e088 --- /dev/null +++ b/demo-queries/data-europa-eu/datasets/transport-datasets.rq @@ -0,0 +1,15 @@ +#+ name: Transport datasets +#+ description: Datasets classified under the DCAT-AP data theme 'Transport' (TRAN). +PREFIX dcat: +PREFIX dct: +PREFIX foaf: +PREFIX theme: + +SELECT ?dataset ?title ?modified WHERE { + ?dataset a dcat:Dataset ; + dcat:theme theme:TRAN ; + dct:title ?title . + FILTER(LANGMATCHES(LANG(?title), "en")) + OPTIONAL { ?dataset dct:modified ?modified } +} +LIMIT 200 diff --git a/demo-queries/dbpedia/abstract-of-a-resource.rq b/demo-queries/dbpedia/abstract-of-a-resource.rq new file mode 100644 index 0000000..7fbdaa1 --- /dev/null +++ b/demo-queries/dbpedia/abstract-of-a-resource.rq @@ -0,0 +1,11 @@ +#+ name: Abstract of a resource in several languages +#+ description: The DBpedia abstract of 'Semantic Web' in English, French, German and Dutch. +PREFIX dbo: +PREFIX dbr: +PREFIX rdfs: + +SELECT ?language ?abstract WHERE { + dbr:Semantic_Web dbo:abstract ?abstract . + BIND(LANG(?abstract) AS ?language) + FILTER(?language IN ("en", "fr", "de", "nl")) +} diff --git a/demo-queries/dbpedia/ask-is-brussels-a-capital.rq b/demo-queries/dbpedia/ask-is-brussels-a-capital.rq new file mode 100644 index 0000000..6aa3e37 --- /dev/null +++ b/demo-queries/dbpedia/ask-is-brussels-a-capital.rq @@ -0,0 +1,9 @@ +#+ name: ASK: is Brussels a capital? +#+ description: An ASK query returns true or false. Checks whether some country has Brussels as its capital. +PREFIX dbo: +PREFIX dbr: +PREFIX rdfs: + +ASK { + ?country dbo:capital dbr:Brussels . +} diff --git a/demo-queries/dbpedia/castles-in-belgium-map.rq b/demo-queries/dbpedia/castles-in-belgium-map.rq new file mode 100644 index 0000000..d4a07eb --- /dev/null +++ b/demo-queries/dbpedia/castles-in-belgium-map.rq @@ -0,0 +1,18 @@ +#+ name: Map: castles in Belgium +#+ description: Castles located in Belgium with their coordinates, built as WKT points from geo:lat/geo:long. +PREFIX dbo: +PREFIX dbr: +PREFIX rdfs: +PREFIX geo: +PREFIX gsp: +PREFIX dct: +PREFIX dbc: + +SELECT ?castle ?name ?wkt WHERE { + ?castle dct:subject/? dbc:Castles_in_Belgium ; + rdfs:label ?name ; + geo:lat ?lat ; + geo:long ?long . + FILTER(LANG(?name) = "en") + BIND(STRDT(CONCAT("POINT(", STR(?long), " ", STR(?lat), ")"), gsp:wktLiteral) AS ?wkt) +} diff --git a/demo-queries/dbpedia/football-clubs-by-stadium-capacity.rq b/demo-queries/dbpedia/football-clubs-by-stadium-capacity.rq new file mode 100644 index 0000000..b49acce --- /dev/null +++ b/demo-queries/dbpedia/football-clubs-by-stadium-capacity.rq @@ -0,0 +1,15 @@ +#+ name: Football clubs by stadium capacity +#+ description: Belgian football clubs with their home ground and its capacity. +PREFIX dbo: +PREFIX dbr: +PREFIX rdfs: + +SELECT ?club ?ground ?capacity WHERE { + ?club a dbo:SoccerClub ; + dbo:ground ?ground . + ?ground dbo:location|dbo:city ?location ; + dbo:seatingCapacity ?capacity . + ?location dbo:country dbr:Belgium . +} +ORDER BY DESC(?capacity) +LIMIT 50 diff --git a/demo-queries/dbpedia/most-populated-european-capitals.rq b/demo-queries/dbpedia/most-populated-european-capitals.rq new file mode 100644 index 0000000..be6de35 --- /dev/null +++ b/demo-queries/dbpedia/most-populated-european-capitals.rq @@ -0,0 +1,15 @@ +#+ name: Most populated European capitals +#+ description: Capital cities of European countries with their population. +PREFIX dbo: +PREFIX dbr: +PREFIX rdfs: +PREFIX dct: +PREFIX dbc: + +SELECT DISTINCT ?country ?capital ?population WHERE { + ?country dct:subject dbc:Countries_in_Europe ; + dbo:capital ?capital . + ?capital dbo:populationTotal ?population . +} +ORDER BY DESC(?population) +LIMIT 50 diff --git a/demo-queries/dbpedia/ontology-class-hierarchy.rq b/demo-queries/dbpedia/ontology-class-hierarchy.rq new file mode 100644 index 0000000..442cf89 --- /dev/null +++ b/demo-queries/dbpedia/ontology-class-hierarchy.rq @@ -0,0 +1,15 @@ +#+ name: DBpedia ontology: subclasses of Place +#+ description: Explores the DBpedia ontology: all (transitive) subclasses of dbo:Place with their direct superclass. +PREFIX dbo: +PREFIX dbr: +PREFIX rdfs: +PREFIX owl: + +SELECT ?class ?label ?superClass WHERE { + ?class rdfs:subClassOf+ dbo:Place ; + rdfs:subClassOf ?superClass ; + rdfs:label ?label . + FILTER(LANG(?label) = "en") + FILTER(STRSTARTS(STR(?superClass), STR(dbo:))) +} +ORDER BY ?superClass ?label diff --git a/demo-queries/dbpedia/programming-languages-influence-graph.rq b/demo-queries/dbpedia/programming-languages-influence-graph.rq new file mode 100644 index 0000000..8db1b2f --- /dev/null +++ b/demo-queries/dbpedia/programming-languages-influence-graph.rq @@ -0,0 +1,23 @@ +#+ name: Programming language influences (graph) +#+ description: CONSTRUCTs the 'influenced by' network between programming languages. Visualise it with the Graph plugin. +PREFIX dbo: +PREFIX dbr: +PREFIX rdfs: + +CONSTRUCT { + ?language dbo:influencedBy ?influence . + ?language rdfs:label ?label . + ?influence rdfs:label ?influenceLabel . +} +WHERE { + VALUES ?language { + + + + + dbr:JavaScript + } + ?language dbo:influencedBy ?influence . + ?language rdfs:label ?label . FILTER(LANG(?label) = "en") + ?influence rdfs:label ?influenceLabel . FILTER(LANG(?influenceLabel) = "en") +} diff --git a/demo-queries/dbpedia/railway-companies-in-europe.rq b/demo-queries/dbpedia/railway-companies-in-europe.rq new file mode 100644 index 0000000..24bdc43 --- /dev/null +++ b/demo-queries/dbpedia/railway-companies-in-europe.rq @@ -0,0 +1,16 @@ +#+ name: European railway companies +#+ description: Railway companies from DBpedia with their country and founding year. +PREFIX dbo: +PREFIX dbr: +PREFIX rdfs: + +SELECT ?company ?name ?country ?founded WHERE { + ?company a dbo:Company ; + dbo:industry dbr:Rail_transport ; + rdfs:label ?name . + FILTER(LANG(?name) = "en") + OPTIONAL { ?company dbo:locationCountry ?country } + OPTIONAL { ?company dbo:foundingYear ?founded } +} +ORDER BY ?name +LIMIT 200 diff --git a/demo-queries/era-rinf/explore/classes-and-instance-counts.rq b/demo-queries/era-rinf/explore/classes-and-instance-counts.rq new file mode 100644 index 0000000..4abe34b --- /dev/null +++ b/demo-queries/era-rinf/explore/classes-and-instance-counts.rq @@ -0,0 +1,8 @@ +#+ name: Classes and instance counts +#+ description: First step on an unknown graph: which classes are used and how many instances does each have? +SELECT ?class (COUNT(?s) AS ?instances) WHERE { + ?s a ?class . +} +GROUP BY ?class +ORDER BY DESC(?instances) +LIMIT 100 diff --git a/demo-queries/era-rinf/explore/describe-an-operational-point.rq b/demo-queries/era-rinf/explore/describe-an-operational-point.rq new file mode 100644 index 0000000..d2ae933 --- /dev/null +++ b/demo-queries/era-rinf/explore/describe-an-operational-point.rq @@ -0,0 +1,12 @@ +#+ name: Describe an operational point +#+ description: DESCRIBE returns all triples about a resource. Shows one Belgian operational point; switch to the Graph plugin to explore it. +PREFIX era: +PREFIX skos: +PREFIX rdfs: +PREFIX country: + +DESCRIBE ?op WHERE { + ?op a era:OperationalPoint ; + era:inCountry country:BEL . +} +LIMIT 1 diff --git a/demo-queries/era-rinf/explore/era-ontology-classes.rq b/demo-queries/era-rinf/explore/era-ontology-classes.rq new file mode 100644 index 0000000..661bbce --- /dev/null +++ b/demo-queries/era-rinf/explore/era-ontology-classes.rq @@ -0,0 +1,14 @@ +#+ name: ERA ontology classes +#+ description: Lists the classes of the ERA vocabulary (http://data.europa.eu/949/) with their English label and definition. +PREFIX era: +PREFIX skos: +PREFIX rdfs: +PREFIX owl: + +SELECT ?class ?label ?definition WHERE { + ?class a owl:Class . + FILTER(STRSTARTS(STR(?class), STR(era:))) + OPTIONAL { ?class rdfs:label ?label . FILTER(LANGMATCHES(LANG(?label), "en") || LANG(?label) = "") } + OPTIONAL { ?class skos:definition ?definition . FILTER(LANGMATCHES(LANG(?definition), "en") || LANG(?definition) = "") } +} +ORDER BY ?class diff --git a/demo-queries/era-rinf/explore/properties-of-operational-points.rq b/demo-queries/era-rinf/explore/properties-of-operational-points.rq new file mode 100644 index 0000000..5a92824 --- /dev/null +++ b/demo-queries/era-rinf/explore/properties-of-operational-points.rq @@ -0,0 +1,12 @@ +#+ name: Properties used by operational points +#+ description: Lists every property used on era:OperationalPoint resources with how often it occurs. +PREFIX era: +PREFIX skos: +PREFIX rdfs: + +SELECT ?property (COUNT(*) AS ?usage) WHERE { + ?op a era:OperationalPoint ; + ?property ?value . +} +GROUP BY ?property +ORDER BY DESC(?usage) diff --git a/demo-queries/era-rinf/explore/skos-concept-schemes.rq b/demo-queries/era-rinf/explore/skos-concept-schemes.rq new file mode 100644 index 0000000..18fa948 --- /dev/null +++ b/demo-queries/era-rinf/explore/skos-concept-schemes.rq @@ -0,0 +1,12 @@ +#+ name: SKOS concept schemes +#+ description: The ERA knowledge graph uses SKOS code lists (operational point types, energy supply systems, ...). Lists every concept scheme and its size. +PREFIX era: +PREFIX skos: +PREFIX rdfs: + +SELECT ?scheme ?schemeLabel (COUNT(?concept) AS ?concepts) WHERE { + ?concept skos:inScheme ?scheme . + OPTIONAL { ?scheme skos:prefLabel|rdfs:label ?schemeLabel } +} +GROUP BY ?scheme ?schemeLabel +ORDER BY ?scheme diff --git a/demo-queries/era-rinf/lines-and-tracks/energy-supply-systems.rq b/demo-queries/era-rinf/lines-and-tracks/energy-supply-systems.rq new file mode 100644 index 0000000..60ef711 --- /dev/null +++ b/demo-queries/era-rinf/lines-and-tracks/energy-supply-systems.rq @@ -0,0 +1,14 @@ +#+ name: Energy supply systems +#+ description: Counts tracks per energy supply system (e.g. AC 25kV-50Hz, DC 3kV) via their contact line system. +PREFIX era: +PREFIX skos: +PREFIX rdfs: + +SELECT ?system ?systemLabel (COUNT(DISTINCT ?track) AS ?tracks) WHERE { + ?track a era:Track ; + era:contactLineSystem ?cls . + ?cls era:energySupplySystem ?system . + OPTIONAL { ?system skos:prefLabel ?systemLabel . FILTER(LANGMATCHES(LANG(?systemLabel), "en")) } +} +GROUP BY ?system ?systemLabel +ORDER BY DESC(?tracks) diff --git a/demo-queries/era-rinf/lines-and-tracks/etcs-levels.rq b/demo-queries/era-rinf/lines-and-tracks/etcs-levels.rq new file mode 100644 index 0000000..5c768d1 --- /dev/null +++ b/demo-queries/era-rinf/lines-and-tracks/etcs-levels.rq @@ -0,0 +1,14 @@ +#+ name: ETCS levels +#+ description: Counts tracks per European Train Control System (ETCS) level. +PREFIX era: +PREFIX skos: +PREFIX rdfs: + +SELECT ?level ?levelLabel (COUNT(DISTINCT ?track) AS ?tracks) WHERE { + ?track a era:Track ; + era:etcsLevel ?etcs . + ?etcs era:etcsLevelType ?level . + OPTIONAL { ?level skos:prefLabel ?levelLabel . FILTER(LANGMATCHES(LANG(?levelLabel), "en")) } +} +GROUP BY ?level ?levelLabel +ORDER BY DESC(?tracks) diff --git a/demo-queries/era-rinf/lines-and-tracks/longest-sections-of-line.rq b/demo-queries/era-rinf/lines-and-tracks/longest-sections-of-line.rq new file mode 100644 index 0000000..e141612 --- /dev/null +++ b/demo-queries/era-rinf/lines-and-tracks/longest-sections-of-line.rq @@ -0,0 +1,15 @@ +#+ name: Longest sections of line +#+ description: The 50 longest sections of line with the names of the operational points at both ends. +PREFIX era: +PREFIX skos: +PREFIX rdfs: + +SELECT ?sol ?from ?to ?length ?country WHERE { + ?sol a era:SectionOfLine ; + era:lengthOfSectionOfLine ?length ; + era:opStart/era:opName ?from ; + era:opEnd/era:opName ?to . + OPTIONAL { ?sol era:inCountry ?country } +} +ORDER BY DESC(?length) +LIMIT 50 diff --git a/demo-queries/era-rinf/lines-and-tracks/longest-tunnels.rq b/demo-queries/era-rinf/lines-and-tracks/longest-tunnels.rq new file mode 100644 index 0000000..990a34a --- /dev/null +++ b/demo-queries/era-rinf/lines-and-tracks/longest-tunnels.rq @@ -0,0 +1,14 @@ +#+ name: Longest tunnels +#+ description: The longest railway tunnels registered in RINF. +PREFIX era: +PREFIX skos: +PREFIX rdfs: + +SELECT ?tunnel ?label ?length ?country WHERE { + ?tunnel a era:Tunnel ; + era:lengthOfTunnel ?length . + OPTIONAL { ?tunnel rdfs:label ?label } + OPTIONAL { ?tunnel era:inCountry ?country } +} +ORDER BY DESC(?length) +LIMIT 50 diff --git a/demo-queries/era-rinf/lines-and-tracks/maximum-speed-distribution.rq b/demo-queries/era-rinf/lines-and-tracks/maximum-speed-distribution.rq new file mode 100644 index 0000000..0738725 --- /dev/null +++ b/demo-queries/era-rinf/lines-and-tracks/maximum-speed-distribution.rq @@ -0,0 +1,12 @@ +#+ name: Maximum permitted speed distribution +#+ description: How many tracks allow each maximum permitted speed (km/h)? +PREFIX era: +PREFIX skos: +PREFIX rdfs: + +SELECT ?maxSpeed (COUNT(?track) AS ?tracks) WHERE { + ?track a era:Track ; + era:maximumPermittedSpeed ?maxSpeed . +} +GROUP BY ?maxSpeed +ORDER BY DESC(?maxSpeed) diff --git a/demo-queries/era-rinf/lines-and-tracks/network-graph-luxembourg.rq b/demo-queries/era-rinf/lines-and-tracks/network-graph-luxembourg.rq new file mode 100644 index 0000000..b76c774 --- /dev/null +++ b/demo-queries/era-rinf/lines-and-tracks/network-graph-luxembourg.rq @@ -0,0 +1,21 @@ +#+ name: Network graph of Luxembourg +#+ description: CONSTRUCT that turns sections of line into direct links between operational points. Open the result in the Graph plugin. +PREFIX era: +PREFIX skos: +PREFIX rdfs: +PREFIX country: +PREFIX ex: + +CONSTRUCT { + ?start ex:connectedTo ?end ; + rdfs:label ?startName . + ?end rdfs:label ?endName . +} +WHERE { + ?sol a era:SectionOfLine ; + era:inCountry country:LUX ; + era:opStart ?start ; + era:opEnd ?end . + ?start era:opName ?startName . + ?end era:opName ?endName . +} diff --git a/demo-queries/era-rinf/lines-and-tracks/network-length-per-country.rq b/demo-queries/era-rinf/lines-and-tracks/network-length-per-country.rq new file mode 100644 index 0000000..dead5df --- /dev/null +++ b/demo-queries/era-rinf/lines-and-tracks/network-length-per-country.rq @@ -0,0 +1,13 @@ +#+ name: Network length per country +#+ description: Sums era:lengthOfSectionOfLine per country to estimate the size of each national network. +PREFIX era: +PREFIX skos: +PREFIX rdfs: + +SELECT ?country (COUNT(?sol) AS ?sectionsOfLine) (ROUND(SUM(?length)) AS ?totalLength) WHERE { + ?sol a era:SectionOfLine ; + era:inCountry ?country ; + era:lengthOfSectionOfLine ?length . +} +GROUP BY ?country +ORDER BY DESC(?totalLength) diff --git a/demo-queries/era-rinf/maps/operational-points-in-belgium.rq b/demo-queries/era-rinf/maps/operational-points-in-belgium.rq new file mode 100644 index 0000000..4e6c1ca --- /dev/null +++ b/demo-queries/era-rinf/maps/operational-points-in-belgium.rq @@ -0,0 +1,14 @@ +#+ name: Map: operational points in Belgium +#+ description: Operational points with their WKT geometry, ready for the Geo plugin. +PREFIX era: +PREFIX skos: +PREFIX rdfs: +PREFIX geo: +PREFIX country: + +SELECT ?op ?name ?wkt WHERE { + ?op a era:OperationalPoint ; + era:inCountry country:BEL ; + era:opName ?name ; + geo:hasGeometry/geo:asWKT ?wkt . +} diff --git a/demo-queries/era-rinf/maps/operational-points-near-brussels.rq b/demo-queries/era-rinf/maps/operational-points-near-brussels.rq new file mode 100644 index 0000000..d379c67 --- /dev/null +++ b/demo-queries/era-rinf/maps/operational-points-near-brussels.rq @@ -0,0 +1,18 @@ +#+ name: Map: operational points around Brussels +#+ description: Filters operational points on a bounding box by extracting longitude/latitude from the WKT string, so it works without a spatial index. +PREFIX era: +PREFIX skos: +PREFIX rdfs: +PREFIX geo: +PREFIX xsd: + +SELECT ?op ?name ?wkt WHERE { + ?op a era:OperationalPoint ; + era:opName ?name ; + geo:hasGeometry/geo:asWKT ?wkt . + BIND(REPLACE(STR(?wkt), "^.*POINT\\s*\\(\\s*([-0-9.]+)\\s+([-0-9.]+).*$", "$1", "i") AS ?lonStr) + BIND(REPLACE(STR(?wkt), "^.*POINT\\s*\\(\\s*([-0-9.]+)\\s+([-0-9.]+).*$", "$2", "i") AS ?latStr) + BIND(xsd:decimal(?lonStr) AS ?lon) + BIND(xsd:decimal(?latStr) AS ?lat) + FILTER(?lon > 4.20 && ?lon < 4.50 && ?lat > 50.76 && ?lat < 50.92) +} diff --git a/demo-queries/era-rinf/maps/sections-of-line-in-switzerland.rq b/demo-queries/era-rinf/maps/sections-of-line-in-switzerland.rq new file mode 100644 index 0000000..d5574a2 --- /dev/null +++ b/demo-queries/era-rinf/maps/sections-of-line-in-switzerland.rq @@ -0,0 +1,14 @@ +#+ name: Map: sections of line in Switzerland +#+ description: Line geometries of Swiss sections of line, drawn as linestrings by the Geo plugin. +PREFIX era: +PREFIX skos: +PREFIX rdfs: +PREFIX geo: +PREFIX country: + +SELECT ?sol ?length ?wkt WHERE { + ?sol a era:SectionOfLine ; + era:inCountry country:CHE ; + geo:hasGeometry/geo:asWKT ?wkt . + OPTIONAL { ?sol era:lengthOfSectionOfLine ?length } +} diff --git a/demo-queries/era-rinf/operational-points/border-points.rq b/demo-queries/era-rinf/operational-points/border-points.rq new file mode 100644 index 0000000..737dc48 --- /dev/null +++ b/demo-queries/era-rinf/operational-points/border-points.rq @@ -0,0 +1,15 @@ +#+ name: Border points +#+ description: Operational points whose type label mentions a border, grouped by country. +PREFIX era: +PREFIX skos: +PREFIX rdfs: + +SELECT ?country ?op ?name ?typeLabel WHERE { + ?op a era:OperationalPoint ; + era:opName ?name ; + era:opType ?type . + ?type skos:prefLabel ?typeLabel . + FILTER(CONTAINS(LCASE(STR(?typeLabel)), "border")) + OPTIONAL { ?op era:inCountry ?country } +} +ORDER BY ?country ?name diff --git a/demo-queries/era-rinf/operational-points/operational-point-types.rq b/demo-queries/era-rinf/operational-points/operational-point-types.rq new file mode 100644 index 0000000..79124f8 --- /dev/null +++ b/demo-queries/era-rinf/operational-points/operational-point-types.rq @@ -0,0 +1,13 @@ +#+ name: Operational point types +#+ description: Distribution of operational point types across Europe, using the labels of the SKOS type concepts. +PREFIX era: +PREFIX skos: +PREFIX rdfs: + +SELECT ?type ?typeLabel (COUNT(DISTINCT ?op) AS ?operationalPoints) WHERE { + ?op a era:OperationalPoint ; + era:opType ?type . + OPTIONAL { ?type skos:prefLabel ?typeLabel . FILTER(LANGMATCHES(LANG(?typeLabel), "en")) } +} +GROUP BY ?type ?typeLabel +ORDER BY DESC(?operationalPoints) diff --git a/demo-queries/era-rinf/operational-points/operational-points-per-country.rq b/demo-queries/era-rinf/operational-points/operational-points-per-country.rq new file mode 100644 index 0000000..0c3855a --- /dev/null +++ b/demo-queries/era-rinf/operational-points/operational-points-per-country.rq @@ -0,0 +1,12 @@ +#+ name: Operational points per country +#+ description: Counts operational points (stations, junctions, border points, ...) per member state. +PREFIX era: +PREFIX skos: +PREFIX rdfs: + +SELECT ?country (COUNT(DISTINCT ?op) AS ?operationalPoints) WHERE { + ?op a era:OperationalPoint ; + era:inCountry ?country . +} +GROUP BY ?country +ORDER BY DESC(?operationalPoints) diff --git a/demo-queries/era-rinf/operational-points/search-by-name.rq b/demo-queries/era-rinf/operational-points/search-by-name.rq new file mode 100644 index 0000000..7462802 --- /dev/null +++ b/demo-queries/era-rinf/operational-points/search-by-name.rq @@ -0,0 +1,15 @@ +#+ name: Search operational points by name +#+ description: Case-insensitive name search. Change the search string to find any station or junction in Europe. +PREFIX era: +PREFIX skos: +PREFIX rdfs: + +SELECT ?op ?name ?uopid ?country WHERE { + ?op a era:OperationalPoint ; + era:opName ?name . + FILTER(CONTAINS(LCASE(STR(?name)), "brussel")) + OPTIONAL { ?op era:uopid ?uopid } + OPTIONAL { ?op era:inCountry ?country } +} +ORDER BY ?name +LIMIT 100 diff --git a/demo-queries/era-rinf/operational-points/stations-in-belgium.rq b/demo-queries/era-rinf/operational-points/stations-in-belgium.rq new file mode 100644 index 0000000..b212510 --- /dev/null +++ b/demo-queries/era-rinf/operational-points/stations-in-belgium.rq @@ -0,0 +1,19 @@ +#+ name: Operational points in Belgium +#+ description: Name, unique OP id (UOPID) and type of all operational points in Belgium. +PREFIX era: +PREFIX skos: +PREFIX rdfs: +PREFIX country: + +SELECT ?op ?name ?uopid ?typeLabel WHERE { + ?op a era:OperationalPoint ; + era:inCountry country:BEL ; + era:opName ?name . + OPTIONAL { ?op era:uopid ?uopid } + OPTIONAL { + ?op era:opType ?type . + ?type skos:prefLabel ?typeLabel . + FILTER(LANGMATCHES(LANG(?typeLabel), "en")) + } +} +ORDER BY ?name diff --git a/demo-queries/geosparql/area-and-length.rq b/demo-queries/geosparql/area-and-length.rq new file mode 100644 index 0000000..285355e --- /dev/null +++ b/demo-queries/geosparql/area-and-length.rq @@ -0,0 +1,18 @@ +#+ name: Area and length of geometries +#+ description: Computes polygon area and line length in metric units with the GeoSPARQL 1.1 functions geof:area and geof:length. +PREFIX geo: +PREFIX geof: +PREFIX uom: + +# Geometries in ETRS89 / Belgian Lambert 2008 (EPSG:3812), a metric CRS, +# so area and length are directly expressed in metres. +SELECT ?label ?wkt ?area_m2 ?length_m WHERE { + VALUES (?label ?wkt) { + ("1 km square" + " POLYGON((650000 670000, 651000 670000, 651000 671000, 650000 671000, 650000 670000))"^^geo:wktLiteral) + ("5 km track" + " LINESTRING(650000 670000, 653000 674000)"^^geo:wktLiteral) + } + OPTIONAL { BIND(geof:area(?wkt, uom:metre) AS ?area_m2) } + OPTIONAL { BIND(geof:length(?wkt, uom:metre) AS ?length_m) } +} diff --git a/demo-queries/geosparql/buffer-and-envelope.rq b/demo-queries/geosparql/buffer-and-envelope.rq new file mode 100644 index 0000000..2f5817b --- /dev/null +++ b/demo-queries/geosparql/buffer-and-envelope.rq @@ -0,0 +1,24 @@ +#+ name: Buffer and envelope +#+ description: Creates WKT points, buffers them with geof:buffer and returns the bounding box with geof:envelope. From the apache-jena-fuseki-geosparql examples. +PREFIX geo: +PREFIX geof: +PREFIX uom: + +# Test basic GeoSPARQL geometry functions without UOM +SELECT ?test_name ?point ?buffer_geom ?envelope_geom WHERE { + + VALUES (?test_name ?lat ?lon ?buffer_distance) { + ("Amsterdam Central" 52.3791 4.9003 0.01) + ("Rotterdam Port" 51.9225 4.4792 0.02) + ("Utrecht Center" 52.0907 5.1214 0.015) + } + # Create point geometry from coordinates + BIND(STRDT(CONCAT(" POINT(", STR(?lat), " ", STR(?lon), ")"), geo:wktLiteral) AS ?point) + + # Create a buffer around the point (simple circular buffer) + BIND(geof:buffer(?point, ?buffer_distance, uom:degree) AS ?buffer_geom) + + # Get the envelope (bounding box) of the buffered geometry + BIND(geof:envelope(?buffer_geom) AS ?envelope_geom) +} +ORDER BY ?test_name diff --git a/demo-queries/geosparql/distance-between-cities.rq b/demo-queries/geosparql/distance-between-cities.rq new file mode 100644 index 0000000..052f8ea --- /dev/null +++ b/demo-queries/geosparql/distance-between-cities.rq @@ -0,0 +1,19 @@ +#+ name: Distance between cities +#+ description: Great-circle distance in km between city pairs with spatialF:distance (Jena vendor function). From the apache-jena-fuseki-geosparql examples. +PREFIX geo: +PREFIX geof: +PREFIX uom: +PREFIX spatialf: + +# Test GeoSPARQL distance calculation between two cities +SELECT ?city1 ?city2 ?distance_km WHERE { + VALUES (?city1 ?point1 ?city2 ?point2) { + ("London" " POINT(-0.1276 51.5074)"^^geo:wktLiteral "Paris" " POINT(2.3522 48.8566)"^^geo:wktLiteral) + ("New York" " POINT(-74.0060 40.7128)"^^geo:wktLiteral "Boston" " POINT(-71.0588 42.3601)"^^geo:wktLiteral) + ("Amsterdam" " POINT(4.9041 52.3676)"^^geo:wktLiteral "Brussels" " POINT(4.3517 50.8503)"^^geo:wktLiteral) + } + + # Calculate distance in kilometers + BIND(spatialf:distance(?point1, ?point2, uom:metre) / 1000 AS ?distance_km) +} +ORDER BY ?distance_km diff --git a/demo-queries/geosparql/geometry-properties.rq b/demo-queries/geosparql/geometry-properties.rq new file mode 100644 index 0000000..0002689 --- /dev/null +++ b/demo-queries/geosparql/geometry-properties.rq @@ -0,0 +1,19 @@ +#+ name: Geometry properties and conversions +#+ description: Inspects geometries with geof:dimension, geof:isEmpty, geof:convexHull, geof:union and geof:asGeoJSON. +PREFIX geo: +PREFIX geof: + +SELECT ?label ?dimension ?hull ?union ?geojson WHERE { + VALUES (?label ?a ?b) { + ("two squares" + "POLYGON((4.0 50.0, 4.1 50.0, 4.1 50.1, 4.0 50.1, 4.0 50.0))"^^geo:wktLiteral + "POLYGON((4.05 50.05, 4.15 50.05, 4.15 50.15, 4.05 50.15, 4.05 50.05))"^^geo:wktLiteral) + ("point and line" + "POINT(4.35 50.85)"^^geo:wktLiteral + "LINESTRING(4.30 50.80, 4.40 50.90)"^^geo:wktLiteral) + } + BIND(geof:dimension(?a) AS ?dimension) + BIND(geof:convexHull(?b) AS ?hull) + BIND(geof:union(?a, ?b) AS ?union) + OPTIONAL { BIND(geof:asGeoJSON(?a) AS ?geojson) } +} diff --git a/demo-queries/geosparql/nearest-station.rq b/demo-queries/geosparql/nearest-station.rq new file mode 100644 index 0000000..1f4d89f --- /dev/null +++ b/demo-queries/geosparql/nearest-station.rq @@ -0,0 +1,21 @@ +#+ name: Nearest station to a point +#+ description: Ranks stations by distance to a given location using spatialF:distance and returns the closest ones. +PREFIX geo: +PREFIX uom: +PREFIX spatialF: + +SELECT ?station ?location ?distance_km WHERE { + # Where am I? (Grand-Place, Brussels) + BIND("POINT(4.3525 50.8467)"^^geo:wktLiteral AS ?here) + + VALUES (?station ?location) { + ("Brussels-Central" "POINT(4.3571 50.8455)"^^geo:wktLiteral) + ("Brussels-North" "POINT(4.3606 50.8603)"^^geo:wktLiteral) + ("Brussels-South" "POINT(4.3360 50.8358)"^^geo:wktLiteral) + ("Brussels-Luxembourg" "POINT(4.3739 50.8383)"^^geo:wktLiteral) + ("Brussels-Schuman" "POINT(4.3806 50.8431)"^^geo:wktLiteral) + } + BIND(ROUND(spatialF:distance(?here, ?location, uom:metre)) / 1000 AS ?distance_km) +} +ORDER BY ?distance_km +LIMIT 3 diff --git a/demo-queries/geosparql/spatial-relations.rq b/demo-queries/geosparql/spatial-relations.rq new file mode 100644 index 0000000..14ff7c7 --- /dev/null +++ b/demo-queries/geosparql/spatial-relations.rq @@ -0,0 +1,33 @@ +#+ name: Spatial relations (within, intersects, contains) +#+ description: Tests Simple Features relations geof:sfWithin, geof:sfIntersects and geof:sfContains on inline geometries. From the apache-jena-fuseki-geosparql examples. +PREFIX geo: +PREFIX geof: + +# Test GeoSPARQL spatial relationships without UOM namespace +# This query tests contains, intersects, and within relationships +SELECT ?relation ?geometry1_label ?geometry2_label ?result WHERE { + + # Define test geometries + VALUES (?geometry1_label ?geometry1 ?geometry2_label ?geometry2 ?relation) { + # Point within polygon test + ("City Center" "POINT(4.9041 52.3676)"^^geo:wktLiteral "Amsterdam Bounds" "POLYGON((4.8 52.3, 5.0 52.3, 5.0 52.4, 4.8 52.4, 4.8 52.3))"^^geo:wktLiteral "within") + + # Line intersects polygon test + ("Highway" "LINESTRING(4.85 52.32, 4.95 52.38)"^^geo:wktLiteral "Amsterdam Bounds" "POLYGON((4.8 52.3, 5.0 52.3, 5.0 52.4, 4.8 52.4, 4.8 52.3))"^^geo:wktLiteral "intersects") + + # Polygon contains point test + ("Large Area" "POLYGON((4.7 52.2, 5.1 52.2, 5.1 52.5, 4.7 52.5, 4.7 52.2))"^^geo:wktLiteral "Small Point" "POINT(4.9 52.35)"^^geo:wktLiteral "contains") + + # Buffer test (point with buffer intersects line) + ("Station" "POINT(4.9 52.37)"^^geo:wktLiteral "Train Line" "LINESTRING(4.88 52.36, 4.92 52.38)"^^geo:wktLiteral "intersects") + } + + # Test the spatial relationship based on the relation type + BIND( + IF(?relation = "within", geof:sfWithin(?geometry1, ?geometry2), + IF(?relation = "intersects", geof:sfIntersects(?geometry1, ?geometry2), + IF(?relation = "contains", geof:sfContains(?geometry1, ?geometry2), + false))) AS ?result + ) +} +ORDER BY ?relation ?geometry1_label diff --git a/demo-queries/geosparql/transform-crs.rq b/demo-queries/geosparql/transform-crs.rq new file mode 100644 index 0000000..598a434 --- /dev/null +++ b/demo-queries/geosparql/transform-crs.rq @@ -0,0 +1,18 @@ +#+ name: Transform coordinate reference system +#+ description: Transforms WGS84 points to ETRS89 / UTM zone 32N with spatialF:transformSRS. From the apache-jena-fuseki-geosparql examples. +PREFIX geo: +PREFIX geof: +PREFIX spatialf: + +# Test transforming between coordinate systems using Jena vendor functions +SELECT * WHERE { + VALUES (?test_name ?lat ?lon) { + ("Amsterdam Central" 52.3791 4.9003) + ("Rotterdam Port" 51.9225 4.4792) + ("Utrecht Center" 52.0907 5.1214) + } + # Create point geometry from coordinates + BIND(STRDT(CONCAT(" POINT(", STR(?lat), " ", STR(?lon), ")"), geo:wktLiteral) AS ?point_wgs84) + BIND(spatialf:transformSRS(?point_wgs84, "http://www.opengis.net/def/crs/EPSG/0/25832") as ?point_utm_zone_32N) +} +ORDER BY ?test_name diff --git a/demo-queries/geosparql/wkt-shapes-on-a-map.rq b/demo-queries/geosparql/wkt-shapes-on-a-map.rq new file mode 100644 index 0000000..675aed3 --- /dev/null +++ b/demo-queries/geosparql/wkt-shapes-on-a-map.rq @@ -0,0 +1,14 @@ +#+ name: WKT shapes on a map +#+ description: Returns points, lines and polygons as geo:wktLiteral so the Geo plugin can draw them. +PREFIX geo: + +SELECT ?label ?kind ?wkt WHERE { + VALUES (?label ?kind ?wkt) { + ("Brussels-Central station" "point" + "POINT(4.3571 50.8455)"^^geo:wktLiteral) + ("Brussels North-South rail link" "line" + "LINESTRING(4.3606 50.8603, 4.3571 50.8455, 4.3360 50.8358)"^^geo:wktLiteral) + ("Brussels pentagon (approx.)" "polygon" + "POLYGON((4.3434 50.8540, 4.3700 50.8560, 4.3780 50.8420, 4.3560 50.8340, 4.3400 50.8430, 4.3434 50.8540))"^^geo:wktLiteral) + } +} diff --git a/demo-queries/getting-started/aggregation.rq b/demo-queries/getting-started/aggregation.rq new file mode 100644 index 0000000..9890567 --- /dev/null +++ b/demo-queries/getting-started/aggregation.rq @@ -0,0 +1,23 @@ +#+ name: Aggregation with GROUP BY +#+ description: COUNT, SUM, AVG, MIN, MAX and GROUP_CONCAT over an inline table, with HAVING. +PREFIX xsd: + +SELECT ?country + (COUNT(?city) AS ?cities) + (SUM(?population) AS ?totalPopulation) + (ROUND(AVG(?population)) AS ?avgPopulation) + (MAX(?population) AS ?largest) + (GROUP_CONCAT(?city; separator=", ") AS ?cityList) +WHERE { + VALUES (?country ?city ?population) { + ("Belgium" "Brussels" 1222637) + ("Belgium" "Antwerp" 536079) + ("Belgium" "Ghent" 265086) + ("Netherlands" "Amsterdam" 931298) + ("Netherlands" "Rotterdam" 664311) + ("Luxembourg" "Luxembourg" 134714) + } +} +GROUP BY ?country +HAVING (COUNT(?city) > 1) +ORDER BY DESC(?totalPopulation) diff --git a/demo-queries/getting-started/construct-a-small-graph.rq b/demo-queries/getting-started/construct-a-small-graph.rq new file mode 100644 index 0000000..f9b8ed3 --- /dev/null +++ b/demo-queries/getting-started/construct-a-small-graph.rq @@ -0,0 +1,22 @@ +#+ name: CONSTRUCT a small graph +#+ description: Builds RDF triples from inline data. Open the result in the Graph plugin to see the network. +PREFIX ex: +PREFIX rdfs: + +# A tiny railway network: stations and the lines connecting them. +CONSTRUCT { + ?from a ex:Station ; rdfs:label ?fromLabel ; ex:connectedTo ?to . + ?to a ex:Station ; rdfs:label ?toLabel . +} +WHERE { + VALUES (?fromLabel ?toLabel) { + ("Brussels-South" "Antwerp-Central") + ("Brussels-South" "Liège-Guillemins") + ("Brussels-South" "Lille-Europe") + ("Antwerp-Central" "Rotterdam Centraal") + ("Liège-Guillemins" "Aachen Hbf") + ("Lille-Europe" "Paris-Nord") + } + BIND(IRI(CONCAT(STR(ex:), "station/", ENCODE_FOR_URI(?fromLabel))) AS ?from) + BIND(IRI(CONCAT(STR(ex:), "station/", ENCODE_FOR_URI(?toLabel))) AS ?to) +} diff --git a/demo-queries/getting-started/dates-and-numbers.rq b/demo-queries/getting-started/dates-and-numbers.rq new file mode 100644 index 0000000..4eeaabe --- /dev/null +++ b/demo-queries/getting-started/dates-and-numbers.rq @@ -0,0 +1,18 @@ +#+ name: Dates and numbers +#+ description: Date/time and numeric functions: NOW, YEAR, arithmetic on xsd:date, ROUND and IF. +PREFIX xsd: + +SELECT ?event ?date ?year ?daysAgo ?century ?era WHERE { + VALUES (?event ?date) { + ("Treaty of Rome signed" "1957-03-25"^^xsd:date) + ("Channel Tunnel opened" "1994-05-06"^^xsd:date) + ("Euro banknotes introduced" "2002-01-01"^^xsd:date) + ("SPARQL 1.1 became a recommendation" "2013-03-21"^^xsd:date) + } + BIND(YEAR(?date) AS ?year) + # Jena supports subtracting dates; the result is an xsd:duration + BIND(xsd:date(NOW()) - ?date AS ?daysAgo) + BIND(CEIL(?year / 100) AS ?century) + BIND(IF(?year < 2000, "20th century", "21st century") AS ?era) +} +ORDER BY ?date diff --git a/demo-queries/getting-started/demo-queries-per-endpoint.rq b/demo-queries/getting-started/demo-queries-per-endpoint.rq new file mode 100644 index 0000000..e6a9485 --- /dev/null +++ b/demo-queries/getting-started/demo-queries-per-endpoint.rq @@ -0,0 +1,14 @@ +#+ name: Demo queries per endpoint +#+ description: Meta query: counts the demo queries per SPARQL endpoint they are associated with. +PREFIX yasgui: +PREFIX dcterms: +PREFIX prov: +PREFIX sd: + +SELECT ?endpoint (COUNT(DISTINCT ?mq) AS ?queries) WHERE { + ?mq a yasgui:ManagedQuery . + ?version dcterms:isVersionOf ?mq ; + prov:used/sd:endpoint ?endpoint . +} +GROUP BY ?endpoint +ORDER BY DESC(?queries) diff --git a/demo-queries/getting-started/hello-world.rq b/demo-queries/getting-started/hello-world.rq new file mode 100644 index 0000000..0527967 --- /dev/null +++ b/demo-queries/getting-started/hello-world.rq @@ -0,0 +1,11 @@ +#+ name: Hello world +#+ description: The smallest useful SELECT: inline data with VALUES, no dataset needed. +# VALUES lets you put a small table of data directly in the query. +SELECT ?greeting ?language WHERE { + VALUES (?greeting ?language) { + ("Hello world" "English") + ("Hallo wereld" "Dutch") + ("Bonjour le monde" "French") + ("Hallo Welt" "German") + } +} diff --git a/demo-queries/getting-started/list-demo-queries.rq b/demo-queries/getting-started/list-demo-queries.rq new file mode 100644 index 0000000..faf7992 --- /dev/null +++ b/demo-queries/getting-started/list-demo-queries.rq @@ -0,0 +1,17 @@ +#+ name: List all demo queries +#+ description: Meta query: the demo workspace is itself stored as RDF in this Jena dataset. Lists every demo query, its folder and its endpoint. +PREFIX yasgui: +PREFIX dcterms: +PREFIX rdfs: +PREFIX prov: +PREFIX sd: + +SELECT ?folder ?query ?endpoint WHERE { + ?mq a yasgui:ManagedQuery ; + rdfs:label ?query ; + dcterms:isPartOf ?container . + OPTIONAL { ?container rdfs:label ?folder } + ?version dcterms:isVersionOf ?mq ; + prov:used/sd:endpoint ?endpoint . +} +ORDER BY ?folder ?query diff --git a/demo-queries/getting-started/string-functions.rq b/demo-queries/getting-started/string-functions.rq new file mode 100644 index 0000000..fe0c966 --- /dev/null +++ b/demo-queries/getting-started/string-functions.rq @@ -0,0 +1,13 @@ +#+ name: String functions +#+ description: BIND together with common SPARQL string functions (UCASE, STRLEN, SUBSTR, REPLACE, CONCAT, ENCODE_FOR_URI). +SELECT ?city ?upper ?length ?firstThree ?noSpaces ?slug ?iri WHERE { + VALUES ?city { "Brussels" "Den Haag" "Frankfurt am Main" "Luxembourg" } + + BIND(UCASE(?city) AS ?upper) + BIND(STRLEN(?city) AS ?length) + BIND(SUBSTR(?city, 1, 3) AS ?firstThree) + BIND(REPLACE(?city, " ", "") AS ?noSpaces) + BIND(LCASE(REPLACE(?city, " ", "-")) AS ?slug) + BIND(IRI(CONCAT("https://example.org/city/", ENCODE_FOR_URI(?slug))) AS ?iri) +} +ORDER BY ?length diff --git a/demo-queries/life-sciences/enzymes-with-ec-number.rq b/demo-queries/life-sciences/enzymes-with-ec-number.rq new file mode 100644 index 0000000..2d7c65e --- /dev/null +++ b/demo-queries/life-sciences/enzymes-with-ec-number.rq @@ -0,0 +1,15 @@ +#+ name: Human enzymes with an EC number +#+ description: Reviewed human proteins annotated with an Enzyme Commission (EC) number. +PREFIX up: +PREFIX taxon: +PREFIX rdfs: + +SELECT ?protein ?name ?ec WHERE { + ?protein a up:Protein ; + up:reviewed true ; + up:organism taxon:9606 ; + up:recommendedName ?recName . + ?recName up:fullName ?name ; + up:ecName ?ec . +} +LIMIT 200 diff --git a/demo-queries/life-sciences/protein-by-gene-name.rq b/demo-queries/life-sciences/protein-by-gene-name.rq new file mode 100644 index 0000000..e48fcdc --- /dev/null +++ b/demo-queries/life-sciences/protein-by-gene-name.rq @@ -0,0 +1,16 @@ +#+ name: Protein by gene name +#+ description: Finds the human protein encoded by the gene BRCA1, with its sequence length. +PREFIX up: +PREFIX taxon: +PREFIX rdfs: +PREFIX skos: + +SELECT ?protein ?name ?length WHERE { + ?protein a up:Protein ; + up:organism taxon:9606 ; + up:reviewed true ; + up:encodedBy/skos:prefLabel "BRCA1" ; + up:recommendedName/up:fullName ?name ; + up:sequence/rdf:value ?sequence . + BIND(STRLEN(?sequence) AS ?length) +} diff --git a/demo-queries/life-sciences/proteins-per-organism.rq b/demo-queries/life-sciences/proteins-per-organism.rq new file mode 100644 index 0000000..45367ee --- /dev/null +++ b/demo-queries/life-sciences/proteins-per-organism.rq @@ -0,0 +1,15 @@ +#+ name: Reviewed proteins per model organism +#+ description: Counts reviewed UniProt entries for a handful of model organisms. +PREFIX up: +PREFIX taxon: +PREFIX rdfs: + +SELECT ?organism ?name (COUNT(?protein) AS ?proteins) WHERE { + VALUES ?organism { taxon:9606 taxon:10090 taxon:7227 taxon:6239 taxon:559292 taxon:83333 taxon:3702 } + ?protein a up:Protein ; + up:reviewed true ; + up:organism ?organism . + ?organism up:scientificName ?name . +} +GROUP BY ?organism ?name +ORDER BY DESC(?proteins) diff --git a/demo-queries/life-sciences/reviewed-human-proteins.rq b/demo-queries/life-sciences/reviewed-human-proteins.rq new file mode 100644 index 0000000..862d27d --- /dev/null +++ b/demo-queries/life-sciences/reviewed-human-proteins.rq @@ -0,0 +1,14 @@ +#+ name: Reviewed human proteins +#+ description: Swiss-Prot (reviewed) proteins of Homo sapiens with their mnemonic and recommended name. +PREFIX up: +PREFIX taxon: +PREFIX rdfs: + +SELECT ?protein ?mnemonic ?name WHERE { + ?protein a up:Protein ; + up:reviewed true ; + up:organism taxon:9606 ; + up:mnemonic ?mnemonic ; + up:recommendedName/up:fullName ?name . +} +LIMIT 100 diff --git a/demo-queries/life-sciences/taxonomy-lineage.rq b/demo-queries/life-sciences/taxonomy-lineage.rq new file mode 100644 index 0000000..520d8db --- /dev/null +++ b/demo-queries/life-sciences/taxonomy-lineage.rq @@ -0,0 +1,11 @@ +#+ name: Taxonomic lineage +#+ description: The full taxonomic lineage of the honey bee (Apis mellifera, taxon 7460) using a property path. +PREFIX up: +PREFIX taxon: +PREFIX rdfs: + +SELECT ?ancestor ?name ?rank WHERE { + taxon:7460 rdfs:subClassOf+ ?ancestor . + ?ancestor up:scientificName ?name . + OPTIONAL { ?ancestor up:rank ?rank } +} diff --git a/demo-queries/openstreetmap/charging-stations-per-operator.rq b/demo-queries/openstreetmap/charging-stations-per-operator.rq new file mode 100644 index 0000000..c81fa79 --- /dev/null +++ b/demo-queries/openstreetmap/charging-stations-per-operator.rq @@ -0,0 +1,16 @@ +#+ name: EV charging stations per operator in Belgium +#+ description: Counts amenity=charging_station features in Belgium (relation 52411) per operator. +PREFIX osmkey: +PREFIX osmrel: +PREFIX ogc: +PREFIX geo: + +SELECT ?operator (COUNT(?station) AS ?stations) WHERE { + osmrel:52411 ogc:sfContains ?station . + ?station osmkey:amenity "charging_station" . + OPTIONAL { ?station osmkey:operator ?op } + BIND(COALESCE(?op, "(unknown)") AS ?operator) +} +GROUP BY ?operator +ORDER BY DESC(?stations) +LIMIT 25 diff --git a/demo-queries/openstreetmap/level-crossings-in-the-netherlands.rq b/demo-queries/openstreetmap/level-crossings-in-the-netherlands.rq new file mode 100644 index 0000000..d07d564 --- /dev/null +++ b/demo-queries/openstreetmap/level-crossings-in-the-netherlands.rq @@ -0,0 +1,15 @@ +#+ name: Level crossings in the Netherlands +#+ description: Counts railway level crossings per crossing type in the Netherlands (relation 47796). +PREFIX osmkey: +PREFIX osmrel: +PREFIX ogc: +PREFIX geo: + +SELECT ?crossingType (COUNT(?node) AS ?crossings) WHERE { + osmrel:47796 ogc:sfContains ?node . + ?node osmkey:railway "level_crossing" . + OPTIONAL { ?node osmkey:crossing:barrier ?barrier } + BIND(COALESCE(?barrier, "(not tagged)") AS ?crossingType) +} +GROUP BY ?crossingType +ORDER BY DESC(?crossings) diff --git a/demo-queries/openstreetmap/railway-lines-in-luxembourg.rq b/demo-queries/openstreetmap/railway-lines-in-luxembourg.rq new file mode 100644 index 0000000..bc34843 --- /dev/null +++ b/demo-queries/openstreetmap/railway-lines-in-luxembourg.rq @@ -0,0 +1,13 @@ +#+ name: Map: railway lines in Luxembourg +#+ description: Rail track geometries (railway=rail) in Luxembourg (relation 2171347), drawn as lines. +PREFIX osmkey: +PREFIX osmrel: +PREFIX ogc: +PREFIX geo: + +SELECT ?way ?name ?geometry WHERE { + osmrel:2171347 ogc:sfContains ?way . + ?way osmkey:railway "rail" ; + geo:hasGeometry/geo:asWKT ?geometry . + OPTIONAL { ?way osmkey:name ?name } +} diff --git a/demo-queries/openstreetmap/railway-stations-in-brussels.rq b/demo-queries/openstreetmap/railway-stations-in-brussels.rq new file mode 100644 index 0000000..ab72ed8 --- /dev/null +++ b/demo-queries/openstreetmap/railway-stations-in-brussels.rq @@ -0,0 +1,13 @@ +#+ name: Map: railway stations in Brussels +#+ description: OSM railway stations inside the Brussels-Capital Region (relation 54094), using QLever's precomputed ogc:sfContains. +PREFIX osmkey: +PREFIX osmrel: +PREFIX ogc: +PREFIX geo: + +SELECT ?station ?name ?geometry WHERE { + osmrel:54094 ogc:sfContains ?station . + ?station osmkey:railway "station" ; + osmkey:name ?name ; + geo:hasGeometry/geo:asWKT ?geometry . +} diff --git a/demo-queries/publications-office/authority-tables/all-labels-of-a-concept.rq b/demo-queries/publications-office/authority-tables/all-labels-of-a-concept.rq new file mode 100644 index 0000000..4045a14 --- /dev/null +++ b/demo-queries/publications-office/authority-tables/all-labels-of-a-concept.rq @@ -0,0 +1,11 @@ +#+ name: All labels of a concept +#+ description: All preferred and alternative labels of one concept (Belgium) in every available language. +PREFIX skos: + +SELECT ?kind ?language ?label WHERE { + VALUES ?concept { } + VALUES (?property ?kind) { (skos:prefLabel "preferred") (skos:altLabel "alternative") } + ?concept ?property ?label . + BIND(LANG(?label) AS ?language) +} +ORDER BY ?language ?kind diff --git a/demo-queries/publications-office/authority-tables/concept-status.rq b/demo-queries/publications-office/authority-tables/concept-status.rq new file mode 100644 index 0000000..0597b24 --- /dev/null +++ b/demo-queries/publications-office/authority-tables/concept-status.rq @@ -0,0 +1,13 @@ +#+ name: Concept status in the country table +#+ description: Authority table concepts carry lifecycle metadata. Counts the country concepts per status and shows start and end of use. +PREFIX skos: +PREFIX euvoc: +PREFIX at-ont: + +SELECT ?status (COUNT(?concept) AS ?concepts) (MIN(?start) AS ?earliestStart) (MAX(?end) AS ?latestEnd) WHERE { + ?concept skos:inScheme . + OPTIONAL { ?concept euvoc:status ?status } + OPTIONAL { ?concept at-ont:start.use ?start } + OPTIONAL { ?concept at-ont:end.use ?end } +} +GROUP BY ?status diff --git a/demo-queries/publications-office/authority-tables/corporate-bodies.rq b/demo-queries/publications-office/authority-tables/corporate-bodies.rq new file mode 100644 index 0000000..7808166 --- /dev/null +++ b/demo-queries/publications-office/authority-tables/corporate-bodies.rq @@ -0,0 +1,12 @@ +#+ name: EU institutions and bodies +#+ description: The corporate-body authority table: EU institutions, agencies and other bodies. +PREFIX skos: +PREFIX dc: + +SELECT ?concept ?code ?label WHERE { + ?concept skos:inScheme ; + skos:prefLabel ?label . + FILTER(LANG(?label) = "en") + OPTIONAL { ?concept dc:identifier ?code } +} +ORDER BY ?code diff --git a/demo-queries/publications-office/authority-tables/countries-multilingual.rq b/demo-queries/publications-office/authority-tables/countries-multilingual.rq new file mode 100644 index 0000000..3c0a235 --- /dev/null +++ b/demo-queries/publications-office/authority-tables/countries-multilingual.rq @@ -0,0 +1,14 @@ +#+ name: Countries in four languages +#+ description: The country authority table with its code and the English, French, German and Dutch labels. +PREFIX skos: +PREFIX dc: + +SELECT ?country ?code ?en ?fr ?de ?nl WHERE { + ?country skos:inScheme ; + dc:identifier ?code . + OPTIONAL { ?country skos:prefLabel ?en . FILTER(LANG(?en) = "en") } + OPTIONAL { ?country skos:prefLabel ?fr . FILTER(LANG(?fr) = "fr") } + OPTIONAL { ?country skos:prefLabel ?de . FILTER(LANG(?de) = "de") } + OPTIONAL { ?country skos:prefLabel ?nl . FILTER(LANG(?nl) = "nl") } +} +ORDER BY ?code diff --git a/demo-queries/publications-office/authority-tables/country-notations.rq b/demo-queries/publications-office/authority-tables/country-notations.rq new file mode 100644 index 0000000..c1de6b1 --- /dev/null +++ b/demo-queries/publications-office/authority-tables/country-notations.rq @@ -0,0 +1,13 @@ +#+ name: Country codes (ISO, EU, ...) +#+ description: Each country concept carries several notations, typed by the code list they come from (ISO 3166 alpha-2/alpha-3, numeric, ...). +PREFIX skos: + +SELECT ?country ?label ?notation ?codeList WHERE { + ?country skos:inScheme ; + skos:prefLabel ?label ; + skos:notation ?notation . + FILTER(LANG(?label) = "en") + BIND(DATATYPE(?notation) AS ?codeList) +} +ORDER BY ?label ?codeList +LIMIT 500 diff --git a/demo-queries/publications-office/authority-tables/currencies.rq b/demo-queries/publications-office/authority-tables/currencies.rq new file mode 100644 index 0000000..c6f4ec2 --- /dev/null +++ b/demo-queries/publications-office/authority-tables/currencies.rq @@ -0,0 +1,12 @@ +#+ name: Currencies +#+ description: The currency authority table. +PREFIX skos: +PREFIX dc: + +SELECT ?concept ?code ?label WHERE { + ?concept skos:inScheme ; + skos:prefLabel ?label . + FILTER(LANG(?label) = "en") + OPTIONAL { ?concept dc:identifier ?code } +} +ORDER BY ?code diff --git a/demo-queries/publications-office/authority-tables/describe-a-concept.rq b/demo-queries/publications-office/authority-tables/describe-a-concept.rq new file mode 100644 index 0000000..97505ca --- /dev/null +++ b/demo-queries/publications-office/authority-tables/describe-a-concept.rq @@ -0,0 +1,3 @@ +#+ name: Describe a concept +#+ description: DESCRIBE returns everything the endpoint knows about the corporate body 'European Union Agency for Railways'. Try the Graph plugin on the result. +DESCRIBE diff --git a/demo-queries/publications-office/authority-tables/file-types.rq b/demo-queries/publications-office/authority-tables/file-types.rq new file mode 100644 index 0000000..c50bc08 --- /dev/null +++ b/demo-queries/publications-office/authority-tables/file-types.rq @@ -0,0 +1,12 @@ +#+ name: File types +#+ description: The file-type authority table used for dct:format in DCAT-AP metadata. +PREFIX skos: +PREFIX dc: + +SELECT ?concept ?code ?label WHERE { + ?concept skos:inScheme ; + skos:prefLabel ?label . + FILTER(LANG(?label) = "en") + OPTIONAL { ?concept dc:identifier ?code } +} +ORDER BY ?code diff --git a/demo-queries/publications-office/authority-tables/languages.rq b/demo-queries/publications-office/authority-tables/languages.rq new file mode 100644 index 0000000..1858235 --- /dev/null +++ b/demo-queries/publications-office/authority-tables/languages.rq @@ -0,0 +1,12 @@ +#+ name: Languages +#+ description: The language authority table (ISO 639-based codes used across EU publications). +PREFIX skos: +PREFIX dc: + +SELECT ?concept ?code ?label WHERE { + ?concept skos:inScheme ; + skos:prefLabel ?label . + FILTER(LANG(?label) = "en") + OPTIONAL { ?concept dc:identifier ?code } +} +ORDER BY ?code diff --git a/demo-queries/publications-office/authority-tables/list-authority-tables.rq b/demo-queries/publications-office/authority-tables/list-authority-tables.rq new file mode 100644 index 0000000..8c83046 --- /dev/null +++ b/demo-queries/publications-office/authority-tables/list-authority-tables.rq @@ -0,0 +1,12 @@ +#+ name: List all authority tables +#+ description: The EU Vocabularies authority tables (countries, languages, corporate bodies, file types, ...) are SKOS concept schemes. Lists all of them with their size. +PREFIX skos: + +SELECT ?scheme ?label (COUNT(?concept) AS ?concepts) WHERE { + ?scheme a skos:ConceptScheme . + FILTER(STRSTARTS(STR(?scheme), "http://publications.europa.eu/resource/authority/")) + OPTIONAL { ?scheme skos:prefLabel ?label . FILTER(LANG(?label) = "en") } + OPTIONAL { ?concept skos:inScheme ?scheme } +} +GROUP BY ?scheme ?label +ORDER BY ?scheme diff --git a/demo-queries/publications-office/eu-law/acts-about-rail-transport.rq b/demo-queries/publications-office/eu-law/acts-about-rail-transport.rq new file mode 100644 index 0000000..a0d86be --- /dev/null +++ b/demo-queries/publications-office/eu-law/acts-about-rail-transport.rq @@ -0,0 +1,23 @@ +#+ name: Recent acts about rail transport +#+ description: Legal acts tagged with the EuroVoc concept 'rail transport', most recent first. +PREFIX cdm: +PREFIX xsd: +PREFIX lang: +PREFIX rt: +PREFIX skos: + +SELECT ?celex ?date ?title WHERE { + ?concept skos:inScheme ; + skos:prefLabel "rail transport"@en . + ?work cdm:work_is_about_concept_eurovoc ?concept ; + cdm:resource_legal_id_celex ?celex ; + cdm:work_date_document ?date . + FILTER(?date >= "2020-01-01"^^xsd:date) + OPTIONAL { + ?expression cdm:expression_belongs_to_work ?work ; + cdm:expression_uses_language lang:ENG ; + cdm:expression_title ?title . + } +} +ORDER BY DESC(?date) +LIMIT 100 diff --git a/demo-queries/publications-office/eu-law/acts-amending-an-act.rq b/demo-queries/publications-office/eu-law/acts-amending-an-act.rq new file mode 100644 index 0000000..8b56e06 --- /dev/null +++ b/demo-queries/publications-office/eu-law/acts-amending-an-act.rq @@ -0,0 +1,19 @@ +#+ name: Acts amending an act +#+ description: Which legal acts amend Directive (EU) 2016/797 on railway interoperability (CELEX 32016L0797)? +PREFIX cdm: +PREFIX xsd: +PREFIX lang: +PREFIX rt: + +SELECT ?amendingCelex ?date ?title WHERE { + ?target cdm:resource_legal_id_celex "32016L0797"^^xsd:string . + ?amending cdm:resource_legal_amends_resource_legal ?target ; + cdm:resource_legal_id_celex ?amendingCelex . + OPTIONAL { ?amending cdm:work_date_document ?date } + OPTIONAL { + ?expression cdm:expression_belongs_to_work ?amending ; + cdm:expression_uses_language lang:ENG ; + cdm:expression_title ?title . + } +} +ORDER BY ?date diff --git a/demo-queries/publications-office/eu-law/eurovoc-subjects-of-an-act.rq b/demo-queries/publications-office/eu-law/eurovoc-subjects-of-an-act.rq new file mode 100644 index 0000000..2f3ec63 --- /dev/null +++ b/demo-queries/publications-office/eu-law/eurovoc-subjects-of-an-act.rq @@ -0,0 +1,15 @@ +#+ name: EuroVoc subjects of an act +#+ description: The EuroVoc concepts a legal act is about (here the GDPR), in English. +PREFIX cdm: +PREFIX xsd: +PREFIX lang: +PREFIX rt: +PREFIX skos: + +SELECT ?concept ?label WHERE { + ?work cdm:resource_legal_id_celex "32016R0679"^^xsd:string ; + cdm:work_is_about_concept_eurovoc ?concept . + ?concept skos:prefLabel ?label . + FILTER(LANG(?label) = "en") +} +ORDER BY ?label diff --git a/demo-queries/publications-office/eu-law/gdpr-in-all-languages.rq b/demo-queries/publications-office/eu-law/gdpr-in-all-languages.rq new file mode 100644 index 0000000..f800f91 --- /dev/null +++ b/demo-queries/publications-office/eu-law/gdpr-in-all-languages.rq @@ -0,0 +1,14 @@ +#+ name: GDPR in all languages +#+ description: Looks up a legal act by CELEX number (32016R0679, the GDPR) and lists its title in every language version. +PREFIX cdm: +PREFIX xsd: +PREFIX lang: +PREFIX rt: + +SELECT ?language ?title WHERE { + ?work cdm:resource_legal_id_celex "32016R0679"^^xsd:string . + ?expression cdm:expression_belongs_to_work ?work ; + cdm:expression_uses_language ?language ; + cdm:expression_title ?title . +} +ORDER BY ?language diff --git a/demo-queries/publications-office/eu-law/latest-regulations.rq b/demo-queries/publications-office/eu-law/latest-regulations.rq new file mode 100644 index 0000000..b2b30e1 --- /dev/null +++ b/demo-queries/publications-office/eu-law/latest-regulations.rq @@ -0,0 +1,18 @@ +#+ name: Latest regulations +#+ description: The 50 most recent EU regulations in Cellar with their CELEX number and English title. +PREFIX cdm: +PREFIX xsd: +PREFIX lang: +PREFIX rt: + +SELECT ?work ?celex ?date ?title WHERE { + ?work cdm:work_has_resource-type rt:REG ; + cdm:resource_legal_id_celex ?celex ; + cdm:work_date_document ?date . + FILTER(?date >= "2025-01-01"^^xsd:date) + ?expression cdm:expression_belongs_to_work ?work ; + cdm:expression_uses_language lang:ENG ; + cdm:expression_title ?title . +} +ORDER BY DESC(?date) +LIMIT 50 diff --git a/demo-queries/publications-office/eu-law/legal-acts-per-year.rq b/demo-queries/publications-office/eu-law/legal-acts-per-year.rq new file mode 100644 index 0000000..414ce0f --- /dev/null +++ b/demo-queries/publications-office/eu-law/legal-acts-per-year.rq @@ -0,0 +1,16 @@ +#+ name: Legal acts per year +#+ description: Number of regulations, directives and decisions adopted per year since 2010. +PREFIX cdm: +PREFIX xsd: +PREFIX lang: +PREFIX rt: + +SELECT ?year ?type (COUNT(DISTINCT ?work) AS ?acts) WHERE { + VALUES ?type { rt:REG rt:DIR rt:DEC } + ?work cdm:work_has_resource-type ?type ; + cdm:work_date_document ?date . + FILTER(?date >= "2010-01-01"^^xsd:date) + BIND(YEAR(?date) AS ?year) +} +GROUP BY ?year ?type +ORDER BY DESC(?year) ?type diff --git a/demo-queries/publications-office/eurovoc/concept-hierarchy-graph.rq b/demo-queries/publications-office/eurovoc/concept-hierarchy-graph.rq new file mode 100644 index 0000000..67884bf --- /dev/null +++ b/demo-queries/publications-office/eurovoc/concept-hierarchy-graph.rq @@ -0,0 +1,19 @@ +#+ name: Concept hierarchy (graph) +#+ description: CONSTRUCTs the narrower hierarchy below 'land transport'. Best viewed with the Graph plugin. +PREFIX skos: + +CONSTRUCT { + ?concept skos:broader ?parent ; + skos:prefLabel ?label . + ?top skos:prefLabel ?topLabel . +} +WHERE { + ?top skos:inScheme ; + skos:prefLabel "land transport"@en ; + skos:prefLabel ?topLabel . + FILTER(LANG(?topLabel) = "en") + ?concept skos:broader+ ?top ; + skos:broader ?parent ; + skos:prefLabel ?label . + FILTER(LANG(?label) = "en") +} diff --git a/demo-queries/publications-office/eurovoc/eurovoc-domains.rq b/demo-queries/publications-office/eurovoc/eurovoc-domains.rq new file mode 100644 index 0000000..b0c04cd --- /dev/null +++ b/demo-queries/publications-office/eurovoc/eurovoc-domains.rq @@ -0,0 +1,11 @@ +#+ name: EuroVoc domains +#+ description: The 21 top-level domains of the EuroVoc thesaurus. +PREFIX skos: +PREFIX eurovoc: + +SELECT ?domain ?label WHERE { + ?domain a eurovoc:Domain ; + skos:prefLabel ?label . + FILTER(LANG(?label) = "en") +} +ORDER BY ?label diff --git a/demo-queries/publications-office/eurovoc/search-concepts.rq b/demo-queries/publications-office/eurovoc/search-concepts.rq new file mode 100644 index 0000000..efe6a39 --- /dev/null +++ b/demo-queries/publications-office/eurovoc/search-concepts.rq @@ -0,0 +1,15 @@ +#+ name: Search EuroVoc concepts +#+ description: Finds EuroVoc concepts whose English label contains a word, with their broader concept. +PREFIX skos: + +SELECT ?concept ?label ?broaderLabel WHERE { + ?concept skos:inScheme ; + skos:prefLabel ?label . + FILTER(LANG(?label) = "en" && CONTAINS(LCASE(STR(?label)), "railway")) + OPTIONAL { + ?concept skos:broader ?broader . + ?broader skos:prefLabel ?broaderLabel . + FILTER(LANG(?broaderLabel) = "en") + } +} +ORDER BY ?label diff --git a/demo-queries/publications-office/eurovoc/translations.rq b/demo-queries/publications-office/eurovoc/translations.rq new file mode 100644 index 0000000..09692d3 --- /dev/null +++ b/demo-queries/publications-office/eurovoc/translations.rq @@ -0,0 +1,11 @@ +#+ name: Translations of a concept +#+ description: The preferred label of the EuroVoc concept 'rail transport' in all EU languages. +PREFIX skos: + +SELECT ?language ?label WHERE { + ?concept skos:inScheme ; + skos:prefLabel "rail transport"@en ; + skos:prefLabel ?label . + BIND(LANG(?label) AS ?language) +} +ORDER BY ?language diff --git a/demo-queries/vocabularies/vocabularies-and-prefixes.rq b/demo-queries/vocabularies/vocabularies-and-prefixes.rq new file mode 100644 index 0000000..9b45f5a --- /dev/null +++ b/demo-queries/vocabularies/vocabularies-and-prefixes.rq @@ -0,0 +1,14 @@ +#+ name: Vocabularies and their prefixes +#+ description: All vocabularies registered in Linked Open Vocabularies with their preferred prefix and namespace. +PREFIX voaf: +PREFIX vann: +PREFIX dct: +PREFIX dcat: + +SELECT ?vocabulary ?prefix ?namespace ?title WHERE { + ?vocabulary a voaf:Vocabulary ; + vann:preferredNamespacePrefix ?prefix ; + vann:preferredNamespaceUri ?namespace . + OPTIONAL { ?vocabulary dct:title ?title . FILTER(LANG(?title) = "en") } +} +ORDER BY ?prefix diff --git a/demo-queries/vocabularies/vocabularies-by-tag.rq b/demo-queries/vocabularies/vocabularies-by-tag.rq new file mode 100644 index 0000000..1543b11 --- /dev/null +++ b/demo-queries/vocabularies/vocabularies-by-tag.rq @@ -0,0 +1,13 @@ +#+ name: Vocabularies by tag +#+ description: How many vocabularies are registered per LOV tag (domain)? +PREFIX voaf: +PREFIX vann: +PREFIX dct: +PREFIX dcat: + +SELECT ?tag (COUNT(?vocabulary) AS ?vocabularies) WHERE { + ?vocabulary a voaf:Vocabulary ; + dcat:keyword ?tag . +} +GROUP BY ?tag +ORDER BY DESC(?vocabularies) diff --git a/demo-queries/wikidata/eu-agencies.rq b/demo-queries/wikidata/eu-agencies.rq new file mode 100644 index 0000000..308924b --- /dev/null +++ b/demo-queries/wikidata/eu-agencies.rq @@ -0,0 +1,15 @@ +#+ name: EU agencies and their seats +#+ description: Agencies of the European Union with their seat and founding date. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: + +SELECT ?agency ?agencyLabel ?seatLabel ?founded WHERE { + ?agency wdt:P31/wdt:P279* wd:Q1377535 . + OPTIONAL { ?agency wdt:P159 ?seat } + OPTIONAL { ?agency wdt:P571 ?founded } + SERVICE wikibase:label { bd:serviceParam wikibase:language "en,fr,de,nl". } +} +ORDER BY ?agencyLabel diff --git a/demo-queries/wikidata/eu-member-states.rq b/demo-queries/wikidata/eu-member-states.rq new file mode 100644 index 0000000..2b04559 --- /dev/null +++ b/demo-queries/wikidata/eu-member-states.rq @@ -0,0 +1,21 @@ +#+ name: EU member states +#+ description: Current member states of the European Union with capital, population and accession date. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: +PREFIX p: +PREFIX ps: +PREFIX pq: + +SELECT ?country ?countryLabel ?capitalLabel ?population ?joined WHERE { + ?country p:P463 ?membership . + ?membership ps:P463 wd:Q458 . + OPTIONAL { ?membership pq:P580 ?joined } + FILTER NOT EXISTS { ?membership pq:P582 ?left } + OPTIONAL { ?country wdt:P36 ?capital } + OPTIONAL { ?country wdt:P1082 ?population } + SERVICE wikibase:label { bd:serviceParam wikibase:language "en,fr,de,nl". } +} +ORDER BY ?joined ?countryLabel diff --git a/demo-queries/wikidata/family-tree-graph.rq b/demo-queries/wikidata/family-tree-graph.rq new file mode 100644 index 0000000..a96477e --- /dev/null +++ b/demo-queries/wikidata/family-tree-graph.rq @@ -0,0 +1,22 @@ +#+ name: Family tree of the Belgian royal family (graph) +#+ description: CONSTRUCTs parent/child relations starting from King Leopold I. Open in the Graph plugin to see the family tree. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: +PREFIX ex: + +CONSTRUCT { + ?person ex:childOf ?parent ; + rdfs:label ?personLabel . + ?parent rdfs:label ?parentLabel . +} +WHERE { + # wd:Q12971 = Leopold I of Belgium; P40 = child + wd:Q12971 wdt:P40* ?parent . + ?parent wdt:P40 ?person . + ?person rdfs:label ?personLabel . FILTER(LANG(?personLabel) = "en") + ?parent rdfs:label ?parentLabel . FILTER(LANG(?parentLabel) = "en") +} +LIMIT 1000 diff --git a/demo-queries/wikidata/high-speed-lines-in-europe.rq b/demo-queries/wikidata/high-speed-lines-in-europe.rq new file mode 100644 index 0000000..e9be309 --- /dev/null +++ b/demo-queries/wikidata/high-speed-lines-in-europe.rq @@ -0,0 +1,17 @@ +#+ name: High-speed railway lines in Europe +#+ description: High-speed railway lines with their country, length and design speed. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: + +SELECT ?line ?lineLabel ?countryLabel ?length ?speed WHERE { + ?line wdt:P31/wdt:P279* wd:Q1323906 ; + wdt:P17 ?country . + ?country wdt:P30 wd:Q46 . + OPTIONAL { ?line wdt:P2043 ?length } + OPTIONAL { ?line wdt:P3978 ?speed } + SERVICE wikibase:label { bd:serviceParam wikibase:language "en,fr,de,nl". } +} +ORDER BY ?countryLabel ?lineLabel diff --git a/demo-queries/wikidata/largest-cities-in-europe.rq b/demo-queries/wikidata/largest-cities-in-europe.rq new file mode 100644 index 0000000..c932940 --- /dev/null +++ b/demo-queries/wikidata/largest-cities-in-europe.rq @@ -0,0 +1,18 @@ +#+ name: Largest cities in Europe +#+ description: Cities in Europe with more than one million inhabitants. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: + +SELECT DISTINCT ?city ?cityLabel ?countryLabel ?population WHERE { + ?city wdt:P31/wdt:P279* wd:Q515 ; + wdt:P17 ?country ; + wdt:P1082 ?population . + ?country wdt:P30 wd:Q46 . + FILTER(?population > 1000000) + SERVICE wikibase:label { bd:serviceParam wikibase:language "en,fr,de,nl". } +} +ORDER BY DESC(?population) +LIMIT 50 diff --git a/demo-queries/wikidata/longest-railway-tunnels.rq b/demo-queries/wikidata/longest-railway-tunnels.rq new file mode 100644 index 0000000..7a9b33e --- /dev/null +++ b/demo-queries/wikidata/longest-railway-tunnels.rq @@ -0,0 +1,16 @@ +#+ name: Longest railway tunnels +#+ description: The world's longest railway tunnels according to Wikidata. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: + +SELECT ?tunnel ?tunnelLabel ?countryLabel ?length WHERE { + ?tunnel wdt:P31/wdt:P279* wd:Q1311958 ; + wdt:P2043 ?length . + OPTIONAL { ?tunnel wdt:P17 ?country } + SERVICE wikibase:label { bd:serviceParam wikibase:language "en,fr,de,nl". } +} +ORDER BY DESC(?length) +LIMIT 30 diff --git a/demo-queries/wikidata/nobel-laureates-per-country.rq b/demo-queries/wikidata/nobel-laureates-per-country.rq new file mode 100644 index 0000000..a05c06a --- /dev/null +++ b/demo-queries/wikidata/nobel-laureates-per-country.rq @@ -0,0 +1,16 @@ +#+ name: Nobel laureates per country of citizenship +#+ description: Counts Nobel Prize winners per country of citizenship. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: + +SELECT ?country ?countryLabel (COUNT(DISTINCT ?laureate) AS ?laureates) WHERE { + ?laureate wdt:P166/wdt:P279? wd:Q7191 ; + wdt:P27 ?country . + SERVICE wikibase:label { bd:serviceParam wikibase:language "en,fr,de,nl". } +} +GROUP BY ?country ?countryLabel +ORDER BY DESC(?laureates) +LIMIT 30 diff --git a/demo-queries/wikidata/official-languages-of-europe.rq b/demo-queries/wikidata/official-languages-of-europe.rq new file mode 100644 index 0000000..80cf08b --- /dev/null +++ b/demo-queries/wikidata/official-languages-of-europe.rq @@ -0,0 +1,19 @@ +#+ name: Official languages of European countries +#+ description: Which languages are official in which European countries? +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: + +SELECT ?countryLabel (GROUP_CONCAT(DISTINCT ?languageLabel; separator=", ") AS ?officialLanguages) WHERE { + ?country wdt:P31 wd:Q6256 ; + wdt:P30 wd:Q46 ; + wdt:P37 ?language . + ?language rdfs:label ?languageLabel . + FILTER(LANG(?languageLabel) = "en") + ?country rdfs:label ?countryLabel . + FILTER(LANG(?countryLabel) = "en") +} +GROUP BY ?countryLabel +ORDER BY ?countryLabel diff --git a/demo-queries/wikidata/painters-born-in-belgium.rq b/demo-queries/wikidata/painters-born-in-belgium.rq new file mode 100644 index 0000000..9e39e5e --- /dev/null +++ b/demo-queries/wikidata/painters-born-in-belgium.rq @@ -0,0 +1,20 @@ +#+ name: Painters born in Belgium +#+ description: Painters born in Belgium with birth and death year and an image when available. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: + +SELECT ?painter ?painterLabel ?born ?died ?image WHERE { + ?painter wdt:P106 wd:Q1028181 ; + wdt:P19/wdt:P17 wd:Q31 ; + wdt:P569 ?birth . + OPTIONAL { ?painter wdt:P570 ?death } + OPTIONAL { ?painter wdt:P18 ?image } + BIND(YEAR(?birth) AS ?born) + BIND(YEAR(?death) AS ?died) + SERVICE wikibase:label { bd:serviceParam wikibase:language "en,fr,de,nl". } +} +ORDER BY ?born +LIMIT 200 diff --git a/demo-queries/wikidata/population-history-of-brussels.rq b/demo-queries/wikidata/population-history-of-brussels.rq new file mode 100644 index 0000000..2e705ee --- /dev/null +++ b/demo-queries/wikidata/population-history-of-brussels.rq @@ -0,0 +1,18 @@ +#+ name: Population history of Brussels +#+ description: Population figures over time for the City of Brussels, using statement qualifiers (point in time). Try the chart plugin. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: +PREFIX p: +PREFIX ps: +PREFIX pq: + +SELECT ?year ?population WHERE { + wd:Q239 p:P1082 ?statement . + ?statement ps:P1082 ?population ; + pq:P585 ?date . + BIND(YEAR(?date) AS ?year) +} +ORDER BY ?year diff --git a/demo-queries/wikidata/power-plants-map.rq b/demo-queries/wikidata/power-plants-map.rq new file mode 100644 index 0000000..b4d3f82 --- /dev/null +++ b/demo-queries/wikidata/power-plants-map.rq @@ -0,0 +1,17 @@ +#+ name: Map: power plants by type in Germany +#+ description: Power stations in Germany with their type and capacity, on a map. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: + +SELECT ?plant ?plantLabel ?typeLabel ?capacity ?coordinates WHERE { + ?plant wdt:P31 ?type ; + wdt:P17 wd:Q183 ; + wdt:P625 ?coordinates . + ?type wdt:P279* wd:Q159719 . + OPTIONAL { ?plant wdt:P2109 ?capacity } + SERVICE wikibase:label { bd:serviceParam wikibase:language "en,fr,de,nl". } +} +LIMIT 2000 diff --git a/demo-queries/wikidata/railway-stations-in-belgium-map.rq b/demo-queries/wikidata/railway-stations-in-belgium-map.rq new file mode 100644 index 0000000..ce54f36 --- /dev/null +++ b/demo-queries/wikidata/railway-stations-in-belgium-map.rq @@ -0,0 +1,14 @@ +#+ name: Map: railway stations in Belgium +#+ description: Railway stations in Belgium with their coordinates (geo:wktLiteral), shown on a map by the Geo plugin. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: + +SELECT ?station ?stationLabel ?coordinates WHERE { + ?station wdt:P31/wdt:P279* wd:Q55488 ; + wdt:P17 wd:Q31 ; + wdt:P625 ?coordinates . + SERVICE wikibase:label { bd:serviceParam wikibase:language "en,fr,de,nl". } +} diff --git a/demo-queries/wikidata/rivers-flowing-through-belgium.rq b/demo-queries/wikidata/rivers-flowing-through-belgium.rq new file mode 100644 index 0000000..78bd0b2 --- /dev/null +++ b/demo-queries/wikidata/rivers-flowing-through-belgium.rq @@ -0,0 +1,17 @@ +#+ name: Rivers flowing through Belgium +#+ description: Rivers located in Belgium with their length and the body of water they flow into. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: + +SELECT ?river ?riverLabel ?length ?mouthLabel WHERE { + ?river wdt:P31/wdt:P279* wd:Q4022 ; + wdt:P17 wd:Q31 . + OPTIONAL { ?river wdt:P2043 ?length } + OPTIONAL { ?river wdt:P403 ?mouth } + SERVICE wikibase:label { bd:serviceParam wikibase:language "en,fr,de,nl". } +} +ORDER BY DESC(?length) +LIMIT 50 diff --git a/demo-queries/wikidata/sparql-endpoints-in-wikidata.rq b/demo-queries/wikidata/sparql-endpoints-in-wikidata.rq new file mode 100644 index 0000000..5cb5205 --- /dev/null +++ b/demo-queries/wikidata/sparql-endpoints-in-wikidata.rq @@ -0,0 +1,13 @@ +#+ name: SPARQL endpoints registered in Wikidata +#+ description: Wikidata items with a SPARQL endpoint URL (P5305) - handy to discover new endpoints to try in MatGUI. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: + +SELECT ?item ?itemLabel ?endpoint WHERE { + ?item wdt:P5305 ?endpoint . + SERVICE wikibase:label { bd:serviceParam wikibase:language "en,fr,de,nl". } +} +ORDER BY ?itemLabel diff --git a/demo-queries/wikidata/unesco-world-heritage-map.rq b/demo-queries/wikidata/unesco-world-heritage-map.rq new file mode 100644 index 0000000..c8a66e0 --- /dev/null +++ b/demo-queries/wikidata/unesco-world-heritage-map.rq @@ -0,0 +1,15 @@ +#+ name: Map: UNESCO World Heritage in the Benelux +#+ description: World Heritage Sites in Belgium, the Netherlands and Luxembourg on a map. +PREFIX wd: +PREFIX wdt: +PREFIX wikibase: +PREFIX bd: +PREFIX rdfs: + +SELECT ?site ?siteLabel ?countryLabel ?coordinates WHERE { + VALUES ?country { wd:Q31 wd:Q55 wd:Q32 } + ?site wdt:P1435 wd:Q9259 ; + wdt:P17 ?country ; + wdt:P625 ?coordinates . + SERVICE wikibase:label { bd:serviceParam wikibase:language "en,fr,de,nl". } +} diff --git a/demo-queries/workspace.json b/demo-queries/workspace.json new file mode 100644 index 0000000..a452f3e --- /dev/null +++ b/demo-queries/workspace.json @@ -0,0 +1,47 @@ +{ + "workspaceIri": "https://matgui.matdata.eu/demo/workspace", + "label": "MatGUI demo queries", + "description": "Ready-to-run example queries for public SPARQL endpoints, maintained at https://github.com/Matdata-eu/matgui-matdata/tree/main/demo-queries", + "sourceBaseUrl": "https://github.com/Matdata-eu/matgui-matdata/blob/main", + "endpoints": { + "demo": "https://jena.matdata.eu/matgui-demo/sparql", + "era": "https://graph.data.era.europa.eu/repositories/rinf-plus", + "op": "https://publications.europa.eu/webapi/rdf/sparql", + "data-europa": "https://data.europa.eu/sparql", + "wikidata": "https://query.wikidata.org/sparql", + "dbpedia": "https://dbpedia.org/sparql", + "osm": "https://qlever.cs.uni-freiburg.de/api/osm-planet", + "uniprot": "https://sparql.uniprot.org/sparql", + "getty": "https://vocab.getty.edu/sparql", + "agrovoc": "https://agrovoc.fao.org/sparql", + "lov": "https://lov.linkeddata.es/dataset/lov/sparql" + }, + "folders": { + "getting-started": { "label": "Getting started", "endpoint": "demo" }, + + "era-rinf": { "label": "ERA - Railway infrastructure (RINF)", "endpoint": "era" }, + "era-rinf/explore": { "label": "Explore the knowledge graph" }, + "era-rinf/operational-points": { "label": "Operational points" }, + "era-rinf/lines-and-tracks": { "label": "Sections of line and tracks" }, + "era-rinf/maps": { "label": "Maps" }, + + "publications-office": { "label": "EU Publications Office", "endpoint": "op" }, + "publications-office/authority-tables": { "label": "Authority tables (SKOS)" }, + "publications-office/eurovoc": { "label": "EuroVoc thesaurus" }, + "publications-office/eu-law": { "label": "EU law (Cellar)" }, + + "data-europa-eu": { "label": "data.europa.eu", "endpoint": "data-europa" }, + "data-europa-eu/datasets": { "label": "Datasets and catalogues" }, + "data-europa-eu/authority-tables": { "label": "Authority tables (SKOS)" }, + + "geosparql": { "label": "GeoSPARQL (Apache Jena)", "endpoint": "demo" }, + + "wikidata": { "label": "Wikidata", "endpoint": "wikidata" }, + "dbpedia": { "label": "DBpedia", "endpoint": "dbpedia" }, + "openstreetmap": { "label": "OpenStreetMap (QLever)", "endpoint": "osm" }, + "life-sciences": { "label": "Life sciences (UniProt)", "endpoint": "uniprot" }, + "cultural-heritage": { "label": "Cultural heritage (Getty vocabularies)", "endpoint": "getty" }, + "agriculture": { "label": "Agriculture (AGROVOC)", "endpoint": "agrovoc" }, + "vocabularies": { "label": "Linked Open Vocabularies", "endpoint": "lov" } + } +} diff --git a/scripts/demo_queries.py b/scripts/demo_queries.py new file mode 100644 index 0000000..56120bf --- /dev/null +++ b/scripts/demo_queries.py @@ -0,0 +1,476 @@ +#!/usr/bin/env python3 +"""Build, validate, smoke-test and publish the MatGUI demo queries. + +The demo queries live as plain ``.rq`` files under ``demo-queries/``. Each +sub-directory becomes a workspace folder; ``demo-queries/workspace.json`` holds +the workspace IRI, the folder labels and the endpoint aliases. + +Every query file starts with a small metadata header (``#+ key: value`` lines, +the same convention grlc uses):: + + #+ name: Operational points per country + #+ description: Counts the operational points in RINF per member state. + #+ endpoint: era + +``name`` is required, ``endpoint`` is required unless the folder (or one of its +parents) defines a default endpoint. ``endpoint`` may be an alias defined in +``workspace.json`` or a full URL. The header lines are stripped from the query +text that is stored in the workspace. + +The generated RDF follows the model used by MatGUI's SPARQL workspace backend +(yasgui:Workspace / yasgui:WorkspaceFolder / yasgui:ManagedQuery / +yasgui:ManagedQueryVersion), so the queries can be browsed in MatGUI's query +browser once the Turtle file is uploaded to the demo dataset. + +Sub-commands: + + validate parse all queries and check metadata (fails on errors) + build write the workspace as Turtle + smoke-test execute every query against its endpoint and report + publish replace the default graph of the demo dataset (Graph Store Protocol) +""" + +from __future__ import annotations + +import argparse +import base64 +import datetime as dt +import hashlib +import json +import os +import re +import subprocess +import sys +import time +import urllib.error +import urllib.parse +import urllib.request +from dataclasses import dataclass, field +from pathlib import Path +from urllib.parse import quote + +REPO_ROOT = Path(__file__).resolve().parent.parent +QUERIES_DIR = REPO_ROOT / "demo-queries" +MANIFEST = QUERIES_DIR / "workspace.json" + +USER_AGENT = "MatGUI-demo-queries/1.0 (https://github.com/Matdata-eu/matgui-matdata)" + +HEADER_RE = re.compile(r"^#\+\s*([A-Za-z_-]+)\s*:\s*(.*?)\s*$") +QUERY_FORM_RE = re.compile(r"\b(SELECT|CONSTRUCT|ASK|DESCRIBE)\b", re.IGNORECASE) + + +@dataclass +class Folder: + path: str # e.g. "era-rinf/operational-points" + label: str + endpoint: str | None = None + + +@dataclass +class DemoQuery: + file: Path + folder_path: str # "" for the workspace root + name: str + endpoint: str + text: str + description: str | None = None + meta: dict = field(default_factory=dict) + + @property + def rel_path(self) -> str: + return self.file.relative_to(REPO_ROOT).as_posix() + + @property + def slug(self) -> str: + return self.file.relative_to(QUERIES_DIR).with_suffix("").as_posix() + + @property + def query_form(self) -> str: + # Strip comments, IRIs and strings so keywords inside them are ignored. + cleaned = re.sub(r"#[^\n]*", "", self.text) + cleaned = re.sub(r"<[^>\s]*>", "<>", cleaned) + cleaned = re.sub(r'"(?:[^"\\\n]|\\.)*"', '""', cleaned) + match = QUERY_FORM_RE.search(cleaned) + return match.group(1).upper() if match else "SELECT" + + +# --------------------------------------------------------------------------- loading + + +def load_manifest() -> dict: + with MANIFEST.open(encoding="utf-8") as fh: + return json.load(fh) + + +def resolve_endpoint(value: str | None, aliases: dict) -> str | None: + if not value: + return None + return aliases.get(value, value) + + +def load_folders(manifest: dict) -> dict[str, Folder]: + aliases = manifest.get("endpoints", {}) + folders: dict[str, Folder] = {} + for path, cfg in manifest.get("folders", {}).items(): + folders[path] = Folder(path=path, label=cfg["label"], endpoint=resolve_endpoint(cfg.get("endpoint"), aliases)) + return folders + + +def default_endpoint_for(folder_path: str, folders: dict[str, Folder]) -> str | None: + parts = folder_path.split("/") if folder_path else [] + while parts: + folder = folders.get("/".join(parts)) + if folder and folder.endpoint: + return folder.endpoint + parts.pop() + return None + + +def parse_query_file(path: Path, folders: dict[str, Folder], aliases: dict) -> DemoQuery: + lines = path.read_text(encoding="utf-8").replace("\r\n", "\n").split("\n") + meta: dict[str, str] = {} + body_start = 0 + for idx, line in enumerate(lines): + match = HEADER_RE.match(line) + if not match: + body_start = idx + break + meta[match.group(1).lower()] = match.group(2) + else: + body_start = len(lines) + + text = "\n".join(lines[body_start:]).strip("\n") + "\n" + folder_path = path.parent.relative_to(QUERIES_DIR).as_posix() + if folder_path == ".": + folder_path = "" + + endpoint = resolve_endpoint(meta.get("endpoint"), aliases) or default_endpoint_for(folder_path, folders) + return DemoQuery( + file=path, + folder_path=folder_path, + name=meta.get("name", "").strip(), + endpoint=endpoint or "", + text=text, + description=meta.get("description") or None, + meta=meta, + ) + + +def load_queries() -> tuple[dict, dict[str, Folder], list[DemoQuery]]: + manifest = load_manifest() + folders = load_folders(manifest) + aliases = manifest.get("endpoints", {}) + files = sorted(p for p in QUERIES_DIR.rglob("*") if p.suffix in (".rq", ".sparql")) + queries = [parse_query_file(p, folders, aliases) for p in files] + return manifest, folders, queries + + +# --------------------------------------------------------------------------- validate + + +def validate(args: argparse.Namespace) -> int: + from rdflib.plugins.sparql import prepareQuery + + _, folders, queries = load_queries() + errors: list[str] = [] + seen_labels: dict[tuple[str, str], str] = {} + + for q in queries: + where = q.rel_path + if not q.name: + errors.append(f"{where}: missing '#+ name:' header") + if not q.endpoint: + errors.append(f"{where}: no endpoint (set '#+ endpoint:' or a folder default)") + elif not re.match(r"^https?://", q.endpoint): + errors.append(f"{where}: endpoint '{q.endpoint}' is not a URL or known alias") + unknown = set(q.meta) - {"name", "description", "endpoint"} + if unknown: + errors.append(f"{where}: unknown header keys {sorted(unknown)}") + + # Every directory on the path must have a label in workspace.json + parts = q.folder_path.split("/") if q.folder_path else [] + for i in range(len(parts)): + sub = "/".join(parts[: i + 1]) + if sub not in folders: + errors.append(f"{where}: folder '{sub}' has no entry in workspace.json") + + key = (q.folder_path, q.name.lower()) + if q.name and key in seen_labels: + errors.append(f"{where}: duplicate name '{q.name}' in folder (also {seen_labels[key]})") + seen_labels[key] = where + + try: + prepareQuery(q.text) + except Exception as exc: # rdflib raises a variety of parse errors + errors.append(f"{where}: SPARQL syntax error: {str(exc).splitlines()[0]}") + + for err in errors: + print(f"::error::{err}" if os.environ.get("GITHUB_ACTIONS") else f"ERROR {err}") + + print(f"Validated {len(queries)} queries in {len({q.folder_path for q in queries})} folders: " + f"{len(errors)} error(s)") + return 1 if errors else 0 + + +# --------------------------------------------------------------------------- build + + +def folder_iri(workspace_iri: str, folder_path: str) -> str: + # Must match mintFolderIri() in MatGUI's SparqlWorkspaceBackend. + return f"{workspace_iri.rstrip('/')}/folder/{quote(folder_path, safe='')}" + + +def query_iri(workspace_iri: str, q: DemoQuery) -> str: + return f"{workspace_iri.rstrip('/')}/query/{quote(q.slug, safe='/')}" + + +def version_iri(workspace_iri: str, q: DemoQuery) -> str: + digest = hashlib.sha256(f"{q.slug}\n{q.endpoint}\n{q.text}".encode("utf-8")).hexdigest()[:16] + return f"{workspace_iri.strip()}_mq_v_{digest}" + + +def git_timestamp(path: Path) -> str | None: + try: + out = subprocess.run( + ["git", "log", "-1", "--format=%cI", "--", str(path)], + cwd=REPO_ROOT, capture_output=True, text=True, check=True, + ).stdout.strip() + return out or None + except (subprocess.CalledProcessError, FileNotFoundError): + return None + + +def build_graph(manifest: dict, folders: dict[str, Folder], queries: list[DemoQuery]): + from rdflib import BNode, Graph, Literal, Namespace, URIRef + from rdflib.namespace import DCTERMS, PROV, RDF, RDFS, SKOS, XSD + + YASGUI = Namespace("https://matdata.eu/ns/yasgui#") + SPIN = Namespace("http://spinrdf.org/spin#") + SD = Namespace("http://www.w3.org/ns/sparql-service-description#") + + g = Graph() + for prefix, ns in { + "yasgui": YASGUI, "spin": SPIN, "sd": SD, "dcterms": DCTERMS, + "prov": PROV, "skos": SKOS, "rdfs": RDFS, "xsd": XSD, + }.items(): + g.bind(prefix, ns) + + ws_iri = manifest["workspaceIri"] + ws = URIRef(ws_iri) + g.add((ws, RDF.type, YASGUI.Workspace)) + g.add((ws, RDFS.label, Literal(manifest.get("label", "MatGUI demo queries")))) + if manifest.get("description"): + g.add((ws, DCTERMS.description, Literal(manifest["description"]))) + + used_folders = {q.folder_path for q in queries if q.folder_path} + all_paths = set() + for path in used_folders: + parts = path.split("/") + all_paths.update("/".join(parts[: i + 1]) for i in range(len(parts))) + + for path in sorted(all_paths): + f = URIRef(folder_iri(ws_iri, path)) + g.add((f, RDF.type, YASGUI.WorkspaceFolder)) + g.add((f, SKOS.inScheme, ws)) + g.add((f, RDFS.label, Literal(folders[path].label))) + if "/" in path: + g.add((f, SKOS.broader, URIRef(folder_iri(ws_iri, path.rsplit("/", 1)[0])))) + + source_base = manifest.get("sourceBaseUrl") + fallback_now = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat() + services: dict[str, URIRef | BNode] = {} + + for q in queries: + mq = URIRef(query_iri(ws_iri, q)) + container = URIRef(folder_iri(ws_iri, q.folder_path)) if q.folder_path else ws + g.add((mq, RDF.type, YASGUI.ManagedQuery)) + g.add((mq, RDFS.label, Literal(q.name))) + g.add((mq, DCTERMS.isPartOf, container)) + if source_base: + g.add((mq, DCTERMS.source, URIRef(f"{source_base.rstrip('/')}/{quote(q.rel_path)}"))) + + v = URIRef(version_iri(ws_iri, q)) + g.add((v, RDF.type, YASGUI.ManagedQueryVersion)) + g.add((v, DCTERMS.isVersionOf, mq)) + g.add((v, DCTERMS.created, Literal(git_timestamp(q.file) or fallback_now, datatype=XSD.dateTime))) + g.add((v, SPIN.text, Literal(q.text))) + if q.description: + g.add((v, DCTERMS.description, Literal(q.description))) + + svc = services.get(q.endpoint) + if svc is None: + svc = URIRef(f"{ws_iri.rstrip('/')}/service/{hashlib.sha256(q.endpoint.encode()).hexdigest()[:12]}") + services[q.endpoint] = svc + g.add((svc, RDF.type, SD.Service)) + g.add((svc, SD.endpoint, URIRef(q.endpoint))) + g.add((v, PROV.used, svc)) + + return g + + +def build(args: argparse.Namespace) -> int: + manifest, folders, queries = load_queries() + g = build_graph(manifest, folders, queries) + out = Path(args.out) + out.parent.mkdir(parents=True, exist_ok=True) + g.serialize(destination=str(out), format="turtle", encoding="utf-8") + print(f"Wrote {len(g)} triples for {len(queries)} queries to {out}") + return 0 + + +# --------------------------------------------------------------------------- smoke test + + +def run_query(endpoint: str, text: str, form: str, timeout: int) -> tuple[bool, str, float]: + accept = { + "SELECT": "application/sparql-results+json", + "ASK": "application/sparql-results+json", + }.get(form, "application/n-triples, text/turtle;q=0.9") + data = urllib.parse.urlencode({"query": text}).encode("utf-8") + req = urllib.request.Request(endpoint, data=data, method="POST", headers={ + "Accept": accept, + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", + "User-Agent": USER_AGENT, + }) + start = time.monotonic() + try: + with urllib.request.urlopen(req, timeout=timeout) as res: + body = res.read() + elapsed = time.monotonic() - start + ctype = res.headers.get("Content-Type", "") + except urllib.error.HTTPError as exc: + detail = exc.read().decode("utf-8", "replace").strip().splitlines() + return False, f"HTTP {exc.code}: {(detail[0] if detail else exc.reason)[:200]}", time.monotonic() - start + except Exception as exc: # timeouts, DNS, TLS, ... + return False, f"{type(exc).__name__}: {exc}"[:200], time.monotonic() - start + + if form in ("SELECT", "ASK"): + try: + parsed = json.loads(body) + except ValueError: + return False, f"non-JSON response ({ctype})", elapsed + if form == "ASK": + return True, f"boolean={parsed.get('boolean')}", elapsed + rows = len(parsed.get("results", {}).get("bindings", [])) + return rows > 0, f"{rows} rows", elapsed + + triples = sum(1 for line in body.decode("utf-8", "replace").splitlines() + if line.strip() and not line.lstrip().startswith(("@prefix", "PREFIX", "#"))) + return triples > 0, f"~{triples} lines of RDF", elapsed + + +def smoke_test(args: argparse.Namespace) -> int: + _, _, queries = load_queries() + if args.only: + queries = [q for q in queries if any(s in q.rel_path for s in args.only)] + + results = [] + for q in queries: + ok, detail, elapsed = run_query(q.endpoint, q.text, q.query_form, args.timeout) + status = "ok" if ok else ("empty" if detail.startswith(("0 rows", "~0 ")) else "FAIL") + results.append((q, status, detail, elapsed)) + print(f"[{status:5}] {elapsed:6.1f}s {q.rel_path} — {detail}", flush=True) + + counts = {s: sum(1 for r in results if r[1] == s) for s in ("ok", "empty", "FAIL")} + summary = [ + "## Demo query smoke test", + "", + f"{counts['ok']} ok · {counts['empty']} empty result · {counts['FAIL']} failed (of {len(results)})", + "", + "| Status | Query | Endpoint | Result | Time |", + "|---|---|---|---|---|", + ] + order = {"FAIL": 0, "empty": 1, "ok": 2} + for q, status, detail, elapsed in sorted(results, key=lambda r: (order[r[1]], r[0].rel_path)): + icon = {"ok": "✅", "empty": "⚠️", "FAIL": "❌"}[status] + detail_md = detail.replace("|", "\\|") + summary.append(f"| {icon} | `{q.slug}` | {urllib.parse.urlparse(q.endpoint).netloc} | {detail_md} | {elapsed:.1f}s |") + + step_summary = os.environ.get("GITHUB_STEP_SUMMARY") + if step_summary: + with open(step_summary, "a", encoding="utf-8") as fh: + fh.write("\n".join(summary) + "\n") + else: + print("\n".join(summary)) + + if args.strict and counts["FAIL"]: + return 1 + return 0 + + +# --------------------------------------------------------------------------- publish + + +def http(method: str, url: str, body: bytes | None, headers: dict, timeout: int = 120) -> tuple[int, str]: + req = urllib.request.Request(url, data=body, method=method, headers={"User-Agent": USER_AGENT, **headers}) + try: + with urllib.request.urlopen(req, timeout=timeout) as res: + return res.status, res.read().decode("utf-8", "replace") + except urllib.error.HTTPError as exc: + return exc.code, exc.read().decode("utf-8", "replace") + + +def publish(args: argparse.Namespace) -> int: + dataset = args.dataset.rstrip("/") + turtle = Path(args.file).read_bytes() + + headers = {"Content-Type": "text/turtle; charset=utf-8"} + user, password = os.environ.get("JENA_USERNAME"), os.environ.get("JENA_PASSWORD") + if user and password: + token = base64.b64encode(f"{user}:{password}".encode()).decode() + headers["Authorization"] = f"Basic {token}" + + # Graph Store Protocol: PUT replaces the default graph, which is where MatGUI's + # SPARQL workspace backend reads and writes its data. + status, body = http("PUT", f"{dataset}/data?default", turtle, headers) + if status >= 300: + print(f"::error::Upload failed with HTTP {status}: {body[:500]}") + return 1 + print(f"Uploaded {len(turtle)} bytes to {dataset} (HTTP {status})") + + count_query = ( + "SELECT (COUNT(?q) AS ?n) WHERE { ?q a }" + ) + status, body = http( + "POST", f"{dataset}/sparql", + urllib.parse.urlencode({"query": count_query}).encode(), + {"Accept": "application/sparql-results+json", "Content-Type": "application/x-www-form-urlencoded"}, + ) + if status >= 300: + print(f"::warning::Could not verify upload (HTTP {status}): {body[:300]}") + return 0 + n = json.loads(body)["results"]["bindings"][0]["n"]["value"] + print(f"Dataset now contains {n} managed queries") + return 0 + + +# --------------------------------------------------------------------------- main + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + sub = parser.add_subparsers(dest="command", required=True) + + sub.add_parser("validate", help="parse queries and check metadata").set_defaults(func=validate) + + p_build = sub.add_parser("build", help="write the workspace as Turtle") + p_build.add_argument("--out", default=str(REPO_ROOT / "build" / "matgui-demo.ttl")) + p_build.set_defaults(func=build) + + p_smoke = sub.add_parser("smoke-test", help="run every query against its endpoint") + p_smoke.add_argument("--timeout", type=int, default=90) + p_smoke.add_argument("--strict", action="store_true", help="exit non-zero when a query fails") + p_smoke.add_argument("--only", nargs="*", help="only run queries whose path contains one of these strings") + p_smoke.set_defaults(func=smoke_test) + + p_pub = sub.add_parser("publish", help="upload the Turtle file to the demo dataset") + p_pub.add_argument("--dataset", default=os.environ.get("JENA_DATASET_URL", "https://jena.matdata.eu/matgui-demo")) + p_pub.add_argument("--file", default=str(REPO_ROOT / "build" / "matgui-demo.ttl")) + p_pub.set_defaults(func=publish) + + args = parser.parse_args() + return args.func(args) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/index.html b/src/index.html index 370953b..a7b25a8 100644 --- a/src/index.html +++ b/src/index.html @@ -75,6 +75,8 @@