Multi-source queries#
RAMOSE can combine results from multiple SPARQL endpoints in a single operation. This is driven by directives: lines starting with @@ inside the #sparql block.
When no directives are present, the query runs against the default endpoint as usual. When directives appear, RAMOSE splits the block into steps and executes them in sequence, building up an accumulator of rows.
SPARQL read retries apply per HTTP SPARQL step and per SPARQL Anything read step. In an @@foreach block, RAMOSE retries only the failed iteration. It does not restart the whole multi-source pipeline. Write operations are not retried by this policy.
Setup#
Register named endpoints in the API section:
#sources meta=https://opencitations.net/meta/sparql; index=https://opencitations.net/index/sparql
Directive syntax#
All directives follow the same grammar:
@@name <arg>... [param=value]...
Parameters can be passed positionally or by name using key=value syntax, like Python function arguments. Once a keyword argument appears, all subsequent arguments must also be keyword. Optional parameters (those with defaults) use key=value syntax.
A token with = is treated as a keyword argument only if the key matches a known parameter name. This allows values containing = (such as URLs with query strings) to be passed positionally without ambiguity.
@@foreach ?br item wait=0.5
@@foreach ?br placeholder=item wait=0.5
@@foreach variable=?br placeholder=item wait=0.5
These three forms are equivalent.
Directives#
@@with#
Switch to a named source or explicit endpoint and, when needed, select the query engine for subsequent queries. Source names are declared in #sources; direct endpoint URLs can be used without declaring a source.
Syntax: @@with [source=<source>|endpoint=<url>] [engine=<sparql|sparql-anything>]
@@with index
SELECT ?citing ?cited WHERE { ... }
@@with endpoint=https://opencitations.net/index/sparql
SELECT ?citing ?cited WHERE { ... }
Parameter |
Required |
Description |
|---|---|---|
|
yes for |
Name declared in |
|
yes for |
SPARQL endpoint URL |
|
no |
|
@@join#
Join the next query’s results with the current accumulator.
Syntax: @@join <left_var> <right_var> [type=<inner|left>]
@@join ?doi ?doi type=left
SELECT ?doi ?citation_count WHERE { ... }
Parameter |
Required |
Default |
Description |
|---|---|---|---|
|
yes |
Join key from the accumulator |
|
|
yes |
Join key from the next query |
|
|
no |
|
|
Join keys are normalized (http/https unification, trailing slash removal) to handle minor URL differences between endpoints.
When a right-side column name collides with an existing column, it gets a _r suffix.
@@values#
Inject accumulated values into the next query as a SPARQL VALUES clause.
Syntax: @@values <var>...
@@values ?doi
SELECT ?doi ?abstract WHERE { ... }
Takes one or more ?variable names. RAMOSE collects distinct values for the listed variables from the accumulator and inserts a VALUES block into the next query’s WHERE clause. Literal values are quoted; IRIs (starting with http:// or https://) are wrapped in angle brackets.
@@foreach#
Iterate the next query once per distinct value of a variable from the accumulator.
Syntax: @@foreach <variable> <placeholder> [wait=<seconds>]
@@foreach ?br item wait=0.5
SELECT ?result WHERE {
BIND(<[[item]]> as ?br)
...
}
Parameter |
Required |
Default |
Description |
|---|---|---|---|
|
yes |
Column from the accumulator to iterate over (must start with |
|
|
yes |
Name used as |
|
|
no |
|
Pause in seconds (float) between iterations |
Results from all iterations are concatenated.
@@remove#
Drop columns from the accumulator.
Syntax: @@remove <var>...
@@remove ?batch_id ?temp_var
Takes one or more ?variable names. Useful for cleaning up intermediate columns before the final output.
@@page#
Keep only one page of distinct values of a variable, so the steps that follow resolve only that page.
Syntax: @@page <variable> [default_size=<N>] [max_size=<M>]
@@page ?id default_size=10 max_size=100
The page number and page size come from the request’s page and page_size parameters, the same ones used by RAMOSE’s built-in pagination. When page_size is absent the directive uses default_size; with neither set it does nothing and every row passes through. An explicit page_size above max_size is rejected with HTTP 422.
The directive counts the distinct values of <variable> in first-appearance order, so a preceding ORDER BY decides which values land on each page. It keeps the rows whose value belongs to the requested page and records the total count of distinct values, the current page, and the page size on the operation. The output converter reads these through the request URL to report the totals.
One way to use it: place @@page after a cheap query that returns just the variable to paginate (and any sort key), and before the queries that resolve full per-item data. The page is fixed first, so the expensive resolution runs for one page instead of every match.
Full example#
A query that fetches metadata from OpenCitations Meta and joins citation counts from the OpenCitations Index:
#sources meta=https://opencitations.net/meta/sparql; index=https://opencitations.net/index/sparql
#sparql
SELECT ?doi ?title WHERE {
?identifier literal:hasLiteralValue "[[doi]]"^^xsd:string ;
datacite:usesIdentifierScheme datacite:doi ;
^datacite:hasIdentifier ?res .
?res dcterm:title ?title .
BIND("[[doi]]"^^xsd:string as ?doi)
}
@@with index
@@join ?doi ?doi type=left
SELECT ?doi ?citation_count WHERE {
BIND("[[doi]]" as ?doi)
{
SELECT (COUNT(?citing) as ?citation_count) WHERE {
?citing cito:cites ?cited .
?cited datacite:hasIdentifier/literal:hasLiteralValue "[[doi]]"^^xsd:string
}
}
}
This fetches the title from Meta, then joins the citation count from Index. The left join keeps the row even if the Index has no citation data for that DOI.
SPARQL Anything#
SPARQL Anything lets you query non-RDF data sources (CSV, JSON, XML, etc.) using SPARQL. RAMOSE integrates it via PySPARQL-Anything.
This requires the optional extra:
pip install ramose[sparql-anything]
Use @@with engine=sparql-anything before the query:
@@with engine=sparql-anything
SELECT * WHERE {
SERVICE <x-sparql-anything:location=https://example.org/data.csv> {
?s ?p ?o
}
}