Usage¶
Overview¶
jonq is a command-line tool for exploring, extracting, and reshaping JSON with readable jq-powered queries. It is designed for JSON-native terminal workflows: inspect a payload, select the fields you need, filter rows, reshape nested arrays, and hand the result to the next shell command or script.
Basic Command Structure¶
Run jonq with the following syntax:
jonq <path/to/json_file> "<query>" [options]
Available Options:
Option |
Description |
|---|---|
|
Output format: |
|
Shorthand for |
|
Print scalar values without JSON quoting |
|
Process row-wise root-array queries in memory-friendly chunks |
|
Force NDJSON mode (auto-detected by default) |
|
Stream NDJSON from stdin line-by-line |
|
Limit rows post-query |
|
Write output to file |
|
Print generated jq filter and exit |
|
Show parsed query breakdown and generated jq filter |
|
Print execution timing to stderr |
|
Pretty-print JSON output |
|
Re-run query when file changes |
|
Disable colorized output |
|
Print shell completion script ( |
|
Show the installed jonq version |
|
Interactive query mode (REPL) with tab completion |
|
Show help message |
Quick Example:
jonq users.json "select name, age if age > 30"
This command selects the name and age fields from users.json where the age is greater than 30.
Query Syntax Breakdown¶
The jonq query syntax mirrors SQL but is tailored for JSON. Here’s the full structure:
select [distinct] <fields> [from <path>] [if|where <condition>] [group by <fields> [having <condition>]] [sort <field> [asc|desc]] [limit N]
``<fields>``: Fields to select (e.g.,
name,age), including aliases, expressions, or aggregations.``distinct``: Optional keyword to return only unique rows.
``from <path>``: Optional path to query a specific part of the JSON (for example,
from productsorfrom [].orders).``if <condition>`` / ``where <condition>``: Optional filter (e.g.,
age > 30).``group by <fields>``: Optional grouping (e.g.,
group by city).``having <condition>``: Optional filter on grouped results (e.g.,
having count > 2).``sort <field>``: Optional sorting field (e.g.,
sort age).``asc|desc``: Optional sort direction (default:
asc).``limit N``: Optional number of results (e.g.,
limit 5).
Field Selection¶
You can select specific fields, all fields, or even compute new values from your JSON data.
Selecting Fields¶
All Fields (``*``):
jonq data.json "select *"
Retrieves all top-level fields in each JSON object.
Specific Fields:
jonq data.json "select name, age"
Returns only
nameandagefrom each object.Nested Fields (Using Dot Notation):
jonq data.json "select profile.age, profile.address.city"
Accesses nested fields like
ageinsideprofileandcityinsideaddress.Array Elements (Using Brackets):
jonq data.json "select orders[0].item"
Retrieves the
itemfrom the first element of theordersarray.Fields with Spaces or Special Characters (Quotes):
jonq data.json "select 'first name', \"last-name\""
Use single or double quotes for field names with spaces or special characters.
DISTINCT¶
Return only unique rows:
jonq data.json "select distinct city"
jonq data.json "select distinct name, city"
Aliases¶
Rename fields in the output using as:
jonq data.json "select name as full_name, age as years"
Output Example:
[
{"full_name": "Alice", "years": 30},
{"full_name": "Bob", "years": 25}
]
Arithmetic Expressions¶
Perform calculations within the select clause:
jonq data.json "select name, age + 10 as age_plus_10, price * 2 as doubled_price"
String Functions¶
Transform string values with built-in functions:
jonq data.json "select upper(name) as name_upper" # ALICE, BOB, ...
jonq data.json "select lower(city) as city_lower" # new york, ...
jonq data.json "select length(name) as name_len" # 5, 3, ...
Math Functions¶
Apply math functions to numeric values:
jonq data.json "select round(price) as price_rounded"
jonq data.json "select abs(balance) as abs_balance"
jonq data.json "select ceil(score) as score_ceil"
jonq data.json "select floor(score) as score_floor"
Type Casting¶
Convert between types:
jonq data.json "select int(price) as price" # string -> integer
jonq data.json "select float(amount) as amount" # string -> float
jonq data.json "select str(code) as code" # number -> string
jonq data.json "select type(value) as t" # get type name
Date/Time Functions¶
Convert between epoch timestamps and ISO dates:
jonq data.json "select todate(timestamp) as date" # epoch -> ISO date
jonq data.json "select date(created_at) as d" # alias for todate
jonq data.json "select fromdate(iso_date) as epoch" # ISO -> epoch
Null-safe: returns null instead of crashing on null input.
CASE/WHEN Expressions¶
Conditional logic inline in queries:
jonq data.json "select name, case when age > 30 then 'senior' when age > 25 then 'mid' else 'junior' end as level"
Must include at least one WHEN ... THEN ..., optional ELSE, and close with END.
COALESCE¶
Return the first non-null value:
jonq data.json "select coalesce(nickname, name) as display"
jonq data.json "select coalesce(todate(ts), 'unknown') as date"
String Concatenation¶
Use || (SQL standard) or +:
jonq data.json "select first || ' ' || last as full_name"
jonq data.json "select name + ' (' + city + ')' as label"
IS NULL / IS NOT NULL¶
Check for null values in conditions:
jonq data.json "select name if email is not null"
jonq data.json "select * if nickname is null"
FROM Clause¶
The FROM clause lets you target a specific part of the JSON structure, such as a nested array.
Basic Usage:
jonq catalog.json "select type, name from products"
Queries the
productsarray within the JSON.With Filtering:
jonq data.json "select order_id, price from [].orders if price > 800"
Filters
orderswherepriceexceeds 800.
Filtering with Conditions¶
The if clause filters data based on conditions using comparison and logical operators. where is accepted as an alias.
Basic Filtering¶
Comparison Operators:
=,==,!=,>,<,>=,<=jonq data.json "select name, age if age >= 30"
String Equality:
jonq data.json "select name if city = 'New York'"
Logical Operators¶
Combine conditions with and, or, and parentheses:
Multiple Conditions:
jonq data.json "select name if age > 25 and city = 'Chicago'"
With ``or``:
jonq data.json "select name if age > 30 or city = 'Los Angeles'"
Complex Logic:
jonq data.json "select name if (age > 30 and city = 'Chicago') or profile.active = true"
Advanced Operators¶
IN (Set membership):
jonq data.json "select * if city in ('New York', 'Chicago', 'Los Angeles')"
Matches values in the given set.
NOT (Logical negation):
jonq data.json "select * if not age > 30"
Negates the condition.
LIKE (Pattern matching):
jonq data.json "select * if name like 'Al%'" # starts with "Al" jonq data.json "select * if name like '%ice'" # ends with "ice" jonq data.json "select * if name like '%li%'" # contains "li"
Uses
%as wildcard for pattern matching.BETWEEN (Numeric Ranges):
jonq data.json "select item, price from [].orders if price between 700 and 1000"
Matches values inclusively between 700 and 1000.
CONTAINS (String Search):
jonq data.json "select item from [].orders if item contains 'book'"
Returns items with “book” in the string.
Sorting and Limiting¶
Control the order and number of results.
Sort Ascending:
jonq data.json "select name, age sort age"
Sort Descending:
jonq data.json "select name, age sort age desc"
Inline Limit (after sort):
jonq data.json "select name, age sort age desc 3"
Returns the top 3 results sorted by
agedescending.Standalone Limit:
jonq data.json "select * limit 5" jonq data.json "select name, age if age > 25 limit 10" jonq data.json "select city, count(*) as cnt group by city limit 3"
Can be used anywhere after the main query, independent of sorting.
Aggregation Functions¶
Summarize data with built-in functions: sum, avg, count, max, min.
Sum:
jonq data.json "select sum(age) as total_age"
Average:
jonq data.json "select avg(price) as average_price from [].orders"
Count:
jonq data.json "select count(*) as total_users"
Count Distinct:
jonq data.json "select count(distinct city) as unique_cities"
Maximum:
jonq data.json "select max(age) as oldest"
Minimum:
jonq data.json "select min(age) as youngest"
Combining Aggregations¶
jonq data.json "select sum(price) as total, avg(price) as avg_price from [].orders"
Grouping Data¶
Use group by to aggregate data by categories.
Simple Grouping:
jonq data.json "select city, count(*) as user_count group by city"
Multiple Fields:
jonq data.json "select city, country, avg(age) as avg_age group by city, country"
Having Clause¶
Filter grouped results with having:
Basic Example:
jonq data.json "select city, count(*) as cnt group by city having cnt > 2"
With Aggregation:
jonq data.json "select city, avg(age) as avg_age group by city having avg_age >= 30"
Complex Conditions:
jonq data.json "select city, count(*) as cnt, avg(age) as avg_age group by city having cnt > 1 and avg_age > 25"
Output Formats¶
Choose how results are displayed:
JSON (Default):
jonq data.json "select name, age"
Table:
jonq data.json "select name, age" -t jonq data.json "select name, age" --format table
Renders aligned columns with headers and separators.
CSV:
jonq data.json "select name, age" --format csv
JSONL:
jonq data.json "select name, age" --format jsonl
YAML:
jonq data.json "select name, age" --format yaml
Uses
pyyamlif installed, built-in fallback otherwise.Raw scalar values:
jonq data.json "select name" -r
Prints one selected value per line, without JSON string quotes. Multi-field rows remain compact JSON objects, one per line.
Smart Inspect¶
Run jonq with just a file (no query) to inspect the root shape, fields, sample values, and suggested queries before writing a query:
jonq data.json
This shows root type information, nested fields with inferred types, a truncated sample object, and copy-paste query suggestions based on the fields found in the data.
Interactive REPL¶
Launch an interactive session with tab completion and persistent history:
jonq -i data.json
jonq interactive mode - querying data.json
Type a query, or 'quit' to exit. Tab completes field names.
jonq> select name, age
[{"name":"Alice","age":30},{"name":"Bob","age":25}]
jonq> select * if age > 28
[{"id":1,"name":"Alice","age":30,"city":"New York"}]
jonq> quit
Features: tab completion for field names + keywords, persistent history (~/.jonq_history), up/down arrow recall.
Watch Mode¶
Re-run a query automatically whenever the file changes:
jonq data.json "select name, age" --watch
Multiple Input Sources¶
URL Fetch:
jonq https://api.example.com/users.json "select name, email"
Multi-File Glob:
jonq 'logs/*.json' "select * if level = 'error'"
Stdin (auto-detected):
curl -s https://api.example.com/data | jonq "select id, name"
cat data.json | jonq "select name, age where age > 25"
cat data.json | jonq - "select name, age"
Follow Mode¶
Stream NDJSON from stdin line-by-line, applying the query to each object:
tail -f app.log | jonq --follow "select level, message if level = 'error'" -t
Non-matching lines are silently skipped. Combine with -t for table output or -f yaml for YAML.
Shell Completions¶
Generate completion scripts for your shell:
# Bash
eval "$(jonq --completions bash)"
# Zsh
eval "$(jonq --completions zsh)"
# Fish
jonq --completions fish > ~/.config/fish/completions/jonq.fish
Auto-detect NDJSON¶
jonq auto-detects NDJSON (newline-delimited JSON) files. No flag needed:
jonq data.ndjson "select name, age if age > 25"
You can still force it with --ndjson if needed. --ndjson cannot be combined with --stream.
Query Repair Suggestions¶
When you mistype a field name, jonq suggests similar fields and a copy-paste repair command. This works across selected fields, filters, sorting, grouping, and aggregations for normal file inputs:
$ jonq users.json "select name where cty = 'Chicago'"
Error: Unknown field(s): cty
Did you mean: cty -> city?
Available fields: id, name, age, city
Try: jonq users.json "select name where city = 'Chicago'"
Colorized Output¶
When outputting to a terminal, jonq auto-pretty-prints and colorizes JSON output with syntax highlighting. Pipe to a file or use --no-color to disable.
Handling Large Files¶
For big JSON files, use streaming mode:
jonq large_data.json "select name, age" --stream
Requirement: The JSON must be an array at the root level.
Benefit: Processes data in chunks in memory, avoiding temp chunk files in the main execution path.
Scope: Streaming supports row-wise queries only. It rejects aggregations,
group by,sort,distinct, andlimitbecause those need the full input.
Tips and Tricks¶
Debugging Queries¶
Test Small: Start with a simple
select *to verify the JSON structure.Use Smart Inspect: Run
jonq data.json(no query) to inspect nested fields, types, samples, and suggested queries.See the jq filter: Use
--jqto see the generated jq filter, or--explainfor a full breakdown.Quote Strings: Always quote string literals in conditions (e.g.,
'New York').
Optimizing Performance¶
Use FROM: Narrow down the data with
fromto avoid processing unnecessary parts.Limit Early: Apply
limitor strictifconditions to reduce output size.Stream Row-wise Queries: Use
--streamfor large root-array JSON files when the query can be evaluated one row at a time.
Working with Arrays¶
Unpack Arrays: Use
from [].pathto query array elements directly.Index Safely: Check array lengths in your data to avoid out-of-bounds errors.
Handling Nulls¶
Filter Nulls: Use
if field is not nullorif field != nullto exclude missing values.Default Values: Use
coalesce(field, default)to provide fallback values.Null-safe functions: Date/time and type casting functions return
nullon null input instead of crashing.
Known Limitations¶
Performance with Very Large Files: Processing JSON files exceeding 100MB may still be slow, even with streaming.
Streaming Scope:
--streamis intentionally limited to row-wise queries; use normal mode for global operations.Advanced jq Features: Some complex
jqfunctionalities (e.g., recursive descent or custom filters) are not exposed throughjonq’s readable query syntax.Joins:
jonqdoes not support joining data across multiple JSON files.Custom Functions: Users cannot define custom functions within queries.
Window Functions: Not supported; use an analytical query engine for analytical queries.