Native to your data
Work naturally with objects, arrays, and deeply nested structures. Shape your data without flattening away what makes it useful.
JSON at its heartTHE JSON QUERY LANGUAGE
The flexibility of JSON. The power of a query language. Explore, transform, and make sense of your data with JSONiq.
pip install jsoniqGet started in Python with RumbleDB.
let $cities := (
{ "name": "Zurich", "country": "CH" },
{ "name": "Paris", "country": "FR" },
{ "name": "Geneva", "country": "CH" }
)
for $city in $cities
where $city.country = "CH"
order by $city.name
return $city.name
"Geneva" "Zurich"BUILT FOR THE WAY DATA REALLY LOOKS
From a single document to a data lake, JSONiq gives you expressive building blocks that work together.
Work naturally with objects, arrays, and deeply nested structures. Shape your data without flattening away what makes it useful.
JSON at its heartFilter, join, group, order, and transform. Every language construct is an expression, so powerful queries compose naturally.
The power of compositionA flexible data model for JSON, text, arrays, and sequences. Query heterogeneous data across data lakes and NoSQL systems.
Beyond a single formatBuilt on lessons from relational query systems and semi-structured data.
Fully specified and publicly documented, with independent implementations and roots in W3C standards.
Maintained specifications and an active community offering support on Stack Overflow.
EXPRESS MORE. WRITE LESS.
Explore grouping, joins, nested documents, windows, updates, and scripting through self-contained examples.
Group sales by region, aggregate each group, then rank the results by revenue. Non-grouping variables become sequences within each group.
let $sales := (
{ "region": "Europe", "amount": 120 },
{ "region": "Asia", "amount": 90 },
{ "region": "Europe", "amount": 80 },
{ "region": "Asia", "amount": 60 }
)
for $sale in $sales
group by $region := $sale.region
let $revenue := sum($sale.amount)
order by $revenue descending, $region ascending
return {
"region": $region,
"revenue": $revenue,
"orders": count($sale)
}
EXPECTED RESULT
{ "region": "Europe", "revenue": 200, "orders": 2 }
{ "region": "Asia", "revenue": 150, "orders": 2 }Join orders with customers using two for clauses and a matching condition. Group the joined records to build a ranked customer summary.
let $customers := (
{ "id": 1, "name": "Ada" },
{ "id": 2, "name": "Grace" }
)
let $orders := (
{ "customerId": 1, "total": 80 },
{ "customerId": 2, "total": 95 },
{ "customerId": 1, "total": 50 }
)
for $customer in $customers
for $order in $orders
where $order.customerId = $customer.id
group by $name := $customer.name
let $spent := sum($order.total)
order by $spent descending
return { "customer": $name, "spent": $spent }
EXPECTED RESULT
{ "customer": "Ada", "spent": 130 }
{ "customer": "Grace", "spent": 95 }Navigate teams, projects, and tasks with array unboxing. Filter nested tasks and construct a fresh, flat report without changing the source document.
let $company := {
"teams": [{
"name": "Research",
"projects": [{
"name": "Atlas",
"tasks": [
{ "title": "Prototype", "done": true },
{ "title": "Review", "done": false }
]
}]
}]
}
for $team in $company.teams[]
for $project in $team.projects[]
for $task in $project.tasks[]
where not($task.done)
order by $team.name, $project.name, $task.title
return {
"team": $team.name,
"project": $project.name,
"pending": $task.title
}
EXPECTED RESULT
{ "team": "Research", "project": "Atlas", "pending": "Review" }A sequence can mix objects, strings, numbers, arrays, and null. A typeswitch handles each shape explicitly and normalizes the values you need.
let $events := (
{ "kind": "purchase", "amount": 120 },
"heartbeat",
42,
{ "kind": "purchase", "amount": 80 },
[1, 2, 3],
null
)
let $amounts :=
for $event in $events
return typeswitch ($event)
case $object as object return
if ($object.kind = "purchase")
then $object.amount else ()
case $number as integer return $number
default return ()
return {
"total": sum($amounts),
"values": [ $amounts ],
"ignored": count($events) - count($amounts)
}
EXPECTED RESULT
{ "total": 242, "values": [120, 42, 80], "ignored": 3 }Build non-overlapping windows of three readings. The end condition closes each batch; the final incomplete batch is retained.
let $readings := (12, 15, 18, 10, 14, 18, 21)
for tumbling window $batch in $readings
start at $start when true
end at $end when $end - $start = 2
return {
"from": $start,
"to": $end,
"readings": [ $batch ],
"average": avg($batch)
}
EXPECTED RESULT
{ "from": 1, "to": 3, "readings": [12, 15, 18], "average": 15 }
{ "from": 4, "to": 6, "readings": [10, 14, 18], "average": 14 }
{ "from": 7, "to": 7, "readings": [21], "average": 21 }Start a window at every reading to calculate a moving average. “Only end” keeps complete three-item windows and drops incomplete trailing ones.
let $readings := (12, 15, 18, 21, 24)
for sliding window $window in $readings
start at $start when true
only end at $end when $end - $start = 2
return {
"from": $start,
"to": $end,
"readings": [ $window ],
"movingAverage": avg($window)
}
EXPECTED RESULT
{ "from": 1, "to": 3, "readings": [12, 15, 18], "movingAverage": 15 }
{ "from": 2, "to": 4, "readings": [15, 18, 21], "movingAverage": 18 }
{ "from": 3, "to": 5, "readings": [18, 21, 24], "movingAverage": 21 }Copy a document, apply several updates together, and return the transformed copy. Replace a field, append to an array, delete a key, and insert a new pair.
let $profile := {
"name": "Ada",
"status": "new",
"tags": ["reader"],
"temporary": true
}
return copy $updated := $profile
modify (
replace value of json $updated.status with "active",
append json "author" into $updated.tags,
delete json $updated.temporary,
insert json { "verified": true } into $updated
)
return $updated
EXPECTED RESULT
{
"name": "Ada",
"status": "active",
"tags": ["reader", "author"],
"verified": true
}Combine typed mutable variables, assignment, and a while loop with JSON construction. This script accumulates squared values and returns a summary.
variable $i as integer := 1;
variable $total as integer := 0;
variable $history as integer* := ();
while ($i <= 4) {
$total := $total + $i * $i;
$history := ($history, $total);
$i := $i + 1;
}
{
"iterations": $i - 1,
"sumOfSquares": $total,
"runningTotals": [ $history ]
}
EXPECTED RESULT
{ "iterations": 4, "sumOfSquares": 30, "runningTotals": [1, 5, 14, 30] }Explore the core language and its extensions. Each example links to its language reference; feature support depends on the implementation.
FROM CURIOUS TO QUERYING
Use JSONiq right from Python with RumbleDB. Create a session, write an expression, and get JSON back.
Open the live Jupyter tutorialfrom jsoniq import RumbleSession
rumble = RumbleSession.builder.getOrCreate()
print(rumble.jsoniq('{ "foo": [ 6*7 ] }').json()){ "foo": [42] }KEEP EXPLORING
A few good places to begin.
Learn the fundamentals with the beginner’s tutorial.
Explore the tutorial → UNDERSTANDExplore the language specification and its building blocks.
Read the specification → BUILDGet started with the recommended, fully supported implementation.
Discover RumbleDB →Good questions deserve good answers. Join us on Stack Overflow