{"id":26107082,"url":"https://github.com/duriantaco/jonq","last_synced_at":"2026-04-01T23:03:51.469Z","repository":{"id":281419956,"uuid":"945239995","full_name":"duriantaco/jonq","owner":"duriantaco","description":"Query JSON with SQL-like syntax. A readable jq alternative that generates pure jq under the hood. Table, CSV, YAML output. Interactive REPL. Pipes from curl, streams NDJSON logs.","archived":false,"fork":false,"pushed_at":"2026-03-22T07:49:19.000Z","size":2698,"stargazers_count":42,"open_issues_count":2,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-03-22T22:35:11.874Z","etag":null,"topics":["cli","command-line-tools","csv","data-extraction","jq","jq-alternative","json","json-parser","json-processor","json-query","log-analysis","ndjson","python","sql","yaml"],"latest_commit_sha":null,"homepage":"https://jonq.readthedocs.io/en/latest/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/duriantaco.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"License","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-03-09T01:05:19.000Z","updated_at":"2026-03-22T07:49:22.000Z","dependencies_parsed_at":null,"dependency_job_id":"787d96b1-cd3f-46de-a354-c3140a0e4c3a","html_url":"https://github.com/duriantaco/jonq","commit_stats":null,"previous_names":["duriantaco/jonq"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/duriantaco/jonq","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/duriantaco%2Fjonq","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/duriantaco%2Fjonq/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/duriantaco%2Fjonq/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/duriantaco%2Fjonq/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/duriantaco","download_url":"https://codeload.github.com/duriantaco/jonq/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/duriantaco%2Fjonq/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31292789,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T21:15:39.731Z","status":"ssl_error","status_checked_at":"2026-04-01T21:15:34.046Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","command-line-tools","csv","data-extraction","jq","jq-alternative","json","json-parser","json-processor","json-query","log-analysis","ndjson","python","sql","yaml"],"created_at":"2025-03-09T22:43:10.543Z","updated_at":"2026-04-01T23:03:51.424Z","avatar_url":"https://github.com/duriantaco.png","language":"Python","readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"docs/source/_static/jonq.png\" alt=\"jonq — SQL-like JSON query tool for the command line\" width=\"200\"/\u003e\n\n# jonq — query JSON with SQL-like syntax from the terminal\n\n### A readable alternative to jq for JSON extraction, filtering, and exploration\n\n[![PyPI version](https://img.shields.io/pypi/v/jonq.svg)](https://pypi.org/project/jonq/)\n[![Python Versions](https://img.shields.io/pypi/pyversions/jonq.svg)](https://pypi.org/project/jonq/)\n[![CI tests](https://github.com/duriantaco/jonq/actions/workflows/tests.yml/badge.svg)](https://github.com/duriantaco/jonq/actions)\n[![Documentation Status](https://readthedocs.org/projects/jonq/badge/?version=latest)](https://jonq.readthedocs.io)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Skylos Grade](https://img.shields.io/badge/Skylos-A%2B%20%28100%29-brightgreen)](https://github.com/duriantaco/skylos)\n\u003c/div\u003e\n\n---\n\n## What is jonq?\n\n**jonq** is a command-line JSON query tool that lets you `select`, `filter`, `group`, and `reshape` JSON data using SQL-like syntax instead of raw jq. It generates pure jq under the hood, so you get jq's speed with a syntax you can actually remember.\n\n```bash\n# Instead of: jq '.[] | select(.age \u003e 30) | {name, age}'\njonq data.json \"select name, age if age \u003e 30\" -t\n```\n\n```\nname    | age\n--------|----\nAlice   | 35\nCharlie | 42\n```\n\n\u003e **jonq is not a database.** It is a readable jq frontend for exploring, extracting, and reshaping JSON. If you need joins, window functions, or large-scale analytics, shape the JSON with jonq first and then hand it to DuckDB, Polars, or Pandas.\n\n---\n\n### Use jonq when you need to\n- Query JSON from APIs, config files, or log streams in the terminal\n- Explore unfamiliar JSON with the built-in path explorer\n- Write readable jq one-liners in shell scripts and CI pipelines\n- Filter, aggregate, or reshape nested JSON without memorizing jq syntax\n- Stream and filter NDJSON log output in real time\n\n### Use something else when you need\n- **Tabular analytics** — DuckDB, Polars, Pandas\n- **Joins across files** — a database or dataframe engine\n- **Large-scale ETL** — tools built for analytical pipelines\n\n**Rule of thumb:** if the problem is still \"I need to understand this JSON\", jonq is a good fit. If the problem has become relational analytics, move to a database.\n\n## Features at a glance\n\n| Category          | What you can do | Example |\n|-------------------|-----------------|---------|\n| **Selection**     | Pick fields     | `select name, age` |\n| **Wildcard**      | All fields      | `select *` |\n| **DISTINCT**      | Unique results  | `select distinct city` |\n| **Filtering**     | `and / or / not / between / contains / in / like` | `if age \u003e 30 and city = 'NY'` |\n| **IS NULL**       | Null checks     | `if email is not null` |\n| **Aggregations**  | `sum avg min max count` | `select avg(price) as avg_price` |\n| **COUNT DISTINCT**| Unique counts   | `select count(distinct city) as n` |\n| **Grouping**      | `group by` + `having`   | `... group by city having count \u003e 2` |\n| **Ordering**      | `sort \u003cfield\u003e [asc\\|desc]` | `sort age desc` |\n| **LIMIT**         | Standalone limit | `select * limit 10` |\n| **CASE/WHEN**     | Conditional expressions | `case when age \u003e 30 then 'senior' else 'junior' end` |\n| **COALESCE**      | Null fallback   | `coalesce(nickname, name) as display` |\n| **String concat** | `+` or `\\|\\|`   | `first \\|\\| ' ' \\|\\| last as full_name` |\n| **Nested arrays** | `from [].orders` or inline paths | `select products[].name ...` |\n| **String funcs**  | `upper lower length trim` | `select upper(name) as name_upper` |\n| **Math funcs**    | `round abs ceil floor` | `select round(price) as price_r` |\n| **Type casting**  | `int float str type` | `select int(price) as price` |\n| **Date/time**     | `todate fromdate date` | `select todate(ts) as date` |\n| **Inline maths**  | Field expressions | `age + 10 as age_plus_10` |\n| **Table output**  | Aligned terminal tables | `--format table` or `-t` |\n| **YAML output**   | YAML rendering  | `--format yaml` |\n| **CSV / stream**  | `--format csv`, `--stream` | |\n| **Follow mode**   | Stream NDJSON line-by-line | `tail -f log \\| jonq --follow \"...\"` |\n| **Worker reuse**  | Reuse jq workers for repeated filters | `--watch`, `--stream`, Python loops |\n| **Path explorer** | Inspect nested JSON paths and types | `jonq data.json` (no query) |\n| **Interactive REPL** | Tab completion + history | `jonq -i data.json` |\n| **Watch mode**    | Re-run on file change | `jonq data.json \"select *\" --watch` |\n| **URL fetch**     | Query remote JSON | `jonq https://api.example.com/data \"select id\"` |\n| **Multi-file glob** | Query across files | `jonq 'logs/*.json' \"select *\"` |\n| **Auto stdin**    | Auto-detect piped input | `curl ... \\| jonq \"select id\"` |\n| **Auto NDJSON**   | Auto-detect line-delimited JSON | No flag needed |\n| **Shell completions** | Bash/Zsh/Fish completions | `jonq --completions bash` |\n| **Explain mode**  | Show query breakdown + jq filter | `--explain` |\n| **Timing**        | Execution timing | `--time` |\n| **Fuzzy suggest** | Typo correction for fields | Suggests similar field names |\n| **Colorized output** | Syntax-highlighted JSON in terminal | Auto when TTY |\n\n---\n\n## Why Jonq?\n\n### Jonq vs raw jq\n\n| Task | Raw **jq** filter | **jonq** one-liner |\n|------|------------------|--------------------|\n| Select specific fields | `jq '.[]\u0026#124;{name:.name,age:.age}'` | `jonq data.json \"select name, age\"` |\n| Filter rows | `jq '.[]\u0026#124;select(.age \u003e 30)\u0026#124;{name,age}'` | `... \"select name, age if age \u003e 30\"` |\n| Sort + limit | `jq 'sort_by(.age) \u0026#124; reverse \u0026#124; .[0:2]'` | `... \"select name, age sort age desc 2\"` |\n| Standalone limit | `jq '.[0:5]'` | `... \"select * limit 5\"` |\n| Distinct values | `jq '[.[].city] \u0026#124; unique'` | `... \"select distinct city\"` |\n| IN filter | `jq '.[] \u0026#124; select(.city==\"NY\" or .city==\"LA\")'` | `... \"select * if city in ('NY', 'LA')\"` |\n| NOT filter | `jq '.[] \u0026#124; select((.age \u003e 30) \u0026#124; not)'` | `... \"select * if not age \u003e 30\"` |\n| LIKE filter | `jq '.[] \u0026#124; select(.name \u0026#124; startswith(\"Al\"))'` | `... \"select * if name like 'Al%'\"` |\n| Uppercase | `jq '.[] \u0026#124; {name: (.name \u0026#124; ascii_upcase)}'` | `... \"select upper(name) as name\"` |\n| Count items | `jq 'map(select(.age\u003e25)) \u0026#124; length'` | `... \"select count(*) as over_25 if age \u003e 25\"` |\n| Count distinct | `jq '[.[].city] \u0026#124; unique \u0026#124; length'` | `... \"select count(distinct city) as n\"` |\n| Group \u0026 count | `jq 'group_by(.city) \u0026#124; map({city:.[0].city,count:length})'` | `... \"select city, count(*) as count group by city\"` |\n| Group \u0026 HAVING | `jq 'group_by(.city) \u0026#124; map(select(length\u003e2)) \u0026#124; ...'` | `... \"select city, count(*) group by city having count \u003e 2\"` |\n| Field expression | `jq '.[] \u0026#124; {name, age_plus: (.age + 10)}'` | `... \"select name, age + 10 as age_plus\"` |\n| CASE/WHEN | `jq '.[] \u0026#124; if .age\u003e30 then \"senior\" else \"junior\" end'` | `... \"select case when age \u003e 30 then 'senior' else 'junior' end as level\"` |\n| COALESCE | `jq '.[] \u0026#124; {d: (.nick // .name)}'` | `... \"select coalesce(nickname, name) as display\"` |\n| IS NULL | `jq '.[] \u0026#124; select(.email != null)'` | `... \"select * if email is not null\"` |\n| String concat | `jq '.[] \u0026#124; {f: (.first + \" \" + .last)}'` | `... \"select first \u0026#124;\u0026#124; ' ' \u0026#124;\u0026#124; last as full\"` |\n| Type cast | `jq '.[] \u0026#124; {p: (.price \u0026#124; tonumber)}'` | `... \"select float(price) as p\"` |\n| Date convert | `jq '.[] \u0026#124; {d: (.ts \u0026#124; todate)}'` | `... \"select todate(ts) as d\"` |\n\n**Take-away:** a single `jonq` string replaces many pipes and brackets while still producing pure jq under the hood.\n\n---\n\n### Where jonq fits\n\n- Use **jonq** when the source of truth is still raw JSON and you need to inspect fields, paths, filters, or nested values quickly.\n- Use **raw jq** when you already know the exact jq filter you want and do not need the friendlier syntax.\n- Use **DuckDB / Polars / Pandas** after the JSON has become a tabular analytics problem.\n\n**TL;DR:** jonq is the \"understand and shape this JSON\" step, not the database step.\n\n---\n\n## Installation\n\n**Supported Platforms**: Linux, macOS, and Windows with WSL.\n\n### Prerequisites\n\n- Python 3.9+\n- `jq` command line tool installed (https://stedolan.github.io/jq/download/)\n\n### Setup\n\n**From PyPI**\n```bash\npip install jonq\n```\n\n**From source**\n```bash\ngit clone https://github.com/duriantaco/jonq.git\ncd jonq \u0026\u0026 pip install -e .\n```\n\n### Quick Start\n\n```bash\n# Create a simple JSON file\necho '[{\"name\":\"Alice\",\"age\":30,\"city\":\"New York\"},{\"name\":\"Bob\",\"age\":25,\"city\":\"LA\"}]' \u003e data.json\n\n# Select fields\njonq data.json \"select name, age if age \u003e 25\"\n# Output: [{\"name\":\"Alice\",\"age\":30}]\n\n# Table output\njonq data.json \"select name, age, city\" -t\n\n# Pipe from stdin (no '-' needed)\ncurl -s https://api.example.com/data | jonq \"select id, name\" -t\n\n# Conditional expressions\njonq data.json \"select name, case when age \u003e 28 then 'senior' else 'junior' end as level\" -t\n\n# Null handling\njonq data.json \"select coalesce(nickname, name) as display\"\n\n# Type casting\njonq data.json \"select name, str(age) as age_str\"\n\n# String concatenation\njonq data.json \"select name || ' (' || city || ')' as label\"\n\n# YAML output\njonq data.json \"select name, age\" -f yaml\n\n# See what jq jonq generates\njonq data.json \"select name, age if age \u003e 25\" --explain\n```\n\n## Query Syntax\n\n```\nselect [distinct] \u003cfields\u003e [from \u003cpath\u003e] [if \u003ccondition\u003e] [group by \u003cfields\u003e [having \u003ccondition\u003e]] [sort \u003cfield\u003e [asc|desc]] [limit N]\n```\n\nWhere:\n* `distinct` - Optional, returns unique rows\n* `\u003cfields\u003e` - Comma-separated: fields, aliases, `CASE/WHEN`, `coalesce()`, functions, aggregations, expressions\n* `from \u003cpath\u003e` - Optional source path for nested data\n* `if \u003ccondition\u003e` - Optional filter (supports `=`, `!=`, `\u003e`, `\u003c`, `\u003e=`, `\u003c=`, `and`, `or`, `not`, `in`, `like`, `between`, `contains`, `is null`, `is not null`)\n* `group by \u003cfields\u003e` - Optional grouping by one or more fields\n* `having \u003ccondition\u003e` - Optional filter on grouped results\n* `sort \u003cfield\u003e [asc|desc]` - Optional ordering\n* `limit N` - Optional result count limit\n\n## Examples\n\nGiven this JSON (`simple.json`):\n\n```json\n[\n  { \"id\": 1, \"name\": \"Alice\",   \"age\": 30, \"city\": \"New York\"    },\n  { \"id\": 2, \"name\": \"Bob\",     \"age\": 25, \"city\": \"Los Angeles\" },\n  { \"id\": 3, \"name\": \"Charlie\", \"age\": 35, \"city\": \"Chicago\"     }\n]\n```\n\n### Selection\n```bash\njonq simple.json \"select *\"                    # all fields\njonq simple.json \"select name, age\"            # specific fields\njonq simple.json \"select name as full_name\"    # with alias\n```\n\n### DISTINCT\n```bash\njonq simple.json \"select distinct city\"\n# [{\"city\":\"Chicago\"},{\"city\":\"Los Angeles\"},{\"city\":\"New York\"}]\n```\n\n### Filtering\n```bash\njonq simple.json \"select name, age if age \u003e 30\"\njonq simple.json \"select name if age \u003e 25 and city = 'New York'\"\njonq simple.json \"select name if age \u003e 30 or city = 'Los Angeles'\"\njonq simple.json \"select name if age between 25 and 30\"\n```\n\n### IN Operator\n```bash\njonq simple.json \"select * if city in ('New York', 'Chicago')\"\n# [{\"id\":1,\"name\":\"Alice\",\"age\":30,\"city\":\"New York\"},{\"id\":3,\"name\":\"Charlie\",\"age\":35,\"city\":\"Chicago\"}]\n```\n\n### NOT Operator\n```bash\njonq simple.json \"select * if not age \u003e 30\"\n# [{\"id\":1,\"name\":\"Alice\",\"age\":30,\"city\":\"New York\"},{\"id\":2,\"name\":\"Bob\",\"age\":25,\"city\":\"Los Angeles\"}]\n```\n\n### LIKE Operator\n```bash\njonq simple.json \"select * if name like 'Al%'\"     # starts with \"Al\"\njonq simple.json \"select * if name like '%ice'\"     # ends with \"ice\"\njonq simple.json \"select * if name like '%li%'\"     # contains \"li\"\n```\n\n### Sorting and Limiting\n```bash\njonq simple.json \"select name, age sort age desc\"\njonq simple.json \"select name, age sort age desc 2\"   # sort + inline limit\njonq simple.json \"select * limit 2\"                    # standalone limit\n```\n\n### Aggregation\n```bash\njonq simple.json \"select sum(age) as total_age\"\njonq simple.json \"select avg(age) as average_age\"\njonq simple.json \"select count(*) as total\"\njonq simple.json \"select count(distinct city) as unique_cities\"\n```\n\n### GROUP BY and HAVING\n```bash\njonq simple.json \"select city, count(*) as cnt group by city\"\njonq simple.json \"select city, avg(age) as avg_age group by city\"\njonq simple.json \"select city, count(*) as cnt group by city having cnt \u003e 0\"\n```\n\n### String Functions\n```bash\njonq simple.json \"select upper(name) as name_upper\"\n# [{\"name_upper\":\"ALICE\"},{\"name_upper\":\"BOB\"},{\"name_upper\":\"CHARLIE\"}]\n\njonq simple.json \"select lower(city) as city_lower\"\njonq simple.json \"select length(name) as name_len\"\n```\n\n### Math Functions\n```bash\njonq simple.json \"select round(age) as rounded_age\"\njonq simple.json \"select abs(age) as abs_age\"\njonq simple.json \"select ceil(age) as ceil_age\"\njonq simple.json \"select floor(age) as floor_age\"\n```\n\n### Nested JSON\n\n```bash\n# nested field access\njonq nested.json \"select name, profile.address.city\"\n\n# from: select from nested arrays\njonq complex.json \"select name, type from products\"\n\n# boolean logic with nested fields\njonq nested.json \"select name if profile.address.city = 'New York' or orders[0].price \u003e 1000\"\n```\n\n### CASE/WHEN Expressions\n```bash\njonq simple.json \"select name, case when age \u003e 30 then 'senior' when age \u003e 25 then 'mid' else 'junior' end as level\"\n# [{\"name\":\"Alice\",\"level\":\"mid\"},{\"name\":\"Bob\",\"level\":\"junior\"},{\"name\":\"Charlie\",\"level\":\"senior\"}]\n```\n\n### COALESCE\n```bash\njonq data.json \"select coalesce(nickname, name) as display_name\"\n# Falls back to name when nickname is null\n\n# Works with nested functions\njonq data.json \"select coalesce(todate(timestamp), 'unknown') as date\"\n```\n\n### IS NULL / IS NOT NULL\n```bash\njonq data.json \"select name if email is not null\"\njonq data.json \"select name if nickname is null\"\n```\n\n### String Concatenation\n```bash\n# Using || (SQL standard)\njonq simple.json \"select name || ' from ' || city as label\"\n\n# Using + (also works)\njonq simple.json \"select name + ' from ' + city as label\"\n```\n\n### Type Casting\n```bash\njonq data.json \"select int(price) as price\"        # string → integer\njonq data.json \"select float(amount) as amount\"     # string → float\njonq data.json \"select str(code) as code\"           # number → string\njonq data.json \"select type(value) as t\"            # get type name\n```\n\n### Date/Time Functions\n```bash\njonq data.json \"select todate(timestamp) as date\"   # epoch → ISO date\njonq data.json \"select date(created_at) as d\"       # alias for todate\n```\n\n### Arithmetic Expressions\n```bash\njonq simple.json \"select name, age + 10 as age_plus_10\"\n```\n\n## Output Formats\n\n### Table Output\n```bash\njonq simple.json \"select name, age, city\" -t\n# name    | age | city\n# --------|-----|-------------\n#  Alice   | 30  | New York\n#  Bob     | 25  | Los Angeles\n#  Charlie | 35  | Chicago\n```\n\n### CSV Output\n```bash\njonq simple.json \"select name, age\" --format csv \u003e output.csv\n```\n\n### YAML Output\n```bash\njonq simple.json \"select name, age\" --format yaml\n# - name: Alice\n#   age: 30\n# - name: Bob\n#   age: 25\n```\n\n### Python API\n\n```python\nfrom jonq import compile_query, query\n\ndata = [\n    {\"name\": \"Alice\", \"age\": 30, \"city\": \"New York\"},\n    {\"name\": \"Bob\", \"age\": 25, \"city\": \"LA\"},\n]\n\ncompiled = compile_query(\"select name, city if age \u003e 26\")\nresult = query(data, compiled)\nprint(result)\n```\n\nOutput:\n\n```python\n[{\"name\": \"Alice\", \"city\": \"New York\"}]\n```\n\nIf you want metadata such as the generated jq filter, use `execute(...)` instead of `query(...)`.\n\nRepeated identical filters reuse a live jq worker in long-lived Python processes, which reduces jq startup overhead in loops and services.\n\n## Streaming Mode\n\nFor processing large root-array JSON files more efficiently:\n\n```bash\njonq large.json \"select name, age\" --stream\n```\n\nChunk execution stays in memory and reuses the same jq worker for the generated filter instead of writing chunk temp files and starting a fresh jq subprocess for every chunk.\n\n## Path Explorer\n\nRun `jonq` with just a file (no query) to inspect nested JSON paths before writing a query:\n\n```bash\njonq data.json\n```\n\nOutput:\n```\ndata.json  (array, sampled 3 items)\n\nPaths:\n  id             int               1\n  name           str               \"Alice\"\n  age            int               30\n  city           str               \"New York\"\n  orders[]       array[object]\n  orders[].id    int               1\n  orders[].item  str               \"Laptop\"\n\nSample:\n  { \"id\": 1, \"name\": \"Alice\", \"age\": 30, \"city\": \"New York\", \"orders\": [{ \"id\": 1, \"item\": \"Laptop\" }] }\n\nTip: jonq data.json \"select name, orders[].item\"\n```\n\n## Interactive REPL\n\nLaunch an interactive session with tab completion and persistent history:\n\n```bash\njonq -i data.json\n```\n\n```\njonq interactive mode — querying data.json\nType a query, or 'quit' to exit. Tab completes field names.\nExample: select name, age if age \u003e 30\n\njonq\u003e select name, age\n[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]\njonq\u003e select * if age \u003e 28\n[{\"id\":1,\"name\":\"Alice\",\"age\":30,\"city\":\"New York\"}]\njonq\u003e quit\n```\n\nFeatures:\n- **Tab completion** for field names and SQL keywords\n- **Persistent history** saved to `~/.jonq_history`\n- **Up/down arrow** to recall previous queries\n\n## Watch Mode\n\nRe-run a query automatically whenever the file changes:\n\n```bash\njonq data.json \"select name, age\" --watch\n```\n\nBecause watch mode reruns the same filter repeatedly inside one loop, jonq reuses a live jq worker to reduce refresh overhead.\n\n## Multiple Input Sources\n\n### URL Fetch\n```bash\njonq https://api.example.com/users.json \"select name, email\"\n```\n\n### Multi-File Glob\n```bash\njonq 'logs/*.json' \"select * if level = 'error'\"\n```\n\n### Stdin\n```bash\n# Auto-detected — no '-' needed\ncurl -s https://api.example.com/data | jonq \"select id, name\"\n\n# Explicit stdin still works\ncat data.json | jonq - \"select name, age\"\n```\n\n## Follow Mode\n\nStream NDJSON from stdin line-by-line, applying the query to each object as it arrives:\n\n```bash\ntail -f app.log | jonq --follow \"select level, message if level = 'error'\" -t\n```\n\nOnly matching lines are printed. Non-matching lines are silently skipped.\n\n## Auto-detect NDJSON\n\njonq auto-detects NDJSON (newline-delimited JSON) files. No flag needed:\n\n```bash\njonq data.ndjson \"select name, age if age \u003e 25\"\n```\n\nYou can still force it with `--ndjson` if needed. `--ndjson` cannot be combined with `--stream`.\n\n## Fuzzy Field Suggestions\n\nWhen you mistype a field name, jonq suggests similar fields:\n\n```\n$ jonq data.json \"select nme, agge\"\nField(s) 'nme, agge' not found. Available fields: age, city, id, name. Did you mean: 'nme' -\u003e name; 'agge' -\u003e age?\n```\n\n## CLI Options\n\n| Option | Description |\n|--------|-------------|\n| `--format, -f` | Output format: `json` (default), `csv`, `table`, `yaml` |\n| `-t, --table` | Shorthand for `--format table` |\n| `--stream, -s` | Process root-array JSON in memory-friendly chunks |\n| `--ndjson` | Force NDJSON mode (auto-detected by default) |\n| `--follow` | Stream NDJSON from stdin line-by-line |\n| `--limit, -n N` | Limit rows post-query |\n| `--out, -o PATH` | Write output to file |\n| `--jq` | Print generated jq filter and exit |\n| `--explain` | Show parsed query breakdown and generated jq filter |\n| `--time` | Print execution timing to stderr |\n| `--pretty, -p` | Pretty-print JSON output |\n| `--watch, -w` | Re-run query when file changes |\n| `--no-color` | Disable colorized output |\n| `--completions SHELL` | Print shell completion script (`bash`, `zsh`, `fish`) |\n| `--version` | Show the installed jonq version |\n| `-i \u003cfile\u003e` | Interactive query mode (REPL) with tab completion |\n| `-h, --help` | Show help message |\n\n## Shell Completions\n\nGenerate completion scripts for your shell:\n\n```bash\n# Bash\neval \"$(jonq --completions bash)\"\n\n# Zsh\neval \"$(jonq --completions zsh)\"\n\n# Fish\njonq --completions fish \u003e ~/.config/fish/completions/jonq.fish\n```\n\n## Colorized Output\n\nWhen outputting to a terminal, jonq auto-pretty-prints and colorizes JSON. Pipe to a file or use `--no-color` to disable.\n\n## Troubleshooting\n\n### Common Errors\n\n**Command 'jq' not found** - Make sure jq is installed and in your PATH. Install: https://stedolan.github.io/jq/download/\n\n**Invalid JSON in file** - Check your JSON file for syntax errors. Use a JSON validator.\n\n**Syntax error in query** - Verify your query follows the correct syntax. Check field names and quotes.\n\n**Runtime jq error** - Errors like `Cannot iterate over string` or `Cannot iterate over null` are surfaced immediately. Adjust the field path or inspect the input with `jonq data.json`.\n\n**No results returned** - Verify your condition isn't filtering out all records. Check field name casing.\n\n## Known Limitations\n\n* Performance: For very large JSON files (100MB+), processing may still be slow. `--stream` is more memory-friendly now, but jonq is still not an analytical engine.\n* Advanced jq Features: Some advanced jq features (recursive descent, custom filters) aren't exposed in the jonq syntax.\n* Custom Functions: User-defined functions aren't supported.\n* Joins: Cross-file joins are not supported — use a database for relational queries.\n* Window Functions: Not supported — use DuckDB or Polars for analytical queries.\n\n## Docs\n\nFull documentation: https://jonq.readthedocs.io/en/latest/\n\nSee also: [SYNTAX.md](SYNTAX.md) for the complete syntax reference.\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nThis project is licensed under the MIT License - see the LICENSE file for details.\n\n### Misc.\n\n- **jq**: This tool depends on the [jq command-line JSON processor](https://stedolan.github.io/jq/), which is licensed under the MIT License. jq is copyright (C) 2012 Stephen Dolan.\n\nThe jq tool itself is not included in this package - users need to install it separately.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fduriantaco%2Fjonq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fduriantaco%2Fjonq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fduriantaco%2Fjonq/lists"}