17 pontos por GN⁺ 2026-03-28 | 3 comentários | Compartilhar no WhatsApp
  • Ferramenta CLI em Rust para buscar documentos JSON com base em caminhos, com velocidade de busca superior a jq, jmespath, jsonpath-rust e jql
  • Expressa consultas como linguagem regular e as compila para DFA, percorrendo a árvore JSON em uma única passagem e processando em tempo O(n)
  • Usa serde_json_borrow, que oferece parsing zero-copy, para minimizar alocações de memória, e foi projetada com base na filosofia de desempenho do ripgrep
  • Nos benchmarks, apresentou o melhor desempenho de ponta a ponta mesmo com JSONs grandes, oferecendo uma linguagem de consulta simples voltada à busca
  • Disponível sob licença MIT, com o mecanismo de consultas baseado em DFA reutilizável como biblioteca Rust

Visão geral do jsongrep

  • jsongrep é uma ferramenta CLI em Rust para buscar valores em documentos JSON com base em caminhos, com o objetivo de ser mais rápida que jq, jmespath, jsonpath-rust e jql
  • Trata documentos JSON como árvores e expressa caminhos (path) como linguagens regulares (regular language), compilando-os em DFAs (Deterministic Finite Automata) para realizar a busca em uma única passagem
  • A linguagem de consulta é simples e foi projetada com foco em busca, sem recursos de transformação ou cálculo
  • Minimiza alocações de memória com parsing zero-copy usando serde_json_borrow
  • Foi desenvolvida com referência à filosofia de design e à abordagem de desempenho do ripgrep

Exemplos de uso do jsongrep

  • O comando jg recebe uma consulta e uma entrada JSON, e imprime todos os valores cujos caminhos correspondem à consulta
  • Acesso a campos aninhados com notação por ponto (dot path)
    • jg 'roommates[0].name'"Alice"
  • Wildcards (*, [*]) para corresponder a todas as chaves ou índices
  • Alternation (|) para escolher um entre vários caminhos
  • Busca recursiva ((* | [*])*) para pesquisar campos em qualquer profundidade
  • Optional (?) permite correspondência de 0 ou 1 vez
  • A opção -F permite buscar rapidamente por um nome de campo específico
  • Ao usar pipe (| less, | sort), a exibição do caminho é omitida automaticamente; --with-path pode forçar sua exibição

Conceitos centrais do jsongrep

  • JSON é uma estrutura em árvore, e chaves de objetos e índices de arrays atuam como arestas (edges)
  • A consulta define um conjunto de caminhos do nó raiz até nós específicos
  • A linguagem de consulta é projetada como uma linguagem regular, o que permite convertê-la em um DFA
  • O DFA lê a entrada apenas uma vez e faz a busca em tempo O(n), sem backtracking
  • Ferramentas existentes (jq, jmespath etc.) interpretam a consulta e fazem busca recursiva, enquanto o jsongrep usa um DFA pré-compilado para busca em passagem única

Estrutura do mecanismo de consultas baseado em DFA

  • O pipeline é composto por 5 etapas
    1. Parse do JSON em árvore com serde_json_borrow
    2. Parse da consulta em AST
    3. Geração de NFA com o algoritmo de Glushkov
    4. Conversão para DFA com Subset Construction
    5. Percurso da árvore JSON com uma única DFS seguindo as transições do DFA
  • Parse da consulta

    • Converte a consulta em AST Query com gramática PEG (usando a biblioteca pest)
    • Principais elementos sintáticos: Field, Index, Range, FieldWildcard, ArrayWildcard, Optional, KleeneStar, Disjunction, Sequence
    • Ex.: roommates[*].nameSequence(Field("roommates"), ArrayWildcard, Field("name"))
  • Modelo de árvore JSON

    • Chaves de objetos e índices de arrays são arestas, e valores são nós
    • Ex.: roommates[*].name percorre o caminho roommates[0]name
  • Construção do NFA (algoritmo de Glushkov)

    • Gera um NFA sem transições ε
    • Etapas
      1. Atribuir números de posição aos símbolos da consulta
      2. Calcular os conjuntos First/Last/Follows
      3. Construir as transições entre posições
    • O NFA da consulta de exemplo roommates[*].name forma uma estrutura linear simples com 4 estados
  • Conversão para DFA (Subset Construction)

    • Gera um DFA determinístico com base em conjuntos de estados do NFA
    • Cada estado corresponde a um conjunto de estados do NFA
    • Adiciona o símbolo Other para pular com eficiência chaves desnecessárias
    • Consultas simples são convertidas em um DFA com estrutura idêntica à do NFA
  • Busca baseada em DFS

    • A partir da raiz, executa transições do DFA seguindo cada aresta
    • Se não houver transição, aquela subárvore é podada (prune)
    • Se o estado do DFA for accepting, registra o caminho e o valor
    • Cada nó é visitado no máximo uma vez, então a busca total é O(n)
    • Com serde_json_borrow, referencia o buffer original sem copiar strings

Metodologia de benchmark

  • Benchmarks estatísticos realizados com Criterion.rs
  • Conjuntos de dados

    • simple.json (106B), kubernetes-definitions.json (~992KB), kestra-0.19.0.json (~7.6MB), citylots.json (~190MB)
  • Ferramentas comparadas

    • jsongrep, jsonpath-rust, jmespath, jaq, jql
  • Grupos de benchmark

    1. document_parse: velocidade de parsing do JSON
    2. query_compile: tempo de compilação da consulta
    3. query_search: apenas a busca
    4. end_to_end: pipeline completo
  • Considerações de justiça na comparação

    • A vantagem do parsing zero-copy foi medida separadamente
    • O custo de compilação do DFA foi medido separadamente
    • Ferramentas sem determinado recurso foram excluídas do teste correspondente
    • O custo de duplicação de dados foi tratado separadamente

Resultados dos benchmarks

  • Tempo de parsing do documento: serde_json_borrow foi o mais rápido
  • Tempo de compilação da consulta: jsongrep teve o maior custo por causa da geração do DFA, enquanto jmespath foi muito mais rápido
  • Tempo de busca: jsongrep foi o mais rápido entre todas as ferramentas
  • Desempenho de ponta a ponta: mesmo no conjunto de dados de 190MB, foi amplamente mais rápido que jq, jmespath, jsonpath-rust e jql
  • Os resultados completos podem ser vistos no site de benchmarks ao vivo

Licença e uso

  • Software de código aberto sob licença MIT
  • Disponível no GitHub, Crates.io e Docs.rs
  • O mecanismo de consultas baseado em DFA pode ser reutilizado como biblioteca, sendo integrado diretamente a projetos Rust

Referências

  • Glushkov, V. M. (1961), The Abstract Theory of Automata
  • Rabin, M. O., & Scott, D. (1959), Finite Automata and Their Decision Problems

3 comentários

 
roxie 28 일 전

Muito legal.

 
lamanus 2026-03-28

| Por que o caractere de pipe aparece diferente no corpo do texto? Que curioso..

 
GN⁺ 2026-03-28
Comentários do Hacker News
  • A sintaxe do jq é difícil demais, então sempre preciso pesquisar até para pegar um único valor simples de JSON

    • Interessante. Eu acho a sintaxe do jq intuitiva, e estou acostumado a usar só ponto, pipe e colchetes, como em um pipeline de shell
      Como normalmente escrevo filtros de uso único, passo mais tempo escrevendo do que lendo
      Talvez meu caso de uso seja simples, ou jq combine bem com a forma como penso
      Sonho com um mundo em que todas as ferramentas CLI façam entrada e saída em JSON e sejam conectadas com jq, mas isso provavelmente seria um pesadelo para você
    • Eu não uso jq com frequência, então ele é uma ferramenta que fica no “vale do aprendizado”
      Toda vez que uso, preciso reaprender, então não parece intuitivo
      Mesmo que o sed seja Turing-completo, a maioria das pessoas só o usa para substituição com regex
    • Recomendo o celq, que eu criei
      Gosto de jq, mas às vezes não conseguia entender consultas que eu mesmo havia escrito antes
      O celq usa a linguagem CEL, que é mais familiar
    • Eu também criei uma ferramenta chamada dq por motivos parecidos
      Ela simplesmente manipula JSON com JavaScript e, surpreendentemente, é mais rápida que jq
      Eu uso assim: $ cat package.json | dq 'Object.keys(data).slice(0, 5)'
    • O próprio JSON tem muito ruído sintático desnecessário, o que é irritante
      Como aprendi Clojure, agora uso EDN em vez de JSON
      É mais conciso, mais fácil de ler e mais simples de manipular estruturalmente
      Hoje em dia uso borkdude/jet ou babashka para lidar com dados, e djblue/portal para visualização
      Não entendo por que insistir nos operadores complexos do jq
  • Eu valorizo desempenho, mas comparações em nanossegundos parecem mais uma performance exibicionista
    Na maioria dos casos, a ferramenta que já uso é suficiente
    Por exemplo, eu só uso rg no lugar de grep quando o arquivo é grande

    • Quando penso assim, costumo concluir: “eu não sou o usuário-alvo
      A diferença entre 2 ms e 0,2 ms pode parecer pequena, mas para quem processa streams em escala de TB isso importa
    • Mas se todo mundo pensar assim, no fim pequenas ineficiências se acumulam e tudo fica mais lento
      O hardware ficou mais rápido, mas o software acabou ficando mais lento
    • Para jq se diferenciar, ele precisaria de uma sintaxe mais intuitiva e mais exemplos do mundo real
    • Essa ideia de “rápido o suficiente” sempre me incomoda
      Recusar otimização parece preguiça e falta de imaginação
      Ficar tranquilo só porque é mais rápido que a latência de rede soa como desculpa
    • Eu também valorizo mais usabilidade e funcionalidade do que velocidade
      Se o JSON é grande demais, então talvez fosse melhor usar um formato binário em vez de JSON
      Se for preciso montar pipelines complexos na CLI, acho melhor escrever um programa de uma vez
  • Muitas ferramentas CLI novas se vendem como “mais rápidas”, mas quase nunca senti que jq fosse lento de verdade

    • Eu trabalho com arquivos ndjson em escala de TB
      Até tarefas simples, como só renomear campos com jq, são lentas demais, então faço isso direto com scripts em Node ou Rust
    • Ao lidar com arquivos de log gigantescos, jq pode parecer lento
      Em ambientes de hyperscaler, as pessoas baixam e analisam diretamente logs com vários TB
    • Nós analisamos respostas JSON vindas de milhares de nós
      Dependendo da resolução do monitoramento, a diferença de desempenho pode ser perceptível
    • Sempre que surge uma ferramenta nova, se repete o padrão de “uma versão mais rápida reescrita em Rust”
      Ela implementa só parte das funcionalidades e depois reivindica vitória com benchmarks
      Este projeto também parece fazer parte dessa tendência de “o subconjunto é mais rápido”
    • Você só percebe que uma ferramenta é lenta quando ela realmente fica lenta para você
      A partir daí, tudo começa a parecer lento
      Depois de usar uma ferramenta rápida como o ripgrep, fica difícil voltar atrás
  • Já usei tanto jq quanto yq, mas nunca me incomodei com o fato de yq ser muito mais lento
    Se existir uma ferramenta mais rápida que jq, ótimo, mas isso só é necessário para um grupo específico de usuários
    Ainda assim, como alguém que ama otimização, deixo meu respeito

    • Fiquei curioso sobre qual yq você quer dizer — a versão em Go ou a versão em Python?
    • Em ambientes integrados de servidor, desempenho importa, mas na CLI geralmente tudo já é rápido o bastante
    • Eu processo GeoJSONs de vários GB exportados do ArcGIS com jq
      Na etapa de ETL isso consome bastante tempo
    • Nem todo mundo usa ferramentas do mesmo jeito
  • Quando abri a página pela primeira vez, havia um problema de cores quebradas no modo claro
    Se eu alternasse para o modo escuro e depois voltasse, resolvia

    • O site também parece ter sido “vibe-coded”, igual a ferramenta
    • Sou o autor, e como não uso modo claro acabei esquecendo de testar; vou corrigir isso agora
    • Era um problema em que alguns estilos do modo escuro estavam vazando no CSS
    • No Firefox para Android estava tudo bem, mas a escala dos gráficos varia demais, então é difícil comparar
    • Já foi corrigido
  • Eu migrei para Jaq por causa da correção
    Dizem que o desempenho dele também é melhor que o do jq

    • Obrigado pela recomendação. O jaq parece ter evoluído na direção certa mais do que o jsongrep
    • Só que, embora o jaq 3.0 seja mais rápido que o jq empacotado em distribuições, um jq compilado manualmente é mais rápido
      A reputação de lentidão do jq parece vir de problemas de empacotamento nas distribuições
  • No trabalho eu lido com frequência com JSON delimitado por nova linha (jsonl)
    Cada linha é um objeto JSON completo, e fiquei curioso para saber se as principais ferramentas CLI suportam esse formato

  • Já usei várias ferramentas CLI de processamento de dados como jq, mlr, htmlq, xsv e yq,
    mas depois que descobri o Nushell, ele substituiu todas elas
    Foi uma experiência renovadora poder lidar com todos os formatos com uma única sintaxe

    • Eu também uso o Nushell como meu shell principal desde meados de 2023
      Só continuo usando jq, yq e mlr em paralelo quando preciso colaborar com colegas
    • Eu também substituí quase tudo por Nushell
      Ainda tenho algum incômodo com configuração de autocomplete e capacidade de descoberta de comandos, mas ele é muito melhor que oh-my-zsh
      Se ganhar imposição de anotações de tipo, compilação para binário estático e até uma biblioteca TUI, eu usaria até para escrever apps pequenos
    • Concordo. O Nushell torna automação muito mais fácil graças à sintaxe intuitiva e consistente
  • Ferramenta legal! Só achei a visualização dos benchmarks um pouco fraca
    Como todas as ferramentas têm a mesma cor, fica difícil localizar o jsongrep
    O próprio jq também não aparece no gráfico, o que me deixou confuso
    O arquivo xLarge tem 190 MiB, o que ainda é pequeno; eu costumo lidar com JSONs de 400 MiB a 1 GiB

    • O autor respondeu: no momento, o escopo dos benchmarks vai de 106 B até 190 MB
      Se alguém souber de documentos JSON públicos maiores, seria ótimo indicar
  • A visualização dos benchmarks parece meio grosseira
    Seria melhor usar cores ou formas para representar mais dimensões
    Ter de ler diretamente o caminho do arquivo para entender os resultados é inconveniente