Patterns
OPTIONAL, FILTER, BIND, UNION, and compound patterns.
Basic triple patterns require all patterns to match. Real scripts need more flexibility: optional data, conditions, computed values, alternatives.
The examples on this page assume the following prefixes:
PREFIX tasks: <https://example.org/spec/tasks#>
PREFIX people: <https://example.org/people/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>OPTIONAL
Include results even when some data is missing:
PREFIX tasks: <https://example.org/spec/tasks#>
SELECT ?task ?title ?description WHERE {
?task a tasks:Task ;
tasks:title ?title .
OPTIONAL {
?task tasks:description ?description .
}
}Tasks without a description still appear in results. ?description is unbound for those rows.
OPTIONAL blocks can contain multiple patterns. All patterns in the block must match, or the entire block is skipped:
PREFIX tasks: <https://example.org/spec/tasks#>
PREFIX people: <https://example.org/people/>
OPTIONAL {
?task tasks:assignee ?person .
?person people:email ?email .
}If the task has an assignee but that person has no email, neither ?person nor ?email is bound.
FILTER
Keep only results that meet a condition:
PREFIX tasks: <https://example.org/spec/tasks#>
SELECT ?task ?title WHERE {
?task a tasks:Task ;
tasks:title ?title ;
tasks:priority ?priority .
FILTER(?priority = "high" || ?priority = "critical")
}Common filter expressions:
PREFIX tasks: <https://example.org/spec/tasks#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
FILTER(?count > 10) # Numeric comparison
FILTER(?status != tasks:Completed) # Not equal
FILTER(CONTAINS(?title, "Q3")) # String contains
FILTER(STRSTARTS(?code, "TASK-")) # Starts with
FILTER(BOUND(?description)) # Variable is bound
FILTER(!BOUND(?assignee)) # Variable is NOT bound
FILTER(REGEX(?name, "^alice", "i")) # Regex (case insensitive)
FILTER(?date > "2025-01-01"^^xsd:date) # Date comparisonBIND
Compute a new value and assign it to a variable:
PREFIX tasks: <https://example.org/spec/tasks#>
SELECT ?task ?label WHERE {
?task a tasks:Task ;
tasks:title ?title ;
tasks:status ?status .
BIND(CONCAT(?title, " [", STR(?status), "]") AS ?label)
}BIND is evaluated for each result row. The variable must not already be bound.
Common BIND patterns:
BIND(NOW() AS ?timestamp) # Current time
BIND(URI(CONCAT("urn:task:", ?id)) AS ?taskUri) # Construct a URI
BIND(COALESCE(?nickname, ?name) AS ?displayName) # First non-null
BIND(IF(?priority = "high", 1, 0) AS ?isUrgent) # Conditional
BIND(STRLEN(?description) AS ?length) # String lengthVALUES
Provide inline data to match against:
PREFIX tasks: <https://example.org/spec/tasks#>
SELECT ?task ?title WHERE {
VALUES ?status { tasks:Open tasks:InProgress }
?task a tasks:Task ;
tasks:title ?title ;
tasks:status ?status .
}This finds tasks with status Open or InProgress. VALUES works like an inline table.
UNION
Match alternative patterns:
PREFIX tasks: <https://example.org/spec/tasks#>
SELECT ?item ?title WHERE {
{
?item a tasks:Task ;
tasks:title ?title .
} UNION {
?item a tasks:BugReport ;
tasks:title ?title .
}
}Results include matches from either branch. In practice, if BugReport is a subclass of Task, you'd just select Task and inference handles the rest.
Nested Patterns
Patterns compose. OPTIONAL blocks can contain FILTERs, BINDs can use OPTIONAL variables, etc.:
PREFIX tasks: <https://example.org/spec/tasks#>
PREFIX people: <https://example.org/people/>
SELECT ?task ?title ?assigneeName WHERE {
?task a tasks:Task ;
tasks:title ?title .
OPTIONAL {
?task tasks:assignee ?person .
?person people:name ?assigneeName .
FILTER(?assigneeName != "")
}
FILTER(BOUND(?title))
}See Also
- Basics: SELECT, WHERE, and simple triple patterns
- Functions: built-in functions for BIND and FILTER expressions
- CONSTRUCT and DESCRIBE: building new subgraphs from script results