SPARQL
Fluree provides full support for SPARQL 1.1, the W3C standard query language for RDF. SPARQL enables compatibility with existing RDF tools, knowledge graphs, and semantic web applications.
Overview
SPARQL (SPARQL Protocol and RDF Query Language) is the industry standard for querying RDF data. Fluree implements SPARQL 1.1, providing full compatibility with SPARQL endpoints and tools.
Basic SPARQL Query
PREFIX ex: <http://example.org/ns/>
SELECT ?name ?age
WHERE {
?person ex:name ?name .
?person ex:age ?age .
}
Default Prefixes
When querying via the CLI, a ledger's default context prefix mappings are injected into SPARQL queries that have no explicit PREFIX declarations. The HTTP API defaults this behavior off; pass ?default-context=true on ledger-scoped query requests to opt in. For example, if the default context includes {"ex": "http://example.org/ns/"}, this query works without a PREFIX line when default-context injection is enabled:
SELECT ?name ?age
WHERE {
?person ex:name ?name .
?person ex:age ?age .
}
If a query includes any PREFIX declarations, the default context is not used — you must declare every prefix you need. To explicitly opt out of the default context without defining any real prefix, use PREFIX : <>. See opting out of the default context for details.
Note: When using
fluree-db-apidirectly (embedded), queries must declare their ownPREFIXdeclarations. The default context is not injected automatically by the core API. Usedb_with_default_context()orGraphDb::with_default_context()to opt in. See Default Context for details.
You can view and manage the default context with fluree context get/set or GET/PUT /v1/fluree/context/{ledger...}.
Query Forms
SELECT Queries
Return variable bindings:
PREFIX ex: <http://example.org/ns/>
SELECT ?name ?email
WHERE {
?person ex:name ?name .
?person ex:email ?email .
}
DISTINCT Results:
SELECT DISTINCT ?name
WHERE {
?person ex:name ?name .
}
Reduced Results:
SELECT REDUCED ?name
WHERE {
?person ex:name ?name .
}
CONSTRUCT Queries
Generate RDF graphs from query results:
PREFIX ex: <http://example.org/ns/>
CONSTRUCT {
?person ex:displayName ?name .
}
WHERE {
?person ex:name ?name .
}
See CONSTRUCT Queries for details.
ASK Queries
Return boolean indicating if query matches:
PREFIX ex: <http://example.org/ns/>
ASK {
?person ex:name "Alice" .
}
DESCRIBE Queries
Return RDF description of resources:
PREFIX ex: <http://example.org/ns/>
DESCRIBE ex:alice
Fluree’s DESCRIBE returns outgoing triples for each described resource (equivalent to CONSTRUCT { ?r ?p ?o } WHERE { VALUES ?r { ... } . ?r ?p ?o }).
Basic Graph Patterns
Triple Patterns
Match RDF triples:
?person ex:name ?name .
Multiple Patterns
Combine patterns with AND semantics:
?person ex:name ?name .
?person ex:age ?age .
?person ex:email ?email .
Property Paths
SPARQL property paths allow complex traversal patterns in the predicate position of a triple pattern.
Supported Operators
| Syntax | Name | Description |
|---|---|---|
p+ | One or more | Transitive closure (follows p one or more hops) |
p* | Zero or more | Reflexive transitive closure (includes self) |
^p | Inverse | Traverses p in reverse direction |
p|q | Alternative | Matches either p or q (UNION semantics) |
p/q | Sequence | Follows p then q (property chain) |
One or More (+):
?person ex:parent+ ?ancestor .
Zero or More (*):
?person ex:parent* ?ancestorOrSelf .
Inverse (^):
?child ^ex:parent ?parent .
This is equivalent to ?parent ex:parent ?child — it reverses the traversal direction.
Inverse can also be applied to complex paths (sequences and alternatives):
?s ^(ex:friend/ex:name) ?o . -- inverse of a sequence
?s ^(ex:name|ex:nick) ?o . -- inverse of an alternative
^(ex:friend/ex:name)reverses the step order and inverts each step:(^ex:name)/(^ex:friend)^(ex:name|ex:nick)distributes inverse into each branch:(^ex:name)|(^ex:nick)- Double inverse cancels:
^(^ex:p)simplifies toex:p
Alternative (|):
?person ex:friend|ex:colleague ?related .
This produces UNION semantics: results from both ex:friend and ex:colleague are combined (bag semantics, so duplicates are preserved).
Three-way and inverse alternatives are supported:
?s ex:a|ex:b|ex:c ?o .
?s ex:friend|^ex:colleague ?related .
Alternative branches can also be sequence chains. For example, to get the name via either the friend or colleague path:
?s (ex:friend/ex:name)|(ex:colleague/ex:name) ?name .
Branches can freely mix simple predicates, inverse predicates, and sequence chains:
?s ex:name|(ex:friend/ex:name)|^ex:colleague ?val .
Sequence (/) — Property Chains:
?person ex:friend/ex:name ?friendName .
This follows ex:friend then ex:name, expanding into a chain of triple patterns joined by internal variables. Multi-step chains are supported:
?person ex:friend/ex:friend/ex:name ?fofName .
Sequence steps can include inverse predicates:
?person ^ex:friend/ex:name ?name .
This traverses ex:friend backwards (finding who links to ?person), then follows ex:name forward.
Sequence steps can also be alternatives. For example, ex:friend/(ex:name|ex:nick) distributes the alternative into a union of chains (ex:friend/ex:name and ex:friend/ex:nick):
?person ex:friend/(ex:name|ex:nick) ?label .
Multiple alternative steps are supported: (ex:a|ex:b)/(ex:c|ex:d) expands to 4 chains. A safety limit of 64 expanded chains is enforced to prevent combinatorial explosion.
Rules:
- Transitive paths (
+,*) require at least one variable (both subject and object cannot be constants). - Sequence (
/) steps must be simple predicates (ex:p), inverse simple predicates (^ex:p), or alternatives of simple predicates ((ex:a|ex:b)). Transitive (+/*) and nested sequence modifiers are not allowed inside sequence steps. - Variable names starting with
?__are reserved for internal use and will not appear inSELECT *(wildcard) output.
Not Yet Supported
The following operators are parsed but not yet supported for execution:
| Syntax | Name |
|---|---|
p? | Zero or one (optional step) |
!p or !(p|q) | Negated property set |
Query Modifiers
FILTER
Filter results with conditions:
SELECT ?name ?age
WHERE {
?person ex:name ?name .
?person ex:age ?age .
FILTER (?age > 18)
}
Multiple Filters:
FILTER (?age > 18 && ?age < 65)
FILTER (regex(?name, "^A"))
OPTIONAL
Match optional patterns:
SELECT ?name ?email
WHERE {
?person ex:name ?name .
OPTIONAL { ?person ex:email ?email . }
}
Multiple Optionals:
SELECT ?name ?email ?phone
WHERE {
?person ex:name ?name .
OPTIONAL { ?person ex:email ?email . }
OPTIONAL { ?person ex:phone ?phone . }
}
UNION
Match alternative patterns:
SELECT ?name
WHERE {
{ ?person ex:name ?name . }
UNION
{ ?person ex:alias ?name . }
}
MINUS
Exclude matching patterns:
SELECT ?person
WHERE {
?person ex:type ex:User .
MINUS { ?person ex:status ex:Inactive . }
}
BIND
Compute values:
SELECT ?name ?nextAge
WHERE {
?person ex:name ?name .
?person ex:age ?age .
BIND (?age + 1 AS ?nextAge)
}
VALUES
Provide initial bindings:
SELECT ?person ?name
WHERE {
VALUES ?name { "Alice" "Bob" "Carol" }
?person ex:name ?name .
}
Aggregation
GROUP BY
Group results by variable:
SELECT ?category (COUNT(?product) AS ?count)
WHERE {
?product ex:category ?category .
}
GROUP BY ?category
Expression-based GROUP BY:
Group by a computed expression using (expr AS ?alias) syntax:
SELECT ?initial (COUNT(?name) AS ?count)
WHERE {
?person ex:name ?name .
}
GROUP BY (SUBSTR(?name, 1, 1) AS ?initial)
The expression is evaluated per row and bound to the alias variable before grouping. Any SPARQL expression is supported, including function calls, arithmetic, and type casts.
HAVING
Filter grouped results:
SELECT ?category (COUNT(?product) AS ?count)
WHERE {
?product ex:category ?category .
}
GROUP BY ?category
HAVING (COUNT(?product) > 10)
Aggregation Functions
COUNT(?var)- Count non-null valuesSUM(?var)- Sum numeric valuesAVG(?var)- Average numeric valuesMIN(?var)- Minimum valueMAX(?var)- Maximum valueSAMPLE(?var)- Arbitrary value from groupGROUP_CONCAT(?var; separator=",")- Concatenate values
All aggregate functions support the DISTINCT modifier, which eliminates duplicate values before aggregation:
SELECT ?category (COUNT(DISTINCT ?customer) AS ?unique_buyers)
(SUM(DISTINCT ?price) AS ?unique_price_total)
WHERE {
?order ex:category ?category .
?order ex:customer ?customer .
?order ex:price ?price .
}
GROUP BY ?category
Aggregate result types: COUNT and SUM of integers return xsd:integer. SUM of mixed numeric types and AVG return xsd:double.
Sorting and Limiting
ORDER BY
Sort results:
SELECT ?name ?age
WHERE {
?person ex:name ?name .
?person ex:age ?age .
}
ORDER BY ?name
Descending:
ORDER BY DESC(?age)
Multiple Sort Keys:
ORDER BY ?last ASC(?first) DESC(?age)
LIMIT
Limit number of results:
SELECT ?name
WHERE {
?person ex:name ?name .
}
LIMIT 10
OFFSET
Skip results:
SELECT ?name
WHERE {
?person ex:name ?name .
}
OFFSET 20
LIMIT 10
Datasets
FROM
Specify default graph:
PREFIX ex: <http://example.org/ns/>
SELECT ?name
FROM <mydb:main>
WHERE {
?person ex:name ?name .
}
Multiple Default Graphs:
SELECT ?name
FROM <mydb:main>
FROM <otherdb:main>
WHERE {
?person ex:name ?name .
}
FROM NAMED
Specify named graphs:
PREFIX ex: <http://example.org/ns/>
SELECT ?graph ?name
FROM NAMED <mydb:main>
FROM NAMED <otherdb:main>
WHERE {
GRAPH ?graph {
?person ex:name ?name .
}
}
Fluree also exposes a built-in named graph inside each ledger for transaction / commit metadata:
FROM <mydb:main#txn-meta>(txn-meta as the default graph), orFROM NAMED <mydb:main#txn-meta>andGRAPH <mydb:main#txn-meta> { ... }
See Datasets for details.
SPARQL Functions
String Functions
STR(?x)- String valueLANG(?x)- Language tagLANGMATCHES(?lang, ?pattern)- Language matchREGEX(?str, ?pattern)- Regular expressionREPLACE(?str, ?pattern, ?replacement)- ReplaceSUBSTR(?str, ?start, ?length)- SubstringSTRLEN(?str)- String lengthUCASE(?str)- UppercaseLCASE(?str)- LowercaseENCODE_FOR_URI(?str)- URI encodeCONCAT(?str1, ?str2, ...)- Concatenate
Numeric Functions
ABS(?x)- Absolute valueROUND(?x)- RoundCEIL(?x)- CeilingFLOOR(?x)- FloorRAND()- Random number
Date/Time Functions
NOW()- Current timestampYEAR(?date)- YearMONTH(?date)- MonthDAY(?date)- DayHOURS(?time)- HoursMINUTES(?time)- MinutesSECONDS(?time)- Seconds
Type Conversion
STRDT(?str, ?datatype)- String to typed literalSTRLANG(?str, ?lang)- String with languageDATATYPE(?literal)- DatatypeIRI(?str)- IRI from stringURI(?str)- URI from stringBNODE(?str)- Blank node
XSD Datatype Constructors (Casts)
Per W3C SPARQL 1.1 §17.5, XSD constructor functions cast values between datatypes. Invalid casts produce unbound (no binding), not errors.
xsd:boolean(?x)- Cast to boolean ("true","1"→ true;"false","0"→ false; numeric 0 → false, non-zero → true)xsd:integer(?x)- Cast to integer (truncates doubles, parses strings)xsd:float(?x)- Cast to single-precision floatxsd:double(?x)- Cast to double-precision floatxsd:decimal(?x)- Cast to decimal (rejects scientific notation strings)xsd:string(?x)- Cast to string (canonical form for decimals)
Logical Functions
BOUND(?var)- Variable is boundIF(?condition, ?then, ?else)- ConditionalCOALESCE(?x, ?y, ...)- First non-null valueISIRI(?x)- Is IRIISURI(?x)- Is URIISBLANK(?x)- Is blank nodeISLITERAL(?x)- Is literalISNUMERIC(?x)- Is numeric
Subqueries
Nest queries:
SELECT ?person ?name
WHERE {
?person ex:name ?name .
{
SELECT ?person
WHERE {
?person ex:age ?age .
FILTER (?age > 18)
}
}
}
Service Queries
SERVICE enables cross-ledger queries within Fluree. You can execute patterns against different ledgers within the same query using the fluree:ledger: URI scheme.
Basic Cross-Ledger Query
Query data from another ledger in your dataset:
PREFIX ex: <http://example.org/ns/>
SELECT ?customer ?name ?total
FROM <customers:main>
FROM NAMED <orders:main>
WHERE {
?customer ex:name ?name .
SERVICE <fluree:ledger:orders:main> {
?order ex:customer ?customer ;
ex:total ?total .
}
}
Endpoint URI Format
For local Fluree ledger queries, use the fluree:ledger: scheme:
| Format | Description | Matches dataset ledger ID |
|---|---|---|
fluree:ledger:<name> | Query ledger with default branch (main) | <name>:main |
fluree:ledger:<name>:<branch> | Query specific branch | <name>:<branch> |
Where:
<name>is the ledger name without the branch (e.g.,orders,acme/people)<branch>is the branch name (e.g.,main,dev)- The full dataset ledger ID is always
<name>:<branch>(e.g.,orders:main,acme/people:dev)
The endpoint is resolved by matching against the full ledger_id in the dataset.
Examples:
SERVICE <fluree:ledger:orders> { ... } -- matches orders:main
SERVICE <fluree:ledger:orders:main> { ... } -- matches orders:main (explicit)
SERVICE <fluree:ledger:orders:dev> { ... } -- matches orders:dev
SERVICE SILENT
Use SERVICE SILENT to return empty results instead of failing if the service errors or is unavailable:
PREFIX ex: <http://example.org/ns/>
SELECT ?name ?order
WHERE {
?person ex:name ?name .
SERVICE SILENT <fluree:ledger:orders:main> {
?order ex:customer ?person .
}
}
If the orders ledger is not in the dataset or encounters an error, the query returns results with unbound ?order values instead of failing.
Variable Endpoints
SERVICE supports variable endpoints that iterate over available ledgers:
PREFIX ex: <http://example.org/ns/>
SELECT ?ledger ?person ?name
FROM NAMED <db1:main>
FROM NAMED <db2:main>
WHERE {
SERVICE ?ledger {
?person ex:name ?name .
}
}
This queries all named ledgers in the dataset.
Cross-Ledger Join Example
Join customer data from one ledger with their orders from another:
PREFIX ex: <http://example.org/ns/>
SELECT ?customerName ?productName ?quantity
FROM <customers:main>
FROM NAMED <orders:main>
FROM NAMED <products:main>
WHERE {
# Get customer from default graph (customers ledger)
?customer ex:name ?customerName .
# Get orders for this customer from orders ledger
SERVICE <fluree:ledger:orders:main> {
?order ex:customer ?customer ;
ex:product ?product ;
ex:quantity ?quantity .
}
# Get product details from products ledger
SERVICE <fluree:ledger:products:main> {
?product ex:name ?productName .
}
}
Requirements
- The target ledger must be included in the dataset (via
FROMorFROM NAMEDclauses) - Results are joined with the outer query on shared variables
- SERVICE patterns are executed as correlated subqueries (like EXISTS)
Remote Fluree Federation
SERVICE supports querying ledgers on remote Fluree instances using the fluree:remote: scheme. This enables cross-server federation — a single SPARQL query can join data from local ledgers with data from ledgers on other Fluree servers.
Remote Endpoint Format
| Format | Description |
|---|---|
fluree:remote:<connection>/<ledger> | Query a ledger on a registered remote server |
Where:
<connection>is a named remote connection registered at build time (maps to a server URL + bearer token)<ledger>is the ledger ID on the remote server (e.g.,customers:main,acme/people:main)
Example: Cross-Server Join
PREFIX ex: <http://example.org/ns/>
SELECT ?localName ?remoteEmail
WHERE {
?person ex:name ?localName .
SERVICE <fluree:remote:acme/customers:main> {
?person ex:email ?remoteEmail .
}
}
This queries ?person ex:name from the local ledger and joins with ?person ex:email from the customers:main ledger on the remote server named acme.
Multiple Ledgers on the Same Remote Server
A single remote connection gives access to any ledger the bearer token is authorized for:
PREFIX ex: <http://example.org/ns/>
SELECT ?customer ?orderId ?productName
WHERE {
SERVICE <fluree:remote:acme/customers:main> {
?customer ex:name ?name .
?customer ex:id ?customerId .
}
SERVICE <fluree:remote:acme/orders:main> {
?order ex:customerId ?customerId .
?order ex:orderId ?orderId .
?order ex:product ?product .
}
SERVICE <fluree:remote:acme/products:main> {
?product ex:name ?productName .
}
}
SILENT with Remote Endpoints
SERVICE SILENT works with remote endpoints. If the remote server is unreachable, the connection is not registered, or the bearer token is rejected, the SERVICE block returns empty results instead of failing the query:
SERVICE SILENT <fluree:remote:partner/inventory:main> {
?item ex:sku ?sku .
}
Registering Remote Connections
Remote connections are registered at connection build time via the Rust API or server configuration. See Configuration: Remote connections and Rust API: Remote federation for setup details.
Datatype Handling
Remote query results preserve their original datatypes. Values returned from a remote server are parsed into the same rich type system used for local data — xsd:dateTime, xsd:date, xsd:decimal, xsd:integer, etc. are all stored with their proper typed representations. Custom datatypes (e.g., http://example.org/myType) are also preserved: the value is kept as a string with the original datatype IRI retained, so round-tripping and downstream FILTER comparisons on shared custom types work correctly.
Limitations (v1)
- Uncorrelated execution only. The SERVICE body is sent to the remote server as a standalone query. Parent-row bindings are not injected as VALUES (bound-join). This means a SERVICE block that references variables bound in the outer query will not push those constraints to the remote server — the remote returns all matching rows, and the join happens locally.
- SPARQL queries only. Remote SERVICE is available in SPARQL queries. JSON-LD queries do not currently support the
fluree:remote:scheme. - No query cancellation propagation. If the local query is cancelled, in-flight remote HTTP requests are not aborted.
- Policy is local only. The remote server enforces its own policy based on the bearer token. The local server's policy engine does not filter rows returned from a remote SERVICE.
External SPARQL Endpoints
Federated queries to non-Fluree SPARQL endpoints (e.g., Wikidata, DBpedia) are not yet supported. Only the fluree:ledger: (local) and fluree:remote: (remote Fluree) schemes are currently available.
Time Travel
Point-in-Time Queries
Query data as it existed at a specific time using time specifiers in the FROM clause:
PREFIX ex: <http://example.org/ns/>
SELECT ?name ?age
FROM <ledger:main@t:100>
WHERE {
?person ex:name ?name ;
ex:age ?age .
}
Time specifiers:
@t:100- Transaction number@iso:2024-01-15T10:30:00Z- ISO 8601 datetime@commit:bafybeig...- Commit ContentId@t:latest- Current/latest state
History Queries
Query all changes (assertions and retractions) within a time range using FROM...TO with RDF-star syntax:
PREFIX ex: <http://example.org/ns/>
PREFIX f: <https://ns.flur.ee/db#>
SELECT ?age ?t ?op
FROM <ledger:main@t:1>
TO <ledger:main@t:latest>
WHERE {
<< ex:alice ex:age ?age >> f:t ?t .
<< ex:alice ex:age ?age >> f:op ?op .
}
ORDER BY ?t
The << subject predicate object >> syntax (RDF-star) treats the triple as an entity that can have metadata:
f:t- Transaction time (integer) when the fact was asserted or retracted.f:op- Operation type as a boolean:truefor assertions,falsefor retractions. MirrorsFlake.opon disk.
Filter by operation type:
PREFIX ex: <http://example.org/ns/>
PREFIX f: <https://ns.flur.ee/db#>
SELECT ?age ?t
FROM <ledger:main@t:1>
TO <ledger:main@t:latest>
WHERE {
<< ex:alice ex:age ?age >> f:t ?t .
<< ex:alice ex:age ?age >> f:op ?op .
FILTER(?op = false)
}
History with ISO datetime range:
PREFIX ex: <http://example.org/ns/>
PREFIX f: <https://ns.flur.ee/db#>
SELECT ?name ?t ?op
FROM <ledger:main@iso:2024-01-01T00:00:00Z>
TO <ledger:main@iso:2024-12-31T23:59:59Z>
WHERE {
<< ex:alice ex:name ?name >> f:t ?t .
<< ex:alice ex:name ?name >> f:op ?op .
}
SPARQL UPDATE
Fluree supports SPARQL 1.1 Update for modifying data using standard SPARQL syntax. SPARQL UPDATE requests use the application/sparql-update content type and are sent to the update endpoints.
INSERT DATA
Insert ground triples (no variables):
PREFIX ex: <http://example.org/ns/>
INSERT DATA {
ex:alice ex:name "Alice" .
ex:alice ex:age 30 .
ex:alice ex:email "alice@example.org" .
}
HTTP Request:
curl -X POST http://localhost:8090/v1/fluree/update/mydb:main \
-H "Content-Type: application/sparql-update" \
-d 'PREFIX ex: <http://example.org/ns/>
INSERT DATA { ex:alice ex:name "Alice" }'
DELETE DATA
Delete specific ground triples:
PREFIX ex: <http://example.org/ns/>
DELETE DATA {
ex:alice ex:email "alice@example.org" .
}
DELETE WHERE
Delete triples matching a pattern:
PREFIX ex: <http://example.org/ns/>
DELETE WHERE {
ex:alice ex:age ?age .
}
This finds all ex:age values for ex:alice and deletes them.
DELETE/INSERT (Modify)
The most powerful form combines WHERE, DELETE, and INSERT clauses:
PREFIX ex: <http://example.org/ns/>
DELETE {
?person ex:age ?oldAge .
}
INSERT {
?person ex:age ?newAge .
}
WHERE {
?person ex:name "Alice" .
?person ex:age ?oldAge .
BIND(?oldAge + 1 AS ?newAge)
}
Update multiple properties:
PREFIX ex: <http://example.org/ns/>
DELETE {
?person ex:name ?oldName .
?person ex:status ?oldStatus .
}
INSERT {
?person ex:name "Alicia" .
?person ex:status ex:Active .
}
WHERE {
?person ex:name "Alice" .
OPTIONAL { ?person ex:name ?oldName }
OPTIONAL { ?person ex:status ?oldStatus }
}
Dataset scoping for MODIFY (WITH / USING / USING NAMED)
SPARQL UPDATE MODIFY supports dataset scoping for named graphs:
WITH <iri>: sets the default graph for INSERT/DELETE templates that don’t use an explicitGRAPH <iri> { ... }block.USING <iri>: scopes the default graph(s) forWHEREevaluation. RepeatedUSINGclauses are evaluated as a merged default graph.USING NAMED <iri>: scopes which named graphs are visible toWHEREGRAPH <iri> { ... }patterns. RepeatedUSING NAMEDclauses allow multiple named graphs.
Blank Nodes in INSERT
Blank nodes can be used in INSERT templates to create new entities:
PREFIX ex: <http://example.org/ns/>
INSERT DATA {
_:newPerson ex:name "Bob" .
_:newPerson ex:age 25 .
}
Typed Literals
Specify datatypes explicitly:
PREFIX ex: <http://example.org/ns/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
INSERT DATA {
ex:alice ex:birthDate "1990-05-15"^^xsd:date .
ex:alice ex:salary "75000.00"^^xsd:decimal .
ex:alice ex:active "true"^^xsd:boolean .
}
Language-Tagged Strings
Insert strings with language tags:
PREFIX ex: <http://example.org/ns/>
INSERT DATA {
ex:alice ex:name "Alice"@en .
ex:alice ex:name "Alicia"@es .
ex:alice ex:name "アリス"@ja .
}
SPARQL UPDATE Restrictions
Current restrictions / boundaries:
- Graph management operations:
LOAD,CLEAR,DROP,CREATE,ADD,MOVE,COPYare not yet supported. - Template graph variables: INSERT/DELETE templates support
GRAPH <iri> { ... }blocks, butGRAPH ?g { ... }is not yet supported. - DELETE WHERE + GRAPH blocks:
GRAPH <iri> { ... }blocks are not yet supported insideDELETE WHERE { ... }. - SERVICE: Only local-ledger endpoints of the form
fluree:ledger:<name>[:<branch>]are supported; arbitrary remote HTTPSERVICEendpoints are not supported. - Property paths: Supported in
WHERE(subject to Fluree capability settings).
Endpoint Usage
SPARQL UPDATE uses the update endpoints with Content-Type: application/sparql-update:
| Endpoint | Description |
|---|---|
POST /v1/fluree/update | Connection-scoped, requires Fluree-Ledger header |
POST /v1/fluree/update/<ledger...> | Ledger-scoped, ledger from URL path |
Examples:
# Ledger-scoped (recommended)
curl -X POST http://localhost:8090/v1/fluree/update/mydb:main \
-H "Content-Type: application/sparql-update" \
-d 'PREFIX ex: <http://example.org/ns/>
INSERT DATA { ex:alice ex:name "Alice" }'
# Connection-scoped with header
curl -X POST http://localhost:8090/v1/fluree/update \
-H "Content-Type: application/sparql-update" \
-H "Fluree-Ledger: mydb:main" \
-d 'PREFIX ex: <http://example.org/ns/>
INSERT DATA { ex:alice ex:name "Alice" }'
Best Practices
- Use PREFIX Declarations: Makes queries readable
- Automatic Pattern Optimization: The query planner automatically reorders patterns for efficient execution using statistics-driven cardinality estimates
- Flexible FILTER Placement: Filters can be placed anywhere in the WHERE clause — the query engine automatically applies each filter as soon as all its required variables are bound
- Limit Results: Use LIMIT for large result sets
- Avoid Cartesian Products: Structure queries to avoid large joins
Related Documentation
- JSON-LD Query: Fluree's native query language
- CONSTRUCT Queries: Generating RDF graphs
- Datasets: Multi-graph queries
- Output Formats: Query result formats
- Transactions: JSON-LD transaction format