Jaq, um clone do jq com foco em precisão, velocidade e simplicidade
(github.com/01mf02)- jaq é um clone da ferramenta de processamento de dados JSON
jq, com o objetivo de oferecer uma implementação mais previsível mantendo compatibilidade com ojqna maioria dos casos - O programa de linha de comando
jaqpode ser usado como um substituto drop-in dojq, e a biblioteca Rustjaq-corepermite compilar e executar programas jq dentro de programas Rust - Oferece suporte a YAML, CBOR, TOML e XML, que não existem no
jq; ojaq-corepode ser usado com segurança em ambientes multithread e dá suporte a tipos de dados arbitrários além de JSON - Em avaliações de desempenho, o
jaq-3.0foi o mais rápido em 20 de 31 benchmarks; ojq-1.8.1foi o mais rápido em 5, e ogojq-0.12.18em 6 - Em termos de segurança, busca garantir ausência de pânico, segurança de memória e limites de I/O para dados de entrada e filtros jq, mas não lida com exaustão de recursos como tempo, memória e pilha
O que o jaq oferece
- jaq é um clone da ferramenta de processamento de dados JSON
jq, pronunciado/ʒaːk/, como Jacques - Tem suporte a formatos de dados que não existem no
jq-
YAML
-
CBOR
-
TOML
- XML
- Há um manual separado, e é possível testá-lo no playground
- O jaq é oferecido em duas formas
- Programa de linha de comando
jaq: pode ser usado como substituto drop-in dojq - Biblioteca
jaq-core: permite compilar e executar programas jq dentro de programas Rust
-
Objetivos de design
-
Precisão
- O jaq busca ser uma implementação de jq mais correta e previsível, mantendo compatibilidade com o
jqna maioria dos casos
- O jaq busca ser uma implementação de jq mais correta e previsível, mantendo compatibilidade com o
-
Desempenho
- O jaq foi criado originalmente por causa do incômodo com o longo tempo de inicialização do
jq 1.6, que naquele ambiente era de cerca de 50 ms - Esse tempo de inicialização fica especialmente evidente ao processar muitos arquivos pequenos
- No
jq 1.7, o tempo de inicialização melhorou bastante, mas o jaq ainda é mais rápido que ojqem vários benchmarks
- O jaq foi criado originalmente por causa do incômodo com o longo tempo de inicialização do
-
Simplicidade
- O jaq busca uma implementação simples e pequena para reduzir a possibilidade de bugs e facilitar contribuições
Instalação e build
- Binários para Linux, Mac e Windows podem ser obtidos na página de releases
- No macOS ou Linux, é possível instalar via homebrew
brew install jaqbrew install --HEAD jaq
- Para compilar a partir do código-fonte, é necessário ter a toolchain do Rust
cargo install --locked jaqcargo install --locked --git https://github.com/01mf02/jaq
- Se você clonou o repositório, é possível compilar ou instalar com
cargo build --releaseoucargo install --locked --path jaq - O jaq deve funcionar em todos os sistemas suportados pelo Rust; caso contrário, o projeto orienta a abrir uma issue
Avaliação de desempenho
- A avaliação de desempenho consiste em vários benchmarks comparando jaq, jq e gojq
- O benchmark
emptymede o tempo de inicialização executando o filtroemptynvezes com entrada null - O benchmark
bf-fibexecuta um script Brainfuck que gera números de Fibonacci usando um interpretador Brainfuck escrito em jq - Os dados de benchmark foram gerados em um sistema Linux com AMD Ryzen 5 5500U
- O comando usado foi
bench.sh target/release/jaq jq-1.8.1 gojq-0.12.17 | tee bench.json - A tabela mostra os resultados de
jaq-3.0,jq-1.8.1egojq-0.12.18em milissegundos N/Asignifica erro ou mais de 10 segundos
- O comando usado foi
- Resumo dos resultados
jaq-3.0foi o mais rápido em 20 benchmarksjq-1.8.1foi o mais rápido em 5 benchmarksgojq-0.12.18foi o mais rápido em 6 benchmarks
- O
gojqé muito mais rápido emtree-flattenporque implementa o filtroflattennativamente, em vez de por definição
Modelo de segurança e limitações
- O jaq busca garantir o seguinte
- Não ocorre pânico, exceto em casos de exaustão de recursos
- Segurança de memória, sem corromper memória
- Exceto pela leitura de arquivos antes da execução do filtro jq, os dados de entrada e o filtro jq não podem iniciar operações de I/O
- Casos em que essas garantias são quebradas são considerados bugs e devem ser reportados
- O jaq não lida com nenhum tipo de exaustão de recursos
- O tempo de execução pode se prolongar indefinidamente
- O uso de memória pode crescer sem limite
- O uso de espaço de pilha pode crescer sem limite
- Por exemplo, pode ocorrer estouro de pilha ao ler dados de entrada ou executar um filtro jq
jaq -nr 'repeat("[")' | jaqjaq -n 'def f: 1+f; f'
Auditorias e testes
- O jaq core foi auditado pela Radically Open Security como parte de dois grants da NLnet
- Na primeira e na segunda auditorias de segurança, foram encontrados problemas de severidade média ou baixa
- Todas as questões das auditorias de segurança foram tratadas, e vários alvos de fuzzing para o jaq foram adicionados em
jaq-core/fuzz - O parser JSON do jaq, hifijson, também já tinha alvos de fuzzing
- O jaq tem uma suíte de testes com mais de 500 testes
Casos de uso
- Um usuário avaliou que o
jaqajudou muito mais do que implementar suporte aojqdiretamente, e que a extensibilidade via traitValTpermitiu adicionar facilmente suporte a jq para seus próprios tipos - Outro usuário afirmou que um programa Rust usando jaq conseguia executar todas as consultas três vezes sobre o arquivo inteiro enquanto o crate PyPI
jqdo Python e um loop em Python executavam uma consulta sobre o arquivo inteiro - No caso do interpretador
wsjq, houve a avaliação de que o jaq era consideravelmente mais rápido que outras implementações de jq, e que a ênfase na precisão era impressionante- Nesse benchmark do
wsjq, o jaq é 5 a 10 vezes mais rápido que o jq e 15 a 196 vezes mais rápido que o gojq
- Nesse benchmark do
- Um usuário que processava dados de certificate transparency logs com
certstream-servertinha problemas no processamento por pipes comjq; depois de trocar para jaq, conseguiu acompanhar o fluxo até em uma VM de baixo desempenho, graças ao tempo de inicialização mais rápido
Financiamento
- O projeto jaq recebeu apoio por meio dos fundos NGI0 Entrust e NGI0 Commons, criados pela NLnet
- O apoio financeiro é fornecido pelo programa Next Generation Internet da European Commission
- Financiamento adicional é fornecido pela Swiss State Secretariat for Education, Research and Innovation
1 comentários
Comentários do Hacker News
Considerando que o desenvolvimento do jq ficou parado por 5 anos e só voltou recentemente, não é estranho que relatórios tenham se acumulado nesse meio-tempo, sejam bugs conhecidos ou novos bugs
Agora parece que ele vai recuperar o ritmo e ir limpando aos poucos a lista de pendências acumulada há muito tempo
Gosto da forma como o README também apresenta projetos semelhantes ou inspirados, que não são substitutos
Conheci https://github.com/yamafaktory/jql pelo README deste projeto, e agradeço porque era uma ferramenta que eu procurava havia muito tempo
Não quero diminuir o JAQ, mas a sintaxe no estilo JQ é difícil demais de entender, então o jql combina melhor comigo
Ele achata JSON em linhas no formato chave-valor, fazendo com que combine bem com operações simples de stream como
grep: https://github.com/tomnomnom/gronMas eu esperava uma experiência realmente parecida com SQL. Não entendo por que não simplesmente copiar SQL e permitir consultas como
"SELECT * FROM $json WHERE x>1"Parece que todo mundo quer criar sua própria linguagem de consulta simbólica e obscura, como se fosse code golf. Eu gostaria que saíssemos da sintaxe antiga de Unix, extremamente curta mas nada óbvia, e nos aproximássemos mais da abordagem do PowerShell
|={"b""d"=2, "c"}parece significar algo comoselect(."b"."d" == 2 or ."c" != null)no jq, e o lado do jq me parece mais claro, embora seja mais longoNa prática, provavelmente seria necessário
.[] | select(...), mas o jql também pode ter uma premissa parecida; não tenho certeza se o exemplo está completo, então isso não afeta muito a conclusãoTambém parece possível aplicá-lo a si mesmo ou usar “macros”
Gosto das ideias do jq, mas como não uso com frequência, toda vez que quero fazer algo preciso procurar a sintaxe no manual
Infelizmente, 99% do que faço com jq é
| jq .Separadamente, comecei a criar uma linguagem de configuração e descobri que ela também era bastante boa para consultas em JSON: https://docs.ruuda.nl/rcl/rcl_query/
Aqui há um exemplo que não consegui resolver com jq, mas consegui com RCL: https://fosstodon.org/@ruuda/111120049523534027
Se você fornecer a tarefa necessária junto com uma amostra reduzida do JSON original, ele cria o script jq correto
Para requisitos complexos, é mais fácil e confiável iterar aos poucos com o Copilot e conduzi-lo até a solução, em vez de tentar explicar tudo com precisão de uma vez. Durante a iteração, às vezes surgem ideias melhores do que as iniciais
Imagino que o ChatGPT ou outras ferramentas funcionem de forma parecida
https://github.com/01mf02/jaq/blob/main/Cargo.lock
Tem bastante dependência
Com muitas dependências, parece que o risco de elas se tornarem fundamentalmente incompatíveis entre si aumenta com o tempo, e a manutenção deve virar um trabalho grande
Por exemplo, fico pensando se ainda vai compilar bem daqui a 2 anos
jq é uma ferramenta muito poderosa, mas hoje em dia o DuckDB também é bastante usado
Se os dados tiverem um formato minimamente tabular, SQL é uma linguagem muito mais natural
É um pouco parecido com LINQ em C#, mas gosto mais de SQL por ser mais padronizado
Seria ótimo poder consultar coleções primitivas dentro da linguagem usando SQL; melhor ainda seria poder armazenar essas coleções de forma transparente no Sqlite
Sempre acho uma pena ver código que busca dados em um banco de dados ou similar e depois faz processamento simples com loops ou APIs de stream. Para esse tipo de uso, SQL é muito mais alto nível e conciso do que Java/Kotlin/Python/JavaScript
Salvo toda a saída JSON original em uma tabela sqlite, crio colunas virtuais ali e então passo o resultado do
selectpor um loop de shellOs loops aninhados desaparecem, e a possibilidade de depuração melhora muito porque dá para verificar o registro exato no DB e reexecutar
Percebi que o que estou construindo é um DAG e sempre recomeço a partir do último registro processado com sucesso. Fico me perguntando se existe alguma ferramenta parecida com
Makepara expressar issoO Make não tem alvo SQL, e processadores completos de DAG, como o Airflow, são pesados demais para juntar trechos de shell
Mesmo assim, ainda é difícil conseguir uma forma concisa de expressar consultas recursivas em SQL
https://github.com/dinedal/textql
Do ponto de vista de correção, fico curioso se ele consegue exibir números uint64 sem truncá-los
Essa é hoje a parte mais irritante do jq
Mas faço a correção de que a especificação mais recente, a RFC 8259, apenas define a forma textual dos números e não sua semântica
Na prática, a maioria das implementações trata JSON como um subconjunto de JavaScript, o que leva à suposição de que os números são ponto flutuante de 64 bits
Diz que ele preserva a precisão usando literais numéricos decimais, e que as operações de comparação respeitam a precisão, mas operações aritméticas podem truncar
Atualmente ele trunca para decimal64, o que é um pouco confuso, mas no próximo release isso deve ser corrigido para truncar para binary64(double), em linha com a recomendação da especificação JSON: https://github.com/jqlang/jq/pull/2949
Depois que migrei para o jless, nunca mais olhei para trás
A interface de usuário está muito à frente das outras
jq não é um simples visualizador, é um processador de linguagem de consulta JSON
É fofo que exista em algum lugar do Rust uma biblioteca de arte em linhas para terminal, mas, quando rodei o jaq, ele despejou megabytes de códigos de escape no iTerm até que, no fim, o iTerm tentou imprimir aquilo em uma impressora
Foi esperto demais
Num contexto como
echo *json | rush -- jaq -rf ./this-program.jq {} | datamash ..., não acho apropriado tentar colocar efeitos artísticos no TTYA causa do erro, de qualquer forma, era que o
jaqnão temstrftimeMinha primeira impressão é que ele tem mensagens de erro bonitas, mas não tem
halt_error/0Depois de comentar o
halt_error, ele foi mais lento que jq e gojqCom a mesma entrada,
jqlevou cerca de 0,023 s,gojqcerca de 0,070 s, ejaqcerca de 0,103 sO
aoc22-13.jqusado está em https://pastebin.com/raw/YiUjEu2n, e oinput.txtestá em https://pastebin.com/raw/X0FSyTNfComecei a usar yq em vez de jq e fico curioso se há alguma diferença importante
Pessoalmente, prefiro https://github.com/mikefarah/yq a https://github.com/kislyuk/yq
Entendo que processar YAML é muito mais difícil do que JSON, mas, embora o yq tenha mudado a sintaxe da versão 3 para a 4 para ficar mais próxima do jq, de algum modo ela ainda não é exatamente igual
Além disso, yq não tem if-then-else, o que parece um desenho ruim ou uma omissão: https://github.com/mikefarah/yq/issues/95
Quando é preciso processar YAML, yq funciona bem e também lida razoavelmente bem com comentários, mas, para JSON puro, jq é a ferramenta melhor