ChangeLog
Unreleased
6.0.0 (2026-09-23)
Breaking: metrics v2, not compatible with v1 metrics. Catalogue scores are aggregated from the pv:finalScore values written by score 4.0.0, and the MongoDB documents and API payloads use the v2 model (metricsVersion 2.0.0); v1 data is not migrated, so clear the metrics at cutover. Requires score 4.0.0, annotator 3.0.0 and accessibility 3.0.0.
Added:
- Every entity block in a detail payload now names the resource it describes, by the URI the triplestore knows it by.
distributions[].distributionanddataServices[].dataServicealready did;dataset.datasetis new, and the standalone-data-service payload (M§17.2) now writes thedataServicefield its OpenAPI schema had always declared. Catalogue documents gaininfo.cataloguebesideinfo.id. The path segment these endpoints are addressed by is a portal-local slug —https-data-gov-lt-datasets-509-, nothttp://data.europa.eu/88u/dataset/https-data-gov-lt-datasets-509-— so a consumer joining a metrics document back to the RDF previously had to reconstruct the URI from the slug and a base URI it had to know out of band. Dataset and data-service details are built live from the triplestore, so those three fields appear as soon as the service is deployed;info.catalogueis served from Mongo and so appears per catalogue only after that catalogue's next successful refresh. Purely additive — no existing field changes name, type or value.
Fixed:
- The refresh sweep now sees every eligible catalogue.
CatalogueInfos.sparqljoineddcat:datasetinto its basic graph pattern and cross-multiplied fiveOPTIONALs over multilingualdct:title/dct:description, so one catalogue with 25 language titles and 25 descriptions emitted 625 rows. Against staging that was 95,489 ms returning 50,000 rows — Virtuoso's default result cap, so the list was truncated after roughly 80 catalogues. The 2026-08-05 sweep refreshed 81 of 216 eligible catalogues; the other 135 had never had a metrics document written, silently and without an error. It is replaced byCatalogueList.sparql, which expresses the eligibility rules asFILTER EXISTS/NOT EXISTSand returns one row per catalogue (216 rows, 221 ms). The eligibility semantics are not identical, in two ways, though measurement shows neither changes the outcome on current data. The old rules all sat inside a singleGRAPH ?g, so a catalogue's type, itsdcat:datasetlinks and its visibility had to share one graph; the replacement uses independent graph variables and matches across graphs — both forms return the same 216 catalogues, consistent with a catalogue's triples living in its own named graph. And a catalogue assertingedp:visibility edp:hiddenalongside another visibility value used to survive (OPTIONAL+!BOUND(?v) || ?v != edp:hiddenyielded a second, non-hidden solution thatDISTINCTkept) whereasFILTER NOT EXISTSnow excludes it — currently unexercised, since only 2 catalogues carryedp:visibilityat all, both asserthidden, and none carries a second value. Both changes are intended; they are recorded because they are real differences that could surface on other data, not because they alter today's result.
info.spatialandinfo.typeno longer depend on result-set order. The previous handler wrote the whole info object once per cross-product row, keeping whichever arrived last, so the stored value could differ between two refreshes of unchanged data. Repeated properties now resolve to the lexicographically smallest value.
POST /admin/refresh/catalogues/{id}now answers immediately instead of blocking on a ~95 s query. It validated its path parameter by callinglistCatalogues(), and only sent the 202 once that returned — so behind a proxy with a 30 s timeout the caller got a gateway timeout while the refresh had in fact started.listCatalogues()runsCatalogueInfos.sparql, which walks every catalogue under an unboundGRAPH ?gand cross-multiplies fiveOPTIONALs over multilingualdct:title/dct:description: measured against staging at 95,489 ms returning 50,000 rows, 50,000 being Virtuoso's default result cap, so the result was also truncated. Validation now uses a bound-graph existence check (CatalogueExists.sparql, ~140 ms). Thecountriesandglobalbranches were never affected — they validate against an in-memory map, or not at all.
- A single-catalogue refresh no longer overwrites a catalogue's stored
infowith null. The info cache is populated bylistCatalogues(), which only a full sweep now calls, sogetCatalogueInfocan legitimately return null on a process that has not swept yet; the stored value is left for the next sweep instead of being erased.
- The reachability endpoint returns the failing URL again, with its status code and check timestamp, under v1's field names — the URL had been absent from the response entirely since the v2 rebuild, so the MQA distributions page showed "No Access URL available" for every entry
- Each reachability entry is now one row per failing probed URL, not one row per
(distribution, metric). As a side effect, four fields present on the older v2 shape are gone from the response:metric, thestatusCodesarray,methodanddescription. Of those,dct:description— the only human-readable failure text this pipeline produces (e.g."Not Found", or a timeout message) — is still written by piveau-metrics-accessibility's probe records; it is simply not read by eitherReachabilityErrors.sparqlor its handler, so it never reaches this endpoint's response
- The reachability endpoint has a documented response schema; it previously had none, which is how a missing field went unnoticed
- Rewrote all 15 scope queries (
{All,Catalogue,Country}×FinalScore/Counts/{Dataset,Distribution,DataService}Coverage) to bind the entity from its scope via the annotator'sdqv:hasQualityMetadataanchor, instead of searching for the version marker under an unboundGRAPH ?g. The old shape had to enumerate every measurement in the store before narrowing to a scope: against the staging triplestore it took a constant ~120 s regardless of catalogue size and exceeded Virtuoso's anytime limit, returning HTTP 206 partial results —FinalScorecame back empty for every catalogue,Coveragecame back with plausible-but-wrong counts. Measured on the same store, a fullestatrefresh (9,785 datasets / 40,200 distributions) now takes 14.6 s across all five queries where each previously took ~120 s and failed. Requires re-annotated data — seedocs/DATA-CONTRACT.adoc§1.3.
- A refresh no longer aborts on the first failing document.
collectIterativepreviously propagated a
per-document failure through its composition chain, so one unreachable catalogue — or one triplestore
query exceeding Virtuoso's anytime limit and returning HTTP 206, whichTripleStore.selectraises as
aTripleStoreException— silently cost every remaining catalogue and country its update. Failures are
now recorded and skipped, and each scope logs a summary of how many documents succeeded, how many
failed, and the first few failing ids.
Changed:
- Breaking:
info.titleandinfo.descriptionare now language maps ({"en": "…", "de": "…"}) rather than single strings, on catalogue and country documents alike. DCAT-AP allows one literal per language and the catalogue keeps all of them; the previous shape kept exactly one, chosen by result-set order. Agreed with the UI team. A literal carrying no language tag is keyedund(BCP-47 "undetermined"), verified to appear on zero existing literals so it cannot merge with real data — and deliberately not mapped to a real language, since 94 of the 96 catalogues with an untagged title also carry a tagged one that guessing would overwrite. Two catalogues (taxud,trade) have only an untagged title, soundmay be the sole key; a consumer wanting one display string should try its preferred language, thenund, then any remaining key. Stored documents self-heal —infois written with$seton every refresh — but between deploying and the first completed sweep,GET /cataloguesreturns a mix of old strings and new objects.
getCatalogueInfo(id)queries the triplestore live; thecatalogueInfoswarm-up map is deleted. That map was populated only as a side effect oflistCatalogues(), which is why a single-catalogue refresh on a process that had not yet swept could writenullover stored info. The guard added for that is removed along with the coupling that required it.
Countsnow counts entities carrying the v2 quality-metadata anchor, i.e. the entities the annotator actually evaluated, rather than entities carrying at least one measurement inside a v2-marked graph. On the staging store the two differ by ~0.04% (entities evaluated whose every metric was excluded or unresolvable). The data-service count remains the distinct union of services embedded in the scope's distributions and services listed directly on the catalogue — counted withUNION+COUNT DISTINCT, not as two counts summed, so a service reachable both ways is counted once.
- v2 aggregation model: catalogue/scope score is now a single Method-1 average of
pv:finalScore(raw 0–7.5 decimal), replacing the v1 five-dimension scores; per-metric
coverage percentages (Dataset/Distribution/DataService blocks) replace the ~20
per-indicator percentages. Only metrics graphs markeddct:conformsTo <https://piveau.eu/ns/metrics/2.0.0>are read (hard cutover, no v1/v2 dual read path).
Runs on Vert.x 5. Seedocs/DATA-CONTRACT.adocanddocs/ADR-005-v2-aggregation-model.adoc.
- Logging/error-handling alignment: boot-time config redaction via allow-list (
ConfigLog),
uniform JSON error responses (400/401/403/404/409/500), dedicatedpiveau.accessrequest
logging, tracing withtrace_idcorrelation (X-Trace-Idheader + 500 bodytrace_id), and
thread-safe date handling (java.time) on request and refresh paths.
5.8.3 (2026-03-02)
Fixed:
- Most SPARQL queries regarding ORDER BY for aggregated results for better compliancy
5.8.2 (2025-10-31)
Fixed:
- metrics history threw an error because of an invalid database query
5.8.1 (2025-04-02)
Fixed:
- Changelog update & readme typo fixes
5.8.0 (2025-04-02)
Added:
- Option to protect all endpoints from unauthorized access. this can be anabled with the environment variable
PROTECT_METRICS_ENDPOINTS_WITH_AUTH
Changed:
- The header for using the API key was changed from
AuthorizationtoX-API-Key.
5.7.0 (2025-02-13)
Changed:
- Counting the datasets instead of the values in some distribution queries
5.6.17 (2025-02-09)
Changed:
- Admin refresh API for single refreshes (now scope is required)
- Overall score is calculated from the dimensions, not via SPARQL query
5.6.16 (2025-02-07)
Added:
- Two additional fields accessUrlAccessibility and downloadUrlAccessibility
Changed:
- Revert the revert again :/ But only count scored distribution
5.6.15 (2025-02-06)
Changed:
- Revert last change (makes no sense, it causes values with more than 100%)
5.6.14 (2025-02-06)
Changed:
- Percentage of status codes now based on total count of datasets
5.6.13 (2025-02-04)
Fixed:
- Total count when no "yes" values available
5.6.12 (2025-02-03)
Fixed:
- NPE because of missing value in status code measurement of distributions
5.6.11 (2024-07-13)
Changed:
- Exclude empty catalogues from calculation
5.6.10 (2024-07-13)
Changed:
- Loading quartz scheduler optionally and configurable
- Queries for distribution measurements are now limited only to scored distributions
Fixed:
- Issue with issued and modified scoring
5.6.9 (2024-01-31)
Added:
- Missing H2 DB table file and quartz properties
5.6.8 (2023-12-27)
Changed:
- Refactored single dataset metrics calculation
5.6.7 (2023-12-09)
Changed:
- Adjusted log output levels
- Dump lib dependencies
5.6.6 (2023-11-08)
Added
- support for scheduling refresh of all
???? Missing changelogs
5.5.0 (2022-11-01)
Fixed:
- code generation for vertx
Added:
- support for linting results in distributions
5.4.1 (2022-11-01)
Changed:
- caching the query results for violations, with an expiry check
Fixed:
- Counting Query for violations and violations query itself are now using the same clauses
- Normalization of piveau id
5.4.0 (2022-07-04)
Changed:
- Moved date modified from distribution to dataset
5.3.6 (2022-04-27)
Changed:
- Lock for refresh, preventing from multiple runs in parallel
Fixed:
- Use getProperty instead of getRequiredProperty to get rid of exceptions
5.3.5 (2022-02-25)
Changed:
- Queries using sub selects for better performance
5.3.4 (2022-02-24)
Added:
- Rudiment support of
Accept-Languageheader
Changed:
- Dataset metrics handling
Fixed:
- Bug in refresh promise complete
5.3.3 (2022-02-04)
Fixed:
- Issue #1957: 404 when graph was found instead of when not found
5.3.2 (2022-01-13)
Changed:
- MongoDB configuration
5.3.2 (2022-01-26)
Added
- Server in OpenApi to fix API call examples, see https://github.com/Redocly/redoc/issues/1172
5.3.1 (2022-01-12)
Changed:
- Lib dependencies
5.3.0 (2021-10-17)
Fixed:
- Hard coded dcat-ap schema context
Changed:
- Complete queries rework
5.2.1 (2021-10-15)
Changed:
- Use Vert.x 4 router builder
- use actual api key security handler
5.2.0 (2021-10-09)
Added:
- Extended DCAT-AP schema configuration
Removed:
- 404 error log output
5.1.1 (2021-09-29)
Fixed:
- Some log outputs
5.1.0 (2021-06-15)
Fixed:
- Jena lib update with bug fixes
5.0.0 (2021-06-07)
Changed:
- Query metrics with reference from record
Fixed:
- Required property disarmed
4.0.6 (2021-03-18)
Changed:
- Status error count query split in two queries
Fixed:
- Dataset quality calculation reliability
- Dataset metrics "tokenizer"
4.0.5 (2021-03-18)
Changed:
- Counting error codes is disarmed due to huge performance issues. Returns always 0
4.0.4 (2021-03-17)
Fixed:
- SPARQL queries
4.0.3 (2021-03-16)
Changed:
- Eventbus timeouts increased
- Small query optimizations
4.0.2 (2021-03-05)
Added:
- Average score for each dimension
- Dimension score fields to OpenApi
Changed:
- Get average scores from latest, not history anymore
Fixed
- Dimension scores are returned when filtering for that dimension, the score or both
3.1.1 (2020-10-26)
Changed:
- Return -1.0 when dqv result is empty
3.1.0 (2020-10-23)
Changed:
- Almost all queries to select latest or historic metrics graphs
3.0.2 (2020-09-17)
Fixed:
- Single metric refresh checks for valid catalogue id
- The current endpoint will now return the last value, when there are multiple for the last day
3.0.1 (2020-09-10)
Added
- Debug output
Removed
- Some stupid log output
3.0.0 (2020-09-xx)
Added
- Licence
Changed:
- Updated README to align with template used in other services
Removed
- Hot-deployment scripts
2.2.0 (2020-07-01)
Added:
- REST API for refreshing single catalogue
- REST API for deleting single catalogue metrics
2.1.0 (2020-06-15)
Added:
- Warnings in SHACL results
- Severity in SHACL violation reports
2.0.1 (2020-05-29)
Fixed:
- Missing time values for status code errors
2.0.0 (2020-05-25)
Changed:
- Almost everything, except the API itself
Added:
Changed:
- dateIssuedAvailability is calculated only on Datasets
- Apikey now must be set via environment variables
Removed:
Fixed:
- Queries for violations