17 puntos por GN⁺ 2026-03-28 | 3 comentarios | Compartir por WhatsApp
  • Herramienta CLI en Rust para buscar documentos JSON por rutas, con mayor velocidad de búsqueda que jq, jmespath, jsonpath-rust y jql
  • Expresa las consultas como un lenguaje regular y las compila a un DFA, recorriendo el árbol JSON en una sola pasada para procesarlo en tiempo O(n)
  • Usa serde_json_borrow, que admite parsing zero-copy, para minimizar las asignaciones de memoria, y fue diseñada tomando como referencia la filosofía de rendimiento de ripgrep
  • Según los benchmarks, ofrece el mejor rendimiento end-to-end incluso con JSON de gran tamaño, y proporciona un lenguaje de consulta simple centrado en la búsqueda
  • Publicado bajo licencia MIT, y su motor de consultas basado en DFA puede reutilizarse como biblioteca Rust

Resumen de jsongrep

  • jsongrep es una herramienta CLI basada en Rust para buscar valores en documentos JSON por ruta, con el objetivo de ser más rápida que jq, jmespath, jsonpath-rust y jql
  • Considera el documento JSON como un árbol y expresa la ruta (path) como un lenguaje regular (regular language), que luego compila a un DFA (Deterministic Finite Automaton) para recorrerlo en una sola pasada
  • El lenguaje de consulta es simple y está diseñado en torno a la búsqueda, por lo que no incluye funciones de transformación ni cálculo
  • Minimiza las asignaciones de memoria mediante parsing zero-copy con serde_json_borrow
  • Fue desarrollado tomando como referencia la filosofía de diseño y el enfoque de rendimiento de ripgrep

Ejemplos de uso de jsongrep

  • El comando jg recibe una consulta y una entrada JSON, y muestra todos los valores cuya ruta coincide con la consulta
  • Acceso a campos anidados con notación de punto (dot path)
    • jg 'roommates[0].name'"Alice"
  • Uso de comodines (*, [*]) para coincidir con todas las claves o índices
  • Alternation (|) para seleccionar una entre varias rutas
  • Búsqueda recursiva ((* | [*])*) para encontrar campos a cualquier profundidad
  • Optional (?) para admitir coincidencias de 0 o 1 vez
  • La opción -F permite buscar rápidamente un nombre de campo específico
  • Al usar pipes (| less, | sort), omite automáticamente la salida de rutas; puede forzarse con --with-path

Conceptos clave de jsongrep

  • JSON es una estructura en árbol, y las claves de objetos y los índices de arreglos actúan como aristas (edge)
  • La consulta define un conjunto de rutas desde la raíz hasta un nodo específico
  • El lenguaje de consulta está diseñado como lenguaje regular, por lo que puede transformarse en un DFA
  • El DFA lee la entrada una sola vez y realiza la búsqueda en tiempo O(n) sin backtracking
  • Herramientas existentes como jq y jmespath interpretan la consulta y recorren de forma recursiva, mientras que jsongrep usa un DFA precompilado para hacer el recorrido en una sola pasada

Estructura del motor de consultas basado en DFA

  • El pipeline consta de 5 etapas
    1. Parsear el JSON como árbol con serde_json_borrow
    2. Parsear la consulta a un AST
    3. Generar un NFA con el algoritmo de Glushkov
    4. Convertirlo a un DFA con Subset Construction
    5. Recorrer el árbol JSON con un único DFS siguiendo las transiciones del DFA
  • Parsing de consultas

    • Convierte la consulta a un AST Query con una gramática PEG (usando la biblioteca pest)
    • Elementos sintácticos principales: Field, Index, Range, FieldWildcard, ArrayWildcard, Optional, KleeneStar, Disjunction, Sequence
    • Ejemplo: roommates[*].nameSequence(Field("roommates"), ArrayWildcard, Field("name"))
  • Modelo de árbol JSON

    • Las claves de objetos y los índices de arreglos son aristas, y los valores son nodos
    • Ejemplo: roommates[*].name recorre la ruta roommates[0]name
  • Construcción del NFA (algoritmo de Glushkov)

    • Genera un NFA sin transiciones ε
    • Pasos
      1. Asignar números de posición a los símbolos de la consulta
      2. Calcular los conjuntos First/Last/Follows
      3. Construir transiciones entre posiciones
    • El NFA para la consulta de ejemplo roommates[*].name tiene una estructura lineal simple de 4 estados
  • Conversión a DFA (Subset Construction)

    • Genera un DFA determinista a partir de conjuntos de estados del NFA
    • Cada estado corresponde a un conjunto de estados del NFA
    • Agrega el símbolo Other para saltarse de forma eficiente las claves innecesarias
    • Las consultas simples se convierten en un DFA con la misma estructura que el NFA
  • Búsqueda basada en DFS

    • Comienza en la raíz y ejecuta transiciones del DFA siguiendo cada arista
    • Si no hay transición, se poda (prune) ese subárbol
    • Si el estado del DFA es accepting, registra la ruta y el valor
    • Cada nodo se visita como máximo una vez, por lo que el recorrido completo es O(n)
    • serde_json_borrow referencia el buffer original sin copiar cadenas

Metodología de benchmark

  • Benchmarks estadísticos realizados con Criterion.rs
  • Conjuntos de datos

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

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

    1. document_parse: velocidad de parsing del JSON
    2. query_compile: tiempo de compilación de la consulta
    3. query_search: solo realiza la búsqueda
    4. end_to_end: pipeline completo
  • Consideraciones de equidad

    • La ventaja del parsing zero-copy se midió por separado
    • El costo de compilación del DFA se midió por separado
    • Las herramientas sin cierta funcionalidad se excluyeron de esa prueba
    • El costo de duplicación de datos se trató por separado

Resultados de benchmark

  • Tiempo de parsing del documento: serde_json_borrow fue el más rápido
  • Tiempo de compilación de la consulta: jsongrep tuvo el mayor costo por la generación del DFA, mientras que jmespath fue mucho más rápido
  • Tiempo de búsqueda: jsongrep fue el más rápido entre todas las herramientas
  • Rendimiento end-to-end: incluso sobre el dataset de 190MB, fue abrumadoramente más rápido que jq, jmespath, jsonpath-rust y jql
  • Los resultados completos pueden verse en el sitio de benchmarks en vivo

Licencia y uso

  • Software de código abierto bajo licencia MIT
  • Disponible en GitHub, Crates.io y Docs.rs
  • El motor de consultas basado en DFA puede reutilizarse como biblioteca, e integrarse directamente en proyectos Rust

Referencias

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

3 comentarios

 
roxie 26 일 전

Qué genial

 
lamanus 2026-03-28

| ¿Por qué el símbolo de tubería se ve diferente en el cuerpo? Qué curioso..

 
GN⁺ 2026-03-28
Comentarios en Hacker News
  • La sintaxis de jq es demasiado críptica, así que cada vez que quiero sacar aunque sea un valor simple de JSON, tengo que buscar cómo hacerlo

    • Interesante. A mí la sintaxis de jq me parece intuitiva, y ya estoy acostumbrado a usar solo puntos, pipes y corchetes, como en un pipeline de shell
      Normalmente escribo filtros de una sola vez, así que paso más tiempo escribiéndolos que leyéndolos
      Quizá mis casos de uso son simples o jq encaja bien con mi forma de pensar
      Sueño con un mundo donde todas las herramientas CLI lean y escriban JSON y se conecten con jq, aunque supongo que para ti eso sería una pesadilla
    • No uso jq tan seguido, así que es una herramienta que se queda en el “valle del aprendizaje”
      Cada vez que la uso siento que tengo que aprenderla de nuevo, así que no se siente intuitiva
      Incluso con Turing complete, la mayoría usa sed solo para reemplazos con regex
    • Recomiendo celq, que hice yo mismo
      Me gusta jq, pero hubo veces en que no podía entender consultas que yo mismo había escrito antes
      celq usa el lenguaje CEL, que resulta más familiar
    • Yo también hice mi propia herramienta llamada dq por razones parecidas
      Básicamente es una forma de manejar JSON con JavaScript, y sorprendentemente es más rápida que jq
      Se usa así: $ cat package.json | dq 'Object.keys(data).slice(0, 5)'
    • El propio JSON tiene demasiado ruido sintáctico innecesario, así que me resulta molesto
      Como aprendí Clojure, ahora uso EDN en vez de JSON
      Es más conciso, más fácil de leer y más cómodo de manejar estructuralmente
      Últimamente manejo datos con borkdude/jet o babashka, y los visualizo con djblue/portal
      No entiendo por qué insistir en operadores complejos como los de jq
  • Me importa el rendimiento, pero comparar en nanosegundos me parece más bien rendimiento para presumir
    En la mayoría de los casos, las herramientas que ya uso son suficientes
    Por ejemplo, yo solo uso rg en lugar de grep cuando trabajo con archivos grandes

    • Cuando pienso eso, simplemente asumo: “yo no soy el usuario objetivo
      La diferencia entre 2 ms y 0.2 ms puede parecer mínima, pero para quien procesa streams de tamaño TB sí importa
    • Pero si todos pensaran así, al final las pequeñas ineficiencias se acumulan y todo se vuelve más lento
      El hardware se ha vuelto más rápido, pero el software en realidad se ha vuelto más lento
    • Si jq quiere diferenciarse, necesita una sintaxis más intuitiva y más ejemplos reales
    • La idea de “es suficientemente rápido” siempre me incomoda
      Negarse a optimizar se siente como pereza y falta de imaginación
      Quedarse tranquilo solo porque es más rápido que la latencia de red suena a excusa
    • Yo también valoro más la usabilidad y las funciones que la velocidad
      Si el JSON es demasiado grande, entonces probablemente deberías usar un formato binario en lugar de JSON
      Si necesitas armar pipelines complejos en la CLI, yo diría que mejor escribas un programa
  • Muchas herramientas CLI nuevas se venden con el mensaje de “somos más rápidas”, pero casi nunca he sentido que jq sea lento

    • Yo trabajo con archivos ndjson de tamaño TB
      Incluso tareas simples como renombrar campos con jq son demasiado lentas, así que las proceso yo mismo con scripts en Node o Rust
    • Si trabajas con archivos de logs gigantescos, jq sí puede sentirse lento
      En entornos de hiperescaladores, se descargan y analizan directamente varios TB de logs
    • Nosotros parseamos respuestas JSON de miles de nodos
      Dependiendo de la resolución del monitoreo, la diferencia de rendimiento puede notarse
    • Cada vez que sale una herramienta nueva, se repite el patrón de “una versión más rápida rehecha en Rust”
      Implementan solo parte de la funcionalidad y luego proclaman victoria con benchmarks
      Este proyecto también parece parte de esa tendencia de “el subconjunto es más rápido”
    • Solo te das cuenta de que una herramienta es lenta cuando realmente te toca el momento en que se vuelve lenta
      Desde entonces, todo te empieza a parecer lento
      Como con ripgrep: una vez que usas una herramienta rápida, cuesta volver atrás
  • He usado tanto jq como yq, pero aunque yq es bastante más lento, nunca me molestó
    Si existe una herramienta más rápida que jq, genial, pero eso solo le hace falta a cierto tipo de usuarios
    Aun así, como alguien que ama la optimización, mis respetos

    • Me da curiosidad a cuál yq te refieres: ¿la versión en Go o la de Python?
    • En entornos de integración de servidores el rendimiento importa, pero en CLI casi siempre es suficientemente rápido
    • Yo proceso varios GB de GeoJSON exportado desde ArcGIS con jq
      En la etapa ETL sí toma bastante tiempo
    • No todo el mundo usa las herramientas de la misma forma
  • Cuando abrí la página por primera vez, había un fallo de colores en modo claro
    Si cambiabas a modo oscuro y luego regresabas, se arreglaba

    • El sitio también se siente como si estuviera vibe-coded, igual que la herramienta
    • Yo soy el autor y no uso modo claro, así que olvidé probarlo; lo arreglo de inmediato
    • Era un problema donde algunos estilos de modo oscuro se estaban filtrando en el CSS
    • En Android Firefox se veía bien, pero la escala de las gráficas era distinta en cada una, así que comparar era difícil
    • Ya quedó arreglado
  • Me cambié a Jaq por la precisión
    Dicen que además rinde mejor que jq

    • Gracias por la recomendación. jaq parece haber evolucionado en una dirección más correcta que jsongrep
    • Aunque jaq 3.0 sí es más rápido que el jq empaquetado por la distro, un jq compilado manualmente es más rápido
      La reputación de jq como lento parece deberse a cómo lo empaquetan las distribuciones
  • En el trabajo manejo mucho newline-delimited JSON (jsonl)
    Cada línea es un objeto JSON completo, y me da curiosidad saber si las principales herramientas CLI soportan este formato

  • He usado varias herramientas CLI para procesar datos como jq, mlr, htmlq, xsv y yq,
    pero desde que descubrí Nushell, reemplazó a todas
    Fue una experiencia refrescante poder manejar todos los formatos con una sola sintaxis

    • Yo también uso Nushell como shell principal desde mediados de 2023
      Solo sigo usando jq, yq y mlr cuando colaboro con compañeros
    • Yo también reemplacé casi todo con Nushell
      Hay algunas pequeñas molestias con la configuración del autocompletado y la facilidad para encontrar comandos, pero es muchísimo mejor que oh-my-zsh
      Si algún día agrega obligatoriedad de anotaciones de tipos, compilación a binarios estáticos y una librería TUI, hasta serviría para escribir apps pequeñas
    • Yo también estoy de acuerdo. Nushell hace la automatización mucho más fácil gracias a su sintaxis intuitiva y consistente
  • ¡Gran herramienta! Pero la visualización de benchmarks me pareció un poco floja
    Todas las herramientas tienen el mismo color, así que cuesta encontrar dónde está jsongrep
    Además jq ni siquiera aparece en la gráfica, lo que me confundió
    El archivo xLarge tiene 190MiB, que me parece pequeño; yo suelo manejar JSON de 400MiB a 1GiB

    • Responde el autor: por ahora el rango de benchmarks va de 106B a 190MB
      Si conocen documentos JSON públicos más grandes, sería bueno que me los compartieran
  • La visualización de benchmarks se siente tosca
    Estaría bien usar colores o formas para expresar más dimensiones
    Tener que leer directamente las rutas de archivos para entender los resultados es incómodo