THE JSON QUERY LANGUAGE

Your data.
Your questions.
Beautifully simple.

The flexibility of JSON. The power of a query language. Explore, transform, and make sense of your data with JSONiq.

pip install jsoniq

Get started in Python with RumbleDB.

hello-data.jqJSONiq
01 / ASK A QUESTION
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
02 / GET YOUR ANSWER✓
"Geneva" "Zurich"
A little query. A lot of possibility.
ONE LANGUAGE. MANY SHAPES OF DATA.
JSONArraysSequencesTextNested objects

BUILT FOR THE WAY DATA REALLY LOOKS

Less plumbing.
More possibility.

From a single document to a data lake, JSONiq gives you expressive building blocks that work together.

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 heart

Small expressions. Big ideas.

Filter, join, group, order, and transform. Every language construct is an expression, so powerful queries compose naturally.

The power of composition

Room to grow

A flexible data model for JSON, text, arrays, and sequences. Query heterogeneous data across data lakes and NoSQL systems.

Beyond a single format
01

Decades of experience

Built on lessons from relational query systems and semi-structured data.

02

Open by design

Fully specified and publicly documented, with independent implementations and roots in W3C standards.

03

Stable and maintained

Maintained specifications and an active community offering support on Stack Overflow.

EXPRESS MORE. WRITE LESS.

Real questions.
Powerful answers.

Explore grouping, joins, nested documents, windows, updates, and scripting through self-contained examples.

Core JSONiq ↗

Find the patterns in your data.

Group sales by region, aggregate each group, then rank the results by revenue. Non-grouping variables become sequences within each group.

aggregate.jq
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 }
Core JSONiq ↗

Bring separate stories together.

Join orders with customers using two for clauses and a matching condition. Group the joined records to build a ranked customer summary.

join.jq
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 }
Core JSONiq ↗

Follow the structure. Keep the meaning.

Navigate teams, projects, and tasks with array unboxing. Filter nested tasks and construct a fresh, flat report without changing the source document.

nested.jq
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" }
Core JSONiq ↗

Different shapes. One clear result.

A sequence can mix objects, strings, numbers, arrays, and null. A typeswitch handles each shape explicitly and normalizes the values you need.

heterogeneous.jq
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 }
JSONiq++ · Window clauses ↗

Turn a sequence into batches.

Build non-overlapping windows of three readings. The end condition closes each batch; the final incomplete batch is retained.

tumbling.jq
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 }
JSONiq++ · Window clauses ↗

Keep a moving view of the data.

Start a window at every reading to calculate a moving average. “Only end” keeps complete three-item windows and drops incomplete trailing ones.

sliding.jq
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 }
JSONiq Updates ↗

Give your documents a new chapter.

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.

updates.jq
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
}
JSONiq Scripting ↗

Add control when you need it.

Combine typed mutable variables, assignment, and a while loop with JSON construction. This script accumulates squared values and returns a summary.

scripting.jq
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

Make your first
connection.

Use JSONiq right from Python with RumbleDB. Create a session, write an expression, and get JSON back.

Open the live Jupyter tutorial
A little Python. A little JSONiq.
from jsoniq import RumbleSession

rumble = RumbleSession.builder.getOrCreate()
print(rumble.jsoniq('{ "foo": [ 6*7 ] }').json())
RESULT{ "foo": [42] }

KEEP EXPLORING

Your next great query.

A few good places to begin.

Good questions deserve good answers. Join us on Stack Overflow