Add SPARQL CONSTRUCT query templates (Blazegraph-only)

Implements #322: ConstructTemplate/ParameterDescriptor/ConstructTemplateRegistry
with injection-safe {{param}} rendering, Blazegraph CONSTRUCT-aware execute_sparql
extension, execute_construct_template (render->execute->parse->persist), and a
construct_template pipeline step. RDF4J/Jena support deferred to a follow-up issue.

Closes #322
This commit is contained in:
Sameer6305
2026-07-18 12:33:04 +05:30
parent 28b71c922f
commit c4e971c91c
10 changed files with 2457 additions and 36 deletions
+34
View File
@@ -495,6 +495,40 @@ result = engine.execute_pipeline(
Delta detection uses SHA-256 checksums on source content. Only sources whose checksum differs from `base_version_id` are passed to downstream steps. For pipelines that run hourly or daily against a growing corpus, delta mode eliminates redundant re-embedding and re-extraction.
</Note>
## SPARQL CONSTRUCT Template Steps
Use the `"construct_template"` step type to render and execute a [SPARQL CONSTRUCT template](triplet_store#sparql-construct-templates) as part of a pipeline. `store_backend` and `construct_template_registry` are execution-time resources, not step config — pass them to `execute_pipeline()`, the same way `delta_mode` steps receive `version_manager` and `triplet_store`:
```python
from semantica.pipeline import PipelineBuilder, ExecutionEngine
from semantica.triplet_store.construct_templates import construct_template_step_handler
builder = PipelineBuilder()
builder.add_step(
"apply_person_template",
"construct_template",
handler=construct_template_step_handler,
template_name="person_to_foaf",
params={"subject": "http://ex.org/p1", "name": "Alice", "age": 30},
target_graph="http://ex.org/graphs/people",
)
pipeline = builder.build("person_pipeline")
engine = ExecutionEngine()
result = engine.execute_pipeline(
pipeline,
data=None,
store_backend=store, # required: a BlazegraphStore instance
construct_template_registry=registry, # required: holds the registered template
)
triplets = result.output # List[Triplet], already persisted via store.add_triplets
```
<Note>
`construct_template` steps raise `ProcessingError` if `store_backend` or `construct_template_registry` is missing from `execute_pipeline()`'s options, and `ValidationError` if `template_name` isn't registered.
</Note>
## Schemas
<AccordionGroup>
+59
View File
@@ -277,6 +277,65 @@ store.execute_query("""
**`execute_query()` returns `QueryResult`, not a list.** Iterate `result.bindings`, not `result` directly. Each binding is a dict mapping variable name → `{"value": ..., "type": ...}`.
</Warning>
## SPARQL CONSTRUCT Templates
`semantica.triplet_store.construct_templates` provides parameterized SPARQL `CONSTRUCT` query templates: define a reusable query once, substitute typed parameters safely, and persist the resulting triples in one call. This is available for the **Blazegraph backend only** (see [Backends](#backends) above) — `BlazegraphStore.execute_sparql()` is the only backend with CONSTRUCT-aware RDF parsing.
```python
from semantica.triplet_store.construct_templates import (
ConstructTemplate,
ParameterDescriptor,
ConstructTemplateRegistry,
render_construct_template,
execute_construct_template,
)
from semantica.triplet_store import BlazegraphStore
# Define and register a template
template = ConstructTemplate(
name="person_to_foaf",
description="Maps a person record subject to a foaf:name triple",
construct_query="""
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
CONSTRUCT { <{{subject}}> foaf:name {{name}} ; foaf:age {{age}} }
WHERE { <{{subject}}> a <http://ex.org/Person> }
""",
parameters=[
ParameterDescriptor(name="subject", type="uri", required=True),
ParameterDescriptor(name="name", type="literal", required=True),
ParameterDescriptor(
name="age", type="typed-literal", required=False, default=0,
datatype="xsd:integer",
),
],
)
registry = ConstructTemplateRegistry()
registry.register(template)
# Render only: inspect the substituted SPARQL string, no network call
sparql = render_construct_template(
registry.get("person_to_foaf"),
params={"subject": "http://ex.org/p1", "name": "Alice", "age": 30},
)
# Render + execute + persist in one call
store = BlazegraphStore(endpoint="http://localhost:9999/blazegraph", namespace="kb")
triplets = execute_construct_template(
template=registry.get("person_to_foaf"),
params={"subject": "http://ex.org/p1", "name": "Alice", "age": 30},
store_backend=store,
target_graph="http://ex.org/graphs/people",
)
# triplets: List[Triplet], already persisted via store.add_triplets
```
Each `ParameterDescriptor.type` controls how its value is rendered: `"uri"` values are validated against an allowlist and wrapped in `<...>`, `"literal"` values are escaped and quoted, and `"typed-literal"` values require a `datatype` (e.g. `"xsd:integer"`) and render unquoted for numeric/boolean XSD types. Placeholders use `{{param}}` rather than SPARQL's own `?param` syntax so template placeholders are never confused with real SPARQL variables in the query body.
<Note>
CONSTRUCT templates are Blazegraph-only. `execute_construct_template()` raises `ProcessingError` if `store_backend` does not implement both `execute_sparql()` and `add_triplets()`.
</Note>
## SPARQL Result Pagination
For large result sets, paginate with LIMIT and OFFSET: