Jump to content

To QLever

From Meta, a Wikimedia project coordination wiki
To QLever
A SPARQL query converter
PlatformsWeb · Bookmarklet
Source CodeGitLab
Runs onToolforge
Target endpointQLever Wikidata

To QLever is a Toolforge tool that rewrites SPARQL queries written for the Wikidata Query Service (WDQS) so that they run on QLever, and then links you straight to the result.

Paste a query.wikidata.org link or the query text itself, and the tool hands you back a working QLever query. The source code is at GitLab.

How it works

[edit]

WDQS runs on Blazegraph, QLever does not. The two engines disagree about a handful of things, and "To QLever" patches over exactly those.

Not every difference between the two endpoints needs a rewrite. The QLever team maintain their own comparison of the two — see See also below — and much of it is about the data and the UI rather than the query language. Both endpoints serve the whole of Wikidata and both track live edits, so a query that is valid on one is usually a rewrite or two away from running on the other. What genuinely differs is the Blazegraph-only syntax WDQS accepts, which is what this tool removes.

Labels are the clearest example. WDQS is slow at rdfs:label, which is why it offers SERVICE wikibase:label in the first place; QLever queries rdfs:label directly without trouble, provided the query says which language it wants. That is exactly the shape the converter emits.

The converter parses your query into an abstract syntax tree with TRAQULA, a real SPARQL 1.1 parser and generator, rewrites the tree, and serializes it back to text. Working on a tree rather than on raw text means nested groups, comments containing SPARQL-like tokens, and unusual whitespace cannot confuse the rewriter.

Named subqueries are the one exception. Their %name syntax is not SPARQL 1.1 at all, so the parser cannot read them and they are rewritten textually, before parsing. The parser still has the last word on whether the result is valid.

What gets rewritten

[edit]
SERVICE wikibase:label
Replaced with one OPTIONAL { ?x rdfs:label ?xLabel . FILTER(LANG(?xLabel) = "…") } block per fallback language.
Both forms are handled. The auto form derives the ?xLabel, ?xAltLabel and ?xDescription variables from the query as a whole; the manual form uses the explicit label triples inside the service block. [AUTO_LANGUAGE] is resolved to your configured language preference.
Where the labelled variable is itself bound inside an OPTIONAL, the label block is placed inside that OPTIONAL rather than after it. This matters far more than it looks: left outside, the block also has to cover the rows where the variable never got bound, and with no binding to join against QLever sorts an enormous intermediate result. The same query nested answers in seconds where it otherwise times out. The results are identical either way.
Named subqueries (WITH { … } AS %name / INCLUDE %name)
Inlined: each named block is substituted at every INCLUDE that refers to it, becoming an ordinary SPARQL subquery. Blazegraph evaluates a named subquery once and reuses the solutions, while an inlined one is evaluated at each site — with a single INCLUDE, the usual case, the two are equivalent. On by default; switch it off under Advanced options to have such queries refused instead.
Long label fallback lists
Trimmed. The label rewrite costs one OPTIONAL per label variable per language, so a list of seven languages across four label variables becomes 28 blocks — enough for QLever to run out of memory on a query WDQS handles. Above three languages the list is cut to the query's own first choice (usually [AUTO_LANGUAGE], the reader's language), then en, then mul, Wikidata's language-neutral label. Shorter lists are never touched, and never padded. On by default; switch it off under Advanced options to keep every language.
Blazegraph query hints (hint:…)
Removed. QLever does not support them, and leaving them in causes parse errors.
Blazegraph-only prefix declarations (bd:, hint:)
Stripped from the output.
Missing prefix declarations
Added. The WDQS editor silently injects the standard Wikidata prefixes for you; QLever's raw endpoint does not. Every prefix the query uses is therefore declared explicitly in the output.
Unsupported services (wikibase:mwapi, wikibase:around, wikibase:box)
Left unchanged, with a warning. These need Blazegraph-specific extensions that QLever cannot run.
Coordinate columns
When a SELECT projects a variable bound to a globe coordinate (any Wikidata coordinate property, such as wdt:P625), that column is moved to the end. QLever's Map View is offered when the last column holds something mappable, so without this an otherwise fine query loses its map. On by default; switch it off under Advanced options.
Leading comments
The WDQS header block (#title:, a description, #defaultView:…) is copied verbatim to the top of the output.

Whenever one of the optional rewrites above actually changes something, it says so in a # To QLever: comment directly beneath that header, so a converted query still explains itself once it has been pasted somewhere and the tool is out of sight:

#title: 'solved' classes from physical/abstract anomaly list
# To QLever: inlined 1 Blazegraph named subquery (%classes)
# To QLever: label fallback languages limited to fr, en, mul (query asked for en, mul, fr, de, es, it, sv)
PREFIX wd: <http://www.wikidata.org/entity/>
…

Bookmarklet

[edit]

The home page offers a draggable bookmarklet. Drag it to your bookmarks bar once. From then on, one click while you are viewing any query on query.wikidata.org opens the converted QLever version.

The bookmarklet simply navigates to https://to-qlever.toolforge.org/to-qlever?url=<current URL>. The conversion happens server-side, and you are redirected to QLever. Nothing is injected into the WDQS page, so none of that UI's constraints apply, and no browser extension is needed.

Notes and limitations

[edit]
  • The label rewrite only approximates WDQS fallback semantics, using ordered OPTIONAL and FILTER(LANG(...)) blocks. It labels the entity bound to the base variable — ?item for ?itemLabel. Labelling properties would also need a ?prop wikibase:directClaim ?p triple, which is not added automatically.
  • The language match is exact, so a label tagged en-gb does not answer a request for en. WDQS's own fallback is looser, and a converted query can therefore return fewer labels than the original.
  • Label blocks are placed where QLever can afford them: nested inside the OPTIONAL that binds the variable where one clearly owns it, otherwise below the patterns that bind it. A block whose label variable is read elsewhere in the same group — by a FILTER or BIND — stays where the service was, which is correct but can be slow. Moving it by hand is worth trying if a converted query times out.
  • A ?xLabel the query binds itself is left to the query, as WDQS leaves it; one whose ?x appears nowhere is dropped with a warning rather than converted into a scan of every label in Wikidata.
  • Only the first auto-mode label service in a query is expanded. A second one, and the languages it asks for, are silently ignored.
  • A label service written inside a subquery labels inside that subquery. Unless the subquery's own SELECT projects the label variable, the column comes back empty.
  • SERVICE wdsubgraph:... is removed: QLever indexes the scholarly and main subgraphs together, so there is nothing to federate with. Where the WDQS internal-federation idiom put the same pattern on both sides of a UNION, the two arms are folded back into one; where the arms differ, the conversion warns that rows may now be duplicated.
  • Hand-built WKT strings passed to geof: functions are wrapped in STRDT(..., geo:wktLiteral). Blazegraph accepts a plain string there and QLever does not — it returns unbound instead, which silently empties the result through whatever FILTER reads it. Other geospatial differences, including replacements for wikibase:around and wikibase:box, are not written for you.
  • QLever's stricter GROUP BY handling and its numeric-literal quirks are not addressed. Queries that lean on WDQS being lenient here may still need a manual fix.
  • Only the leading comment block survives. Comments in the middle of the query body cannot be preserved: the SPARQL parser discards comment tokens while building the tree, leaving no node to re-attach them to.

See also

[edit]