SPARQL (SPARQL Protocol and RDF Query Language) is the SQL of the semantic web. This guide teaches you to write custom queries against the shipment knowledge graph.
SPARQL Endpoint
Execute custom SPARQL queries via REST API:
curl -X POST http://localhost:8081/api/v1/grafo/sparql \
-H "Content-Type: application/json" \
-d '{
"query": "PREFIX enc: <http://www.encomiendas.com/ontologia#> SELECT ?codigo WHERE { ?envio enc:codigoSeguimiento ?codigo } LIMIT 10"
}'
Code Reference: (svc-web-semantica/src/main/java/org/jchilon3mas/springcloud/svc/web/semantica/svc_web_semantica/controllers/SemanticController.java:38)
Query Basics
Anatomy of a SPARQL Query
PREFIX enc: <http://www.encomiendas.com/ontologia#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?codigo ?estado ?peso
WHERE {
?envioURI enc: codigoSeguimiento ?codigo .
?envioURI enc: tieneEstado ?estado .
?envioURI enc: tienePesoKg ?peso .
}
ORDER BY ?estado
LIMIT 10
Parts:
PREFIX : Define namespace shortcuts
SELECT : Specify variables to return
WHERE : Pattern matching (like SQL WHERE + JOIN)
ORDER BY : Sort results (optional)
LIMIT : Restrict number of results (optional)
Variables
Variables start with ? or $:
?envioURI # URI of a shipment
?codigo # Literal value (tracking code)
?peso # Numeric value (weight)
Triple Patterns
Each line in WHERE is a triple pattern:
?subject predicate ?object .
Example:
?envioURI enc: tieneEstado "PENDIENTE" .
# ↑ ↑ ↑
# subject property object (literal)
Common Query Patterns
1. List All Shipments
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?codigo ?estado ?fechaEnvio
WHERE {
?envioURI enc: codigoSeguimiento ?codigo .
?envioURI enc: tieneEstado ?estado .
?envioURI enc: fechaEnvio ?fechaEnvio .
}
ORDER BY DESC ( ?fechaEnvio )
LIMIT 20
curl -X POST http://localhost:8081/api/v1/grafo/sparql \
-H "Content-Type: application/json" \
-d '{
"query": "PREFIX enc: <http://www.encomiendas.com/ontologia#> SELECT ?codigo ?estado ?fechaEnvio WHERE { ?envioURI enc:codigoSeguimiento ?codigo . ?envioURI enc:tieneEstado ?estado . ?envioURI enc:fechaEnvio ?fechaEnvio . } ORDER BY DESC(?fechaEnvio) LIMIT 20"
}'
2. Filter by State
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?codigo ?nombreRemitente ?peso
WHERE {
?clienteURI enc: realizaEnvio ?envioURI .
?clienteURI enc: tieneNombre ?nombreRemitente .
?envioURI enc: codigoSeguimiento ?codigo .
?envioURI enc: tieneEstado "PENDIENTE" .
OPTIONAL { ?envioURI enc: tienePesoKg ?peso }
}
OPTIONAL means the pattern is optional - results are returned even if ?peso doesn’t exist.
3. Weight Range Query
PREFIX enc: <http://www.encomiendas.com/ontologia#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?codigo ?peso ?descripcion
WHERE {
?envioURI enc: codigoSeguimiento ?codigo .
?envioURI enc: tienePesoKg ?peso .
?envioURI enc: contienePaquete ?descripcion .
FILTER ( xsd: decimal ( ?peso ) >= 2.0 && xsd: decimal ( ?peso ) <= 5.0 )
}
ORDER BY ?peso
Always cast numeric values with xsd:decimal() for comparisons in FILTER.
4. Find Heavy Packages (> 20kg)
PREFIX enc: <http://www.encomiendas.com/ontologia#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?codigo ?peso ?descripcion ?sucursalDestino
WHERE {
?envioURI enc: codigoSeguimiento ?codigo .
?envioURI enc: tienePesoKg ?peso .
?envioURI enc: contienePaquete ?descripcion .
?envioURI enc: entregarEnSucursal ?sucursalDestino .
FILTER ( xsd: decimal ( ?peso ) > 20 )
}
ORDER BY DESC ( ?peso )
5. Shipments by Destination
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?codigo ?origenEn ?destinoEn ?estado
WHERE {
?envioURI enc: codigoSeguimiento ?codigo .
?envioURI enc: origenEn ?origenEn .
?envioURI enc: destinoEn ?destinoEn .
?envioURI enc: tieneEstado ?estado .
FILTER ( contains ( lcase ( ?destinoEn ), "lima" ))
}
contains(lcase(?var), "text") performs case-insensitive substring matching.
6. Find Shipments by Client DNI
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?nombreCliente ?dniCliente ?codigo ?descripcion ?estado
WHERE {
?clienteURI enc: tieneDni ?dniCliente .
?clienteURI enc: tieneNombre ?nombreCliente .
?clienteURI enc: realizaEnvio ?envioURI .
?envioURI enc: codigoSeguimiento ?codigo .
?envioURI enc: contienePaquete ?descripcion .
?envioURI enc: tieneEstado ?estado .
FILTER ( ?dniCliente = "12345678" )
}
ORDER BY ?codigo
7. Search by Date Range
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?codigo ?fechaEnvio ?destinoEn
WHERE {
?envioURI enc: codigoSeguimiento ?codigo .
?envioURI enc: fechaEnvio ?fechaEnvio .
?envioURI enc: destinoEn ?destinoEn .
FILTER (
str ( ?fechaEnvio ) >= "2026-03-01" &&
str ( ?fechaEnvio ) <= "2026-03-31T23:59:59"
)
}
ORDER BY ?fechaEnvio
8. Delivered Shipments with Delivery Date
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?codigo ?nombreDestinatario ?fechaEnvio ?fechaEntrega
WHERE {
?envioURI enc: codigoSeguimiento ?codigo .
?envioURI enc: nombreDestinatario ?nombreDestinatario .
?envioURI enc: tieneEstado "ENTREGADO" .
?envioURI enc: fechaEnvio ?fechaEnvio .
?envioURI enc: fechaEntrega ?fechaEntrega .
}
ORDER BY DESC ( ?fechaEntrega )
9. Count Shipments by State
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?estado ( COUNT ( ?envioURI ) AS ?total )
WHERE {
?envioURI enc: tieneEstado ?estado .
}
GROUP BY ?estado
ORDER BY DESC ( ?total )
COUNT(), SUM(), AVG(), MIN(), MAX() are aggregate functions available in SPARQL.
10. Average Weight by State
PREFIX enc: <http://www.encomiendas.com/ontologia#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?estado ( AVG ( xsd: decimal ( ?peso )) AS ?pesoPromedio )
WHERE {
?envioURI enc: tieneEstado ?estado .
?envioURI enc: tienePesoKg ?peso .
}
GROUP BY ?estado
11. Shipments with Vehicle Assigned
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?codigo ?placa ?estado ?destinoEn
WHERE {
?envioURI enc: codigoSeguimiento ?codigo .
?envioURI enc: placaVehiculo ?placa .
?envioURI enc: tieneEstado ?estado .
?envioURI enc: destinoEn ?destinoEn .
FILTER ( BOUND ( ?placa ))
}
BOUND(?var) checks if a variable has a value (not null).
12. Find All Clients and Their Shipments
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?nombreCliente ?dniCliente ?telefono ?codigo
WHERE {
?clienteURI a enc: Cliente .
?clienteURI enc: tieneNombre ?nombreCliente .
?clienteURI enc: tieneDni ?dniCliente .
OPTIONAL { ?clienteURI enc: tieneTelefono ?telefono }
OPTIONAL {
?clienteURI enc: realizaEnvio ?envioURI .
?envioURI enc: codigoSeguimiento ?codigo .
}
}
ORDER BY ?nombreCliente ?codigo
a is shorthand for rdf:type - checks class membership.
Advanced Queries
Full Shipment Details
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT *
WHERE {
?clienteURI enc: realizaEnvio ?envioURI .
?clienteURI enc: tieneNombre ?nombreRemitente .
?envioURI enc: codigoSeguimiento ?codigo .
OPTIONAL { ?clienteURI enc: tieneDni ?dniRemitente }
OPTIONAL { ?clienteURI enc: tieneTelefono ?telefonoRemitente }
OPTIONAL { ?envioURI enc: tieneEstado ?estado }
OPTIONAL { ?envioURI enc: contienePaquete ?descripcion }
OPTIONAL { ?envioURI enc: tienePesoKg ?peso }
OPTIONAL { ?envioURI enc: tieneDimensiones ?dimensiones }
OPTIONAL { ?envioURI enc: tienePrecio ?precio }
OPTIONAL { ?envioURI enc: nombreDestinatario ?nombreDestinatario }
OPTIONAL { ?envioURI enc: dniDestinatario ?dniDestinatario }
OPTIONAL { ?envioURI enc: origenEn ?origenEn }
OPTIONAL { ?envioURI enc: destinoEn ?destinoEn }
OPTIONAL { ?envioURI enc: sucursalOrigen ?sucursalOrigen }
OPTIONAL { ?envioURI enc: entregarEnSucursal ?sucursalDestino }
OPTIONAL { ?envioURI enc: fechaEnvio ?fechaEnvio }
OPTIONAL { ?envioURI enc: fechaEntrega ?fechaEntrega }
OPTIONAL { ?envioURI enc: placaVehiculo ?placaVehiculo }
FILTER ( ?codigo = "ENV-1710334200000" )
}
SELECT * returns all bound variables.
Calculate Delivery Time
PREFIX enc: <http://www.encomiendas.com/ontologia#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?codigo ?fechaEnvio ?fechaEntrega
( xsd: dateTime ( ?fechaEntrega ) - xsd: dateTime ( ?fechaEnvio ) AS ?duracion )
WHERE {
?envioURI enc: codigoSeguimiento ?codigo .
?envioURI enc: tieneEstado "ENTREGADO" .
?envioURI enc: fechaEnvio ?fechaEnvio .
?envioURI enc: fechaEntrega ?fechaEntrega .
}
ORDER BY DESC ( ?duracion )
Most Active Clients
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?nombreCliente ?dniCliente ( COUNT ( ?envioURI ) AS ?totalEnvios )
WHERE {
?clienteURI enc: tieneNombre ?nombreCliente .
?clienteURI enc: tieneDni ?dniCliente .
?clienteURI enc: realizaEnvio ?envioURI .
}
GROUP BY ?nombreCliente ?dniCliente
ORDER BY DESC ( ?totalEnvios )
LIMIT 10
Popular Routes
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?origenEn ?destinoEn ( COUNT ( ?envioURI ) AS ?cantidad )
WHERE {
?envioURI enc: origenEn ?origenEn .
?envioURI enc: destinoEn ?destinoEn .
}
GROUP BY ?origenEn ?destinoEn
ORDER BY DESC ( ?cantidad )
Revenue by Destination
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?destinoEn ( SUM ( xsd: decimal ( ?precio )) AS ?ingresoTotal )
WHERE {
?envioURI enc: destinoEn ?destinoEn .
?envioURI enc: tienePrecio ?precio .
}
GROUP BY ?destinoEn
ORDER BY DESC ( ?ingresoTotal )
SPARQL Functions
String Functions
# Case conversion
lcase ( ?var ) # lowercase
ucase ( ?var ) # uppercase
# String operations
contains ( ?str , "substring" ) # substring match
strstarts ( ?str , "prefix" ) # starts with
strends ( ?str , "suffix" ) # ends with
strlen ( ?str ) # length
substr ( ?str , start, length) # substring
# Concatenation
concat ( ?str1 , " - " , ?str2 )
# Replace
replace ( ?str , "pattern" , "replacement" )
Example:
SELECT ?codigo ( ucase ( ?estado ) AS ?estadoMayus )
WHERE {
?envio enc: codigoSeguimiento ?codigo .
?envio enc: tieneEstado ?estado .
}
Numeric Functions
abs ( ?num ) # absolute value
round ( ?num ) # round to integer
ceiling( ?num ) # round up
floor ( ?num ) # round down
Date Functions
now () # current timestamp
year ( ?date ) # extract year
month ( ?date ) # extract month
day ( ?date ) # extract day
Example:
SELECT ?codigo ( year ( ?fechaEnvio ) AS ?anio ) ( month ( ?fechaEnvio ) AS ?mes )
WHERE {
?envio enc: codigoSeguimiento ?codigo .
?envio enc: fechaEnvio ?fechaEnvio .
}
FILTER Operators
# Comparison
= , != , < , > , <= , >=
# Logical
&& (AND)
|| (OR)
! (NOT)
# Regex
regex ( ?var , "pattern" , "i" ) # i = case-insensitive
# Existence
BOUND ( ?var ) # variable has a value
! BOUND ( ?var ) # variable is null
# Type checking
isURI ( ?var )
isLiteral ( ?var )
Example:
FILTER (
?peso >= 2.0 &&
?peso <= 10.0 &&
( ?estado = "PENDIENTE" || ?estado = "EN_TRANSITO" )
)
Query Optimization Tips
Use LIMIT Always limit results during development:
Filter Early Put most restrictive FILTERs first to reduce intermediate results
Use OPTIONAL Wisely OPTIONAL patterns can be expensive. Only use when truly optional.
Index-Friendly Patterns Start patterns with specific subjects when possible
Query Types
SELECT (Most Common)
Returns variable bindings:
SELECT ?codigo ?peso WHERE { ... }
ASK
Returns boolean (true/false):
PREFIX enc: <http://www.encomiendas.com/ontologia#>
ASK {
?envio enc: codigoSeguimiento "ENV-1710334200000" .
?envio enc: tieneEstado "ENTREGADO" .
}
Response: true or false
CONSTRUCT
Returns RDF triples:
PREFIX enc: <http://www.encomiendas.com/ontologia#>
CONSTRUCT {
?envio enc: esUrgente true .
}
WHERE {
?envio enc: tienePesoKg ?peso .
FILTER ( xsd: decimal ( ?peso ) > 50 )
}
DESCRIBE
Returns all triples about a resource:
PREFIX enc: <http://www.encomiendas.com/ontologia#>
DESCRIBE <http://www.encomiendas.com/envio/ENV-1710334200000>
Debugging Queries
Start Simple
# Step 1: Get all shipments
SELECT ?envio WHERE { ?envio a enc: Envio } LIMIT 10
# Step 2: Add tracking codes
SELECT ?envio ?codigo WHERE {
?envio a enc: Envio .
?envio enc: codigoSeguimiento ?codigo .
} LIMIT 10
# Step 3: Add filters
SELECT ?codigo ?estado WHERE {
?envio enc: codigoSeguimiento ?codigo .
?envio enc: tieneEstado ?estado .
FILTER ( ?estado = "PENDIENTE" )
}
Check Variable Bindings
# See what variables are bound
SELECT * WHERE {
?envio enc: codigoSeguimiento ?codigo .
?envio enc: tienePesoKg ?peso .
} LIMIT 5
Test FILTERs Separately
# First without FILTER
SELECT ?codigo ?peso WHERE {
?envio enc: codigoSeguimiento ?codigo .
?envio enc: tienePesoKg ?peso .
}
# Then add FILTER
SELECT ?codigo ?peso WHERE {
?envio enc: codigoSeguimiento ?codigo .
?envio enc: tienePesoKg ?peso .
FILTER ( xsd: decimal ( ?peso ) > 10 )
}
Common Errors
No results but data exists
Possible causes:
Missing PREFIX declarations
Incorrect property URIs
FILTERs too restrictive
Data type mismatch (e.g., comparing string to decimal)
Solution: Start simple, add complexity incrementally
Possible causes:
Too many OPTIONAL patterns
Missing LIMIT
Cartesian product (multiple disconnected patterns)
Solution: Add LIMIT, remove unnecessary OPTIONALs, connect patterns
Solution: Cast with xsd:decimal() or xsd:integer():FILTER ( xsd: decimal ( ?peso ) > 5 )
Integration Examples
Python with SPARQLWrapper
from SPARQLWrapper import SPARQLWrapper, JSON
sparql = SPARQLWrapper( "http://localhost:8081/api/v1/grafo/sparql" )
query = """
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?codigo ?estado ?peso
WHERE {
?envio enc:codigoSeguimiento ?codigo .
?envio enc:tieneEstado ?estado .
?envio enc:tienePesoKg ?peso .
FILTER (?estado = "PENDIENTE")
}
"""
sparql.setQuery(query)
sparql.setReturnFormat( JSON )
results = sparql.query().convert()
for result in results[ "results" ][ "bindings" ]:
print ( f " { result[ 'codigo' ][ 'value' ] } : { result[ 'peso' ][ 'value' ] } kg" )
JavaScript/Node.js
const axios = require ( 'axios' );
const executeSparql = async ( query ) => {
const response = await axios . post (
'http://localhost:8081/api/v1/grafo/sparql' ,
{ query },
{ headers: { 'Content-Type' : 'application/json' } }
);
return response . data ;
};
const query = `
PREFIX enc: <http://www.encomiendas.com/ontologia#>
SELECT ?codigo ?estado
WHERE {
?envio enc:codigoSeguimiento ?codigo .
?envio enc:tieneEstado ?estado .
}
LIMIT 10
` ;
const results = await executeSparql ( query );
results . forEach ( row => {
console . log ( ` ${ row . codigo } : ${ row . estado } ` );
});
Resources
W3C SPARQL Spec Official SPARQL 1.1 specification
Apache Jena Jena SPARQL tutorial
SPARQL Cheat Sheet Quick reference guide
Online SPARQL Editor Test queries online (YASGUI)
Next Steps
Ontology Reference Understand all available properties
Semantic Search Use natural language queries
Semantic Web Concepts Learn RDF and OWL fundamentals
Architecture See how SPARQL fits in the system