feat(views): render SVG views from CONSTRUCT queries in OntoSVG - #709
Open
ashleycaselli wants to merge 1 commit into
Open
ashleycaselli wants to merge 1 commit into
ashleycaselli wants to merge 1 commit into
Conversation
An SVG view's query had to return ready-made markup in an svg column, which means building the figure as a string in SPARQL: group_concat, whose ordering is not dependable, and hand-escaping every data-derived label. A view query may now instead be a CONSTRUCT describing the image as RDF in the OntoSVG vocabulary. The graph is serialized to markup and handed on as the svg column the view already renders, so headings, actions, sanitization and the empty state stay in one place, and the display type is unchanged. The element names come from the vocabulary rather than from the class names, which do not always agree: svg:TextElement is <text>, while svg:Text is character data, and the font and colour-profile classes hyphenate. Children are read from rdf:_1, rdf:_2, ... in numeric order, so rdf:_10 follows rdf:_9 and the order does not depend on how the graph came back. Text and attribute values are XML-escaped on the way out, and the markup still goes through Utils.sanitizeSvg, which remains the only thing deciding what may render. OntoSVG models SVG 1.1, whose links are xlink:href. Those are written as href, the SVG 2 spelling, which is both what the sanitizer keeps and what browsers follow; emitting xlink:href would render links that silently do nothing. Closes #592 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NnGpEQkFqCaf4AEQwwhFFn
ashleycaselli
force-pushed
the
feat/592-rdf-svg-representations
branch
from
September 14, 2026 10:42
b469f70 to
4328f50
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #592.
An SVG view's query had to return ready-made markup in an
svgcolumn, which means assembling the figure as a string in SPARQL —group_concat, whose orderingdocs/svg-views.mdalready warns is not dependable, plus hand-escaping every data-derived label.A view query may now instead be a CONSTRUCT describing the image as RDF in the OntoSVG vocabulary. Nothing else about the view declaration changes: the display type stays
gen:SvgView.How it fits together
Most of the plumbing was already there:
QueryAccessparses a non-CSV response into an RDF4JModel,ApiCache.retrieveRdfModelAsynccaches it, andQueryTemplate.isConstructQuery()says which kind of query this is — all used today byQueryPage. The new parts are:OntoSvg— serializes the graph to SVG markup.QueryResultSvgBuilder— picks the RDF path for a CONSTRUCT query and hands the serialized figures on as thesvgcolumn the view already renders, one row per figure.QueryResultSvgitself is unchanged apart from its Javadoc, so headings, entry actions, sanitization and the "(nothing found)" state stay in one place.The markup still goes through
Utils.sanitizeSvg, which remains the only thing deciding what may render. The sanitizer is untouched by this PR.Details worth a reviewer's attention
Element names come from the vocabulary, not the class names. They do not always agree, and the mismatches are silent ones:
svg:TextElementis<text>;svg:Textis character data. Deriving the tag from the class name would have turned every text node into an empty<text>element and dropped every label. The reference smiley in the OntoSVG repo is 11/15svg:Textwhitespace nodes, which is what caught this.svg:ColorProfile→color-profile,svg:FontFace*→font-face*,svg:MissingGlyph→missing-glyph.The 80-entry map is generated from the
xml:tagvalues in the vocabulary'ssvg - core.trig, not typed by hand.Ordering is made deterministic in three places, since a
Modelis an unordered set of statements and these views are cached and re-rendered: children sort numerically onrdf:_N(sordf:_10followsrdf:_9, notrdf:_1), attributes sort by name, and multiple figures sort by root IRI.xlink:hrefis written ashref. This is the one deliberate deviation from the spec's serialization rules. OntoSVG models SVG 1.1, where links arexlink:hrefand there is nosvg:href; the sanitizer allows only a barehrefona. Emittingxlink:hreffaithfully would produce links that are silently stripped, and links are a first-class part of this view type (the existing typology diagram links every label to its class IRI).hrefis the SVG 2 spelling and the one browsers follow. Documented indocs/svg-views.md.Root detection takes the outermost
svg:Svgnodes — those not inside anothersvg:Svg. "Not anyone's child" is the wrong test, because the vocabulary wraps a document in ansvg:Documentholding the doctype and thesvg:Svg; my first version used it and failed on the reference smiley.I did not port the reference implementation's approach: it drives a PyShacl rule engine over ~180KB of SHACL vocabulary to build fragments bottom-up. The data model underneath is a plain tree walk.
Known limitation
A CONSTRUCT result has no columns, so a figure rendered this way has no per-figure
titleheading and nonpsource link — the view's own title still shows. Both are documented under "What a CONSTRUCT view gives up". Adding them back would mean inventing OntoSVG-adjacent predicates for view metadata, which seemed worth deciding separately.Testing
12 new tests in
OntoSvgTest, including the reference smiley from the OntoSVG repository serialized and checked against its published SVG rendering, plus numeric child ordering, theText/TextElementdistinction, hyphenated tags, escaping of data-derived text and attributes, nested<svg>, stable multi-figure order, unknown classes, and a cyclic graph.Full suite: 1426 tests, 0 failures, 0 errors — no regressions.
🤖 Generated with Claude Code
https://claude.ai/code/session_01NnGpEQkFqCaf4AEQwwhFFn