This guide covers upgrading from SPARQL 1.1 to SPARQL 1.2 in Exocortex, including new capabilities, recommended patterns, and migration considerations.
SPARQL 1.2 introduces several features that address common SPARQL 1.1 pain points:
| Feature | Benefit |
|---|---|
| LATERAL Joins | "Top N per group" patterns |
| PREFIX* | Simplified prefix management |
| DESCRIBE Options | Fine-grained DESCRIBE control |
| Directional Language Tags | Proper RTL/LTR support |
| DateTime Arithmetic | Native date/time operations |
| NORMALIZE/FOLD | Unicode-aware string handling |
Good news! The SPARQL 1.2 implementation in Exocortex is fully backward compatible with SPARQL 1.1 queries. All existing queries will continue to work without modification.
SPARQL 1.2 features are additive - they extend the language without changing existing behavior.
While there are no breaking changes, be aware of these considerations:
-
New Reserved Keywords:
LATERALis now a reserved keyword. If you use it as a variable or prefix name, rename it. -
Direction in Literals: Literals with directional language tags (
@en--ltr) are now handled differently. The direction is preserved and affects equality comparisons. -
PREFIX Availability*: The
PREFIX*syntax requires network access to resolve vocabularies (or uses cached well-known prefixes).
Before (SPARQL 1.1) - Getting top item per group was awkward:
# Complex workaround using nested subqueries
SELECT ?project ?topTask ?maxVotes
WHERE {
?project a :Project .
{
SELECT ?project (MAX(?votes) AS ?maxVotes)
WHERE {
?task :belongsTo ?project .
?task :votes ?votes .
}
GROUP BY ?project
}
?topTask :belongsTo ?project .
?topTask :votes ?maxVotes .
}After (SPARQL 1.2) - Clean and intuitive:
SELECT ?project ?topTask ?votes
WHERE {
?project a :Project .
LATERAL {
SELECT ?topTask ?votes WHERE {
?topTask :belongsTo ?project .
?topTask :votes ?votes .
}
ORDER BY DESC(?votes)
LIMIT 1
}
}Benefits:
- 40% shorter query
- Clearer intent ("top 1 per project")
- No duplicate matching issues
- Easily extend to "top N" by changing LIMIT
Before (SPARQL 1.1):
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX schema: <http://schema.org/>
SELECT ?name WHERE {
?person rdf:type foaf:Person .
?person foaf:name ?name .
}After (SPARQL 1.2):
PREFIX* <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX* <http://xmlns.com/foaf/0.1/>
SELECT ?name WHERE {
?person rdf:type foaf:Person .
?person foaf:name ?name .
}Benefits:
- Fewer lines of boilerplate
- Automatic prefix discovery
- Consistent prefix naming
Before (SPARQL 1.1):
SELECT ?name WHERE {
?s :name ?name .
FILTER(LCASE(?name) = LCASE("JOHN DOE"))
}After (SPARQL 1.2):
SELECT ?name WHERE {
?s :name ?name .
FILTER(FOLD(?name) = FOLD("JOHN DOE"))
}Benefits:
- Full Unicode support (not just ASCII)
- Proper handling of German ß, Greek sigma, etc.
- Ligature handling (fi = fi)
Before (SPARQL 1.1) - No control over DESCRIBE depth:
# Returns unpredictable amount of data
DESCRIBE ?resourceAfter (SPARQL 1.2) - Precise control:
# Only direct properties
DESCRIBE ?resource DEPTH 1
# Include one hop of related resources
DESCRIBE ?resource DEPTH 2 SYMMETRICBenefits:
- Predictable result size
- Better performance
- Explicit about what's included
LATERAL enables patterns that were impossible or very complex in SPARQL 1.1:
# Get top 3 tasks per project by votes
SELECT ?project ?task ?votes
WHERE {
?project a :Project .
LATERAL {
SELECT ?task ?votes WHERE {
?task :belongsTo ?project .
?task :votes ?votes .
}
ORDER BY DESC(?votes)
LIMIT 3
}
}Calculate aggregates that depend on outer context:
# For each area, get its projects with task counts
SELECT ?area ?project ?taskCount
WHERE {
?area a :Area .
LATERAL {
SELECT ?project (COUNT(?task) AS ?taskCount) WHERE {
?project :belongsTo ?area .
?task :belongsTo ?project .
}
GROUP BY ?project
HAVING (?taskCount > 0)
}
}Proper support for bidirectional text:
# Create Arabic label with correct direction
SELECT (STRLANGDIR("مرحبا بك", "ar", "rtl") AS ?greeting)
WHERE {}
# Check if text has direction specified
SELECT ?label
WHERE {
?s :label ?label .
FILTER(hasLANGDIR(?label))
}Native date/time arithmetic:
# Calculate duration between events
SELECT ?event ?start ?end (?end - ?start AS ?duration)
WHERE {
?event :startTime ?start .
?event :endTime ?end .
}
# Get date 7 days ago
SELECT (dateSubtract("2025-01-22", "P7D") AS ?weekAgo)
WHERE {}# Normalize Unicode for comparison
SELECT ?s WHERE {
?s :label ?label .
FILTER(NORMALIZE(?label) = NORMALIZE("café"))
}
# Case-folded search (handles Straße = strasse)
SELECT ?company WHERE {
?company :name ?name .
FILTER(FOLD(?name) = FOLD("STRASSE"))
}Don't use complex MAX() subqueries with self-joins. Do use LATERAL with ORDER BY and LIMIT.
LCASE only handles ASCII. FOLD provides full Unicode support.
Unbounded DESCRIBE can return unexpectedly large results. Always specify DEPTH.
Reduces boilerplate and ensures consistent prefix naming.
When working with RTL languages (Arabic, Hebrew), use directional literals:
FILTER(LANG(?text) = "ar" || hasLANGDIR(?text))When comparing strings from different sources:
FILTER(NORMALIZE(?a) = NORMALIZE(?b))Use this checklist when migrating queries to SPARQL 1.2:
- Review queries with complex nested subqueries for LATERAL replacement
- Check for "top N per group" patterns - prime candidates for LATERAL
- Replace LCASE() comparisons with FOLD() for Unicode support
- Add DEPTH to DESCRIBE queries for predictability
- Consider PREFIX* for standard vocabulary prefixes
- Review multilingual content for directional tag opportunities
- Check for date/time calculations that could use native arithmetic
| SPARQL 1.1 Feature | SPARQL 1.2 Status |
|---|---|
| SELECT | Unchanged |
| CONSTRUCT | Unchanged |
| DESCRIBE | Extended with DEPTH/SYMMETRIC |
| ASK | Unchanged |
| FILTER | Extended with new functions |
| OPTIONAL | Unchanged |
| UNION | Unchanged |
| MINUS | Unchanged |
| EXISTS/NOT EXISTS | Unchanged |
| Property Paths | Unchanged |
| Aggregates | Unchanged |
| Subqueries | Extended with LATERAL |
| VALUES | Unchanged |
| BIND | Unchanged |
- SPARQL 1.2 Features - Complete feature reference
- SPARQL User Guide - Basic SPARQL usage
- Query Examples - Practical query patterns