Jaq, un clon de jq enfocado en la precisión, la velocidad y la simplicidad
(github.com/01mf02)- jaq es un clon de la herramienta de procesamiento de datos JSON
jqque busca una implementación más precisa y predecible, manteniendo compatibilidad conjqen la mayoría de los casos - El programa de línea de comandos
jaqpuede usarse como un reemplazo directo dejq, y la biblioteca de Rustjaq-corepermite compilar y ejecutar programas jq dentro de programas Rust - Ofrece soporte para YAML, CBOR, TOML y XML, formatos que
jqno incluye; además,jaq-corepuede usarse de forma segura en entornos multihilo y soporta tipos de datos arbitrarios más allá de JSON - En la evaluación de rendimiento,
jaq-3.0fue el más rápido en 20 de 31 benchmarks,jq-1.8.1en 5 ygojq-0.12.18en 6 - En seguridad, busca garantizar ausencia de pánicos, seguridad de memoria y límites de I/O para los datos de entrada y los filtros jq, pero no aborda el agotamiento de recursos como tiempo, memoria o pila
Lo que ofrece jaq
- jaq es un clon de la herramienta de procesamiento de datos JSON
jq, y se pronuncia/ʒaːk/, igual que Jacques - Tiene soporte para formatos de datos que
jqno incluye-
YAML
-
CBOR
-
TOML
- XML
- Tiene un manual independiente, y puede probarse en el playground
- jaq se ofrece en dos formas
- Programa de línea de comandos
jaq: puede usarse como reemplazo directo dejq - Biblioteca
jaq-core: permite compilar y ejecutar programas jq dentro de programas Rust
-
Objetivos de diseño
-
Precisión
- jaq busca una implementación de jq más precisa y predecible, manteniendo compatibilidad con
jqen la mayoría de los casos
- jaq busca una implementación de jq más precisa y predecible, manteniendo compatibilidad con
-
Rendimiento
- jaq se creó originalmente porque el largo tiempo de arranque de
jq 1.6resultaba incómodo, y en ese entorno el arranque era de unos 50 ms - Ese tiempo de arranque se nota especialmente al procesar una gran cantidad de archivos pequeños
- En
jq 1.7el tiempo de arranque mejoró mucho, pero jaq sigue siendo más rápido quejqen varios benchmarks
- jaq se creó originalmente porque el largo tiempo de arranque de
-
Simplicidad
- jaq apunta a una implementación simple y pequeña para reducir la posibilidad de errores y facilitar las contribuciones
Instalación y compilación
- Los binarios para Linux, Mac y Windows pueden descargarse desde la releases page
- En macOS o Linux puede instalarse con homebrew
brew install jaqbrew install --HEAD jaq
- Para compilar desde el código fuente se necesita el toolchain de Rust
cargo install --locked jaqcargo install --locked --git https://github.com/01mf02/jaq
- Si se clonó el repositorio, puede compilarse o instalarse con
cargo build --releaseocargo install --locked --path jaq - jaq debería funcionar en cualquier sistema compatible con Rust; si no es así, se pide reportar un issue
Evaluación de rendimiento
- La evaluación de rendimiento consiste en varios benchmarks que comparan jaq, jq y gojq
- El benchmark
emptymide el tiempo de arranque ejecutando el filtroemptynveces con entrada null - El benchmark
bf-fibejecuta un script Brainfuck que genera números de Fibonacci usando un intérprete Brainfuck escrito en jq - Los datos de benchmark se generaron en un sistema Linux con AMD Ryzen 5 5500U
- El comando usado fue
bench.sh target/release/jaq jq-1.8.1 gojq-0.12.17 | tee bench.json - La tabla muestra resultados de
jaq-3.0,jq-1.8.1ygojq-0.12.18en milisegundos N/Asignifica error o más de 10 segundos
- El comando usado fue
- Resumen de resultados
jaq-3.0fue el más rápido en 20 benchmarksjq-1.8.1fue el más rápido en 5 benchmarksgojq-0.12.18fue el más rápido en 6 benchmarks
gojqes mucho más rápido entree-flattenporque implementa el filtroflattende forma nativa y no como definición
Modelo de seguridad y límites
- jaq busca garantizar lo siguiente
- No se producen pánicos, salvo en casos de agotamiento de recursos
- Seguridad de memoria sin corrupción de memoria
- Excepto por la lectura de archivos antes de ejecutar filtros jq, los datos de entrada y los filtros jq no pueden iniciar operaciones de I/O
- Si alguna de estas garantías se rompe, se considera un bug y debe reportarse
- jaq no aborda ningún tipo de agotamiento de recursos
- El tiempo de ejecución puede crecer sin límite
- Puede usar memoria sin límite
- Puede usar espacio de pila sin límite
- Como ejemplo, puede producirse un desbordamiento de pila al leer datos de entrada o ejecutar un filtro jq
jaq -nr 'repeat("[")' | jaqjaq -n 'def f: 1+f; f'
Auditorías y pruebas
- jaq core fue auditado por Radically Open Security como parte de dos grants de NLnet
- La primera y la segunda auditorías de seguridad encontraron problemas de severidad media o baja
- Todos los problemas detectados en las auditorías fueron resueltos, y se agregaron varios objetivos de fuzzing para jaq en
jaq-core/fuzz - El parser JSON de jaq, hifijson, ya contaba también con objetivos de fuzzing
- jaq tiene una suite de pruebas compuesta por más de 500 tests
Casos de uso de usuarios
- Un usuario comentó que
jaqfue de gran ayuda frente a implementar soporte dejqdirectamente, y que gracias a la extensibilidad mediante el traitValTpudo agregar fácilmente soporte de jq a sus propios tipos - Otro usuario dijo que un programa Rust usando jaq podía ejecutar tres veces todas las consultas sobre un archivo completo mientras el crate
jqde PyPI para Python y un bucle en Python ejecutaban una sola consulta sobre ese mismo archivo completo - En el caso del intérprete
wsjq, se destacó que jaq era considerablemente más rápido que otras implementaciones de jq y que su énfasis en la precisión resultaba impresionante- En ese benchmark de
wsjq, jaq fue entre 5 y 10 veces más rápido que jq, y entre 15 y 196 veces más rápido que gojq
- En ese benchmark de
- Un usuario que procesaba datos de certificate transparency log con
certstream-servertuvo problemas con el pipeline dejq, y al cambiar a jaq pudo seguir el ritmo incluso en una VM de bajos recursos gracias a su arranque más rápido
Financiamiento
- El proyecto jaq recibe apoyo a través de los fondos NGI0 Entrust y NGI0 Commons, creados por NLnet
- El financiamiento proviene del programa Next Generation Internet de la European Commission
- El financiamiento adicional proviene de la Swiss State Secretariat for Education, Research and Innovation
1 comentarios
Opiniones de Hacker News
Considerando que el desarrollo de jq estuvo detenido durante 5 años y recién hace poco volvió a activarse, no es raro que se hayan acumulado reportes durante ese tiempo, fueran bugs conocidos o nuevos
Ahora parece que va a recuperar ritmo y empezar a ordenar poco a poco la lista de pendientes acumulada desde hace tiempo
Me gusta la forma en que el README presenta juntos proyectos similares o inspirados en este, pero que no son reemplazos
Gracias al README de este proyecto conocí https://github.com/yamafaktory/jql, una herramienta que llevaba mucho tiempo buscando, así que se agradece
No quiero menospreciar a JAQ, pero la sintaxis estilo JQ es demasiado difícil de entender, así que jql me queda mejor
Aplana JSON en líneas con formato clave-valor, lo que lo hace encajar bien con operaciones simples de streaming como
grep: https://github.com/tomnomnom/gronAunque esperaba una experiencia realmente parecida a SQL. No entiendo por qué no simplemente copian SQL para poder consultar algo como
"SELECT * FROM $json WHERE x>1"Parece que todos quieren crear su propio lenguaje de consultas críptico basado en símbolos, como si estuvieran haciendo code golf. Ojalá nos alejemos de esa sintaxis antigua de Unix, extremadamente breve pero nada evidente, y nos acerquemos más al enfoque de PowerShell
|={"b""d"=2, "c"}parece significar algo comoselect(."b"."d" == 2 or ."c" != null)en jq, y aunque la versión de jq sea más larga, se siente más claraEn la práctica probablemente haría falta
.[] | select(...), pero jql podría tener una premisa similar; no estoy seguro de si el ejemplo está completo, así que eso no cambia mucho la conclusiónTambién parece posible aplicarlo sobre sí mismo o usar “macros”
Me gusta la idea de jq, pero como no lo uso seguido, cada vez que quiero hacer algo tengo que buscar la sintaxis en el manual
Lamentablemente, el 99% de lo que hago con jq es
| jq .Por otro lado, empecé a crear un lenguaje de configuración y resultó que también funcionaba bastante bien para consultas JSON: https://docs.ruuda.nl/rcl/rcl_query/
Aquí hay un ejemplo que no pude resolver con jq, pero sí con RCL: https://fosstodon.org/@ruuda/111120049523534027
Si le das la tarea necesaria junto con una muestra reducida del JSON original, genera el script jq correcto
Para requisitos complejos, es más fácil y confiable ir iterando poco a poco con Copilot y guiarlo hacia la solución, en vez de intentar explicarlo todo con precisión de una sola vez. Durante la iteración, a veces también se te ocurren ideas mejores que al principio
ChatGPT u otras herramientas probablemente funcionen de forma similar
https://github.com/01mf02/jaq/blob/main/Cargo.lock
Tiene bastantes dependencias
Si hay muchas dependencias, parece que con el tiempo aumenta el riesgo de que terminen siendo fundamentalmente incompatibles entre sí, y el mantenimiento podría volverse un gran trabajo
Por ejemplo, me pregunto si seguirá compilando dentro de 2 años
jq es una herramienta muy potente, pero últimamente también se usa mucho DuckDB
Si los datos tienen más o menos forma tabular, SQL es un lenguaje mucho más natural
Se parece un poco a LINQ de C#, pero SQL está más estandarizado, así que me gusta más
Sería genial poder consultar colecciones primitivas dentro del lenguaje con SQL y, más aún, poder almacenar colecciones de forma transparente en Sqlite
Cada vez que veo código que trae datos de una base de datos y luego hace procesamiento simple con loops o APIs de streams, me queda una sensación de oportunidad perdida. Para este tipo de uso, SQL es de mucho más alto nivel y más conciso que Java/Kotlin/Python/JavaScript
Guardo toda la salida JSON original en una tabla sqlite, creo columnas virtuales ahí y luego paso el resultado de
selectpor un loop de shellSe eliminan los loops anidados, y como puedo verificar en la DB el registro exacto y volver a ejecutar, la capacidad de depuración mejora muchísimo
Me di cuenta de que lo que estoy construyendo es un DAG, y siempre reinicio desde el último registro procesado correctamente. Me pregunto si existe alguna herramienta parecida a
Makepara expresar estoMake no tiene objetivos SQL, y los procesadores DAG completos como Airflow son demasiado pesados para pegar fragmentos de shell
Aun así, sigue siendo difícil encontrar una forma concisa de expresar consultas recursivas en SQL
https://github.com/dinedal/textql
Desde el punto de vista de la exactitud, me pregunto si puede mostrar números uint64 sin truncarlos
Es lo que más me molesta actualmente de jq
Aunque hago la corrección de que la especificación más reciente, RFC 8259, solo define el formato textual de los números y no su semántica
En la práctica, la mayoría de las implementaciones tratan JSON como un subconjunto de JavaScript, lo que lleva a asumir que los números son de punto flotante de 64 bits
Dice que usa literales numéricos decimales para preservar la precisión, y que las operaciones de comparación respetan la precisión, aunque las aritméticas pueden truncarse
Actualmente los trunca a decimal64, lo cual es un poco confuso; en la próxima versión se corregirá para truncarlos a binary64(double), en línea con la recomendación de la especificación JSON: https://github.com/jqlang/jq/pull/2949
Desde que me pasé a jless, no he mirado atrás
Su interfaz de usuario está muy por delante de las demás
jq no es un simple visor, sino un procesador de lenguaje de consultas JSON
Es simpático que exista en algún lugar de Rust una biblioteca de arte de líneas para terminal, pero al ejecutar jaq empezó a volcar megabytes de códigos de escape en iTerm hasta que finalmente iTerm intentó imprimirlos en una impresora
Se pasó de listo
En un contexto como
echo *json | rush -- jaq -rf ./this-program.jq {} | datamash ..., no creo que sea apropiado intentar meter efectos artísticos en la TTYLa causa del error, en cualquier caso, era que
jaqno teníastrftimeMi primera impresión es que tiene mensajes de error bonitos, pero no tiene
halt_error/0Después de comentar
halt_error, fue más lento que jq y gojqCon la misma entrada,
jqtardó unos 0.023 segundos,gojqunos 0.070 segundos yjaqunos 0.103 segundosEl
aoc22-13.jqusado está en https://pastebin.com/raw/YiUjEu2n yinput.txtestá en https://pastebin.com/raw/X0FSyTNfEmpecé a usar yq en lugar de jq, y me pregunto si hay alguna diferencia importante
Personalmente prefiero https://github.com/mikefarah/yq sobre https://github.com/kislyuk/yq
Entiendo que procesar YAML es mucho más difícil que JSON, pero aunque yq cambió su sintaxis de la versión 3 a la 4 para parecerse más a jq, por alguna razón no termina de ser igual
Además, yq no tiene if-then-else, lo que parece un mal diseño o una omisión: https://github.com/mikefarah/yq/issues/95
Cuando hay que procesar YAML, yq funciona bien y también maneja bastante bien los comentarios, pero para procesar JSON puro, jq es una mejor herramienta