17 Punkte von GN⁺ 2026-03-28 | 3 Kommentare | Auf WhatsApp teilen
  • Ein Rust-CLI-Tool zum pfadbasierten Durchsuchen von JSON-Dokumenten, das bei der Suchgeschwindigkeit schneller ist als jq, jmespath, jsonpath-rust und jql
  • Es drückt Abfragen als reguläre Sprache aus und kompiliert sie zu einem DFA; die JSON-Baumstruktur wird in einem einzigen Durchlauf durchsucht, wodurch die Verarbeitung in O(n) erfolgt
  • Verwendet serde_json_borrow mit Unterstützung für Zero-Copy-Parsing, um Speicherallokationen zu minimieren, und ist nach der Performance-Philosophie von ripgrep entworfen
  • Benchmark-Ergebnisse zeigen die beste End-to-End-Performance auch bei großen JSON-Dateien; dazu kommt eine einfache, auf Suche fokussierte Abfragesprache
  • Unter der MIT-Lizenz veröffentlicht; die DFA-basierte Query-Engine kann auch als Rust-Bibliothek wiederverwendet werden

Überblick über jsongrep

  • jsongrep ist ein Rust-basiertes CLI-Tool, das Werte in JSON-Dokumenten pfadbasiert sucht und auf höhere Geschwindigkeit als jq, jmespath, jsonpath-rust und jql abzielt
  • JSON-Dokumente werden als Baum betrachtet; Pfade werden als reguläre Sprache ausgedrückt, in einen DFA (Deterministic Finite Automaton) kompiliert und dann in einem einzigen Durchlauf durchsucht
  • Die Abfragesprache ist einfach und suchorientiert gestaltet; Transformations- oder Rechenfunktionen gibt es nicht
  • Zero-Copy-Parsing mit serde_json_borrow minimiert Speicherallokationen
  • Entwickelt mit Bezug auf die Design-Philosophie und den Performance-Ansatz von ripgrep

Beispiele für die Verwendung von jsongrep

  • Der Befehl jg nimmt eine Abfrage und JSON-Eingabe entgegen und gibt alle Werte aus, deren Pfad zur Abfrage passt
  • Zugriff auf verschachtelte Felder mit Punktnotation (dot path)
    • jg 'roommates[0].name'"Alice"
  • Wildcards (*, [*]) zum Abgleichen aller Schlüssel oder Indizes
  • Alternation (|) zur Auswahl eines von mehreren Pfaden
  • Rekursive Suche ((* | [*])*) für die Feldsuche in beliebiger Tiefe
  • Optional (?) unterstützt 0- oder 1-maliges Matching
  • Mit der Option -F kann schnell nach einem bestimmten Feldnamen gesucht werden
  • Bei Verwendung von Pipes (| less, | sort) wird die Pfadausgabe automatisch unterdrückt; mit --with-path lässt sie sich erzwingen

Zentrale Konzepte von jsongrep

  • JSON ist eine Baumstruktur, in der Objektschlüssel und Array-Indizes als Kanten (edges) fungieren
  • Eine Abfrage definiert eine Menge von Pfaden vom Root zu bestimmten Knoten
  • Die Abfragesprache ist als reguläre Sprache entworfen und kann daher in einen DFA umgewandelt werden
  • Der DFA liest die Eingabe nur einmal und durchsucht sie ohne Backtracking in O(n)
  • Bestehende Tools (jq, jmespath usw.) interpretieren Abfragen und durchsuchen rekursiv, während jsongrep mit einem vorab kompilierten DFA in einem einzigen Durchlauf sucht

Aufbau der DFA-basierten Query-Engine

  • Die Pipeline besteht aus 5 Schritten
    1. JSON mit serde_json_borrow in einen Baum parsen
    2. Die Abfrage in einen AST parsen
    3. Mit dem Glushkov-Algorithmus einen NFA erzeugen
    4. Per Subset Construction in einen DFA umwandeln
    5. Den JSON-Baum in einer einzelnen DFS entlang der DFA-Übergänge durchsuchen
  • Parsing der Abfragen

    • Mit einer PEG-Grammatik (unter Verwendung der Bibliothek pest) wird die Abfrage in einen Query-AST umgewandelt
    • Wichtige Syntaxelemente: Field, Index, Range, FieldWildcard, ArrayWildcard, Optional, KleeneStar, Disjunction, Sequence
    • Beispiel: roommates[*].nameSequence(Field("roommates"), ArrayWildcard, Field("name"))
  • JSON-Baummodell

    • Objektschlüssel und Array-Indizes sind Kanten, Werte sind Knoten
    • Beispiel: roommates[*].name durchsucht den Pfad roommates[0]name
  • NFA-Konstruktion (Glushkov-Algorithmus)

    • Erzeugt einen NFA ohne ε-Übergänge
    • Schritte
      1. Positionsnummern für die Abfragesymbole vergeben
      2. First-/Last-/Follows-Mengen berechnen
      3. Übergänge zwischen den Positionen aufbauen
    • Das Beispiel roommates[*].name ergibt einen einfachen linearen NFA mit 4 Zuständen
  • DFA-Umwandlung (Subset Construction)

    • Erzeugt einen deterministischen DFA auf Basis von Mengen von NFA-Zuständen
    • Jeder Zustand entspricht einer Menge von NFA-Zuständen
    • Ein zusätzliches Symbol Other erlaubt es, irrelevante Schlüssel effizient zu überspringen
    • Einfache Abfragen werden in einen DFA mit derselben Struktur wie der NFA umgewandelt
  • DFS-basierte Suche

    • Beginnend am Root werden entlang jeder Kante DFA-Übergänge ausgeführt
    • Gibt es keinen Übergang, wird der betreffende Teilbaum abgeschnitten (pruned)
    • Ist ein DFA-Zustand accepting, werden Pfad und Wert aufgezeichnet
    • Jeder Knoten wird höchstens einmal besucht; die gesamte Suche ist O(n)
    • Mit serde_json_borrow wird ohne String-Kopien direkt auf den Originalpuffer referenziert

Benchmark-Methodik

  • Statistikbasierte Benchmarks mit Criterion.rs
  • Datensätze

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

    • jsongrep, jsonpath-rust, jmespath, jaq, jql
  • Benchmark-Gruppen

    1. document_parse: Geschwindigkeit beim JSON-Parsing
    2. query_compile: Zeit für die Query-Kompilierung
    3. query_search: nur die Suche
    4. end_to_end: die gesamte Pipeline
  • Aspekte der Fairness

    • Der Vorteil von Zero-Copy-Parsing wird separat gemessen
    • Die Kosten der DFA-Kompilierung werden getrennt gemessen
    • Tools ohne entsprechende Funktion werden von dem jeweiligen Test ausgeschlossen
    • Die Kosten für Datenkopien werden separat behandelt

Benchmark-Ergebnisse

  • Zeit fürs Dokument-Parsing: serde_json_borrow ist am schnellsten
  • Zeit für die Query-Kompilierung: jsongrep hat wegen der DFA-Erzeugung die höchsten Kosten, jmespath ist deutlich schneller
  • Suchzeit: jsongrep ist unter allen Tools am schnellsten
  • End-to-End-Performance: Selbst beim 190-MB-Datensatz deutlich schneller als jq, jmespath, jsonpath-rust und jql
  • Die vollständigen Ergebnisse sind auf der Live-Benchmark-Seite verfügbar

Lizenz und Einsatzmöglichkeiten

  • Open-Source-Software unter der MIT-Lizenz
  • Verfügbar über GitHub, Crates.io und Docs.rs
  • Die DFA-basierte Query-Engine kann als Bibliothek wiederverwendet und direkt in Rust-Projekte integriert werden

Literaturhinweise

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

3 Kommentare

 
roxie 26 일 전

Ziemlich cool.

 
lamanus 2026-03-28

| Warum wird das Pipe-Zeichen im Haupttext anders angezeigt? Irgendwie interessant..

 
GN⁺ 2026-03-28
Hacker-News-Kommentare
  • Die Syntax von jq ist zu kryptisch, sodass ich jedes Mal nachschlagen muss, selbst wenn ich nur einen einfachen JSON-Wert herausziehen will

    • Interessant. Ich empfinde die Syntax von jq als intuitiv, und die Nutzung von Punkten, Pipes und eckigen Klammern wie in einer Shell-Pipeline fühlt sich vertraut an
      Ich schreibe meist Einweg-Filter, daher verbringe ich mehr Zeit mit dem Schreiben als mit dem Lesen
      Wahrscheinlich sind meine Anwendungsfälle einfach oder jq passt einfach gut zu meiner Denkweise
      Ich träume von einer Welt, in der alle CLI-Tools JSON ein- und ausgeben und über jq verbunden werden, aber für dich klingt das vermutlich wie ein Albtraum
    • jq ist ein Tool, das man selten genug nutzt, um im „Tal des Lernens“ stecken zu bleiben
      Jedes Mal, wenn man es verwendet, muss man es praktisch neu lernen, also ist es nicht intuitiv
      Auch sed ist Turing-vollständig, trotzdem nutzen die meisten es nur für Regex-Ersetzungen
    • Ich empfehle celq, das ich gebaut habe
      Ich mag jq, aber es gab Zeiten, in denen ich Abfragen, die ich früher selbst geschrieben hatte, nicht mehr verstanden habe
      celq verwendet die vertrautere CEL-Sprache
    • Aus ähnlichen Gründen habe ich selbst ein Tool namens dq gebaut
      Es ist einfach ein Weg, JSON mit JavaScript zu verarbeiten, und überraschenderweise ist es schneller als jq
      Verwendet wird es etwa so: $ cat package.json | dq 'Object.keys(data).slice(0, 5)'
    • JSON selbst hat zu viel unnötiges syntaktisches Rauschen, was lästig ist
      Da ich Clojure gelernt habe, nutze ich inzwischen statt JSON EDN
      Es ist kompakter, leichter zu lesen und strukturell einfacher zu verarbeiten
      Heute arbeite ich mit Daten über borkdude/jet oder babashka und visualisiere sie mit djblue/portal
      Ich verstehe nicht, warum man auf den komplizierten Operatoren von jq beharren sollte
  • Leistung ist mir wichtig, aber Vergleiche im Nanosekundenbereich wirken auf mich wie Performance-Theater
    In den meisten Fällen reicht das aktuelle Tool vollkommen aus
    Zum Beispiel nutze ich bei großen Dateien statt grep nur dann rg

    • Wenn man so denkt, sollte man sich einfach sagen: „Ich bin nicht die Zielgruppe
      Der Unterschied zwischen 2 ms und 0,2 ms wirkt gering, ist aber für Leute, die Streams im TB-Bereich verarbeiten, relevant
    • Aber wenn alle so denken, summieren sich kleine Ineffizienzen und am Ende wird alles langsam
      Die Hardware ist schneller geworden, aber die Software in der Realität eher langsamer
    • Damit jq sich abheben kann, braucht es mehr intuitive Syntax und praxisnahe Beispiele
    • Diese Aussage „schnell genug“ stört mich immer etwas
      Sich Optimierung zu verweigern, wirkt wie Faulheit und mangelnde Vorstellungskraft
      Sich damit zu beruhigen, dass etwas immer noch schneller als Netzwerklatenz ist, klingt nach einer Ausrede
    • Ich halte Benutzbarkeit und Funktionen ebenfalls für wichtiger als rohe Geschwindigkeit
      Wenn JSON zu groß ist, sollte man statt JSON besser ein Binärformat verwenden
      Und wenn man in der CLI komplexe Pipelines bauen muss, ist es womöglich besser, gleich ein Programm zu schreiben
  • Viele neue CLI-Tools werben mit „schneller“, aber ich hatte nur selten das Gefühl, dass jq tatsächlich langsam ist

    • Ich arbeite mit ndjson-Dateien im TB-Bereich
      Selbst einfache Aufgaben wie das Umbenennen von Feldnamen sind mit jq zu langsam, daher verarbeite ich sie direkt mit Node- oder Rust-Skripten
    • Bei extrem großen Logdateien kann sich jq durchaus langsam anfühlen
      In Hyperscaler-Umgebungen lädt man mehrere TB an Logs herunter und analysiert sie direkt
    • Wir parsen JSON-Antworten von Tausenden von Knoten
      Je nach Auflösung des Monitorings kann der Leistungsunterschied spürbar sein
    • Bei jedem neuen Tool wiederholt sich das Muster „eine schnellere, in Rust neu geschriebene Version“
      Oft wird nur ein Teil der Funktionen implementiert und dann mit Benchmarks der Sieg verkündet
      Auch dieses Projekt wirkt wie Teil dieses Trends „ein Teilmengen-Tool ist schneller“
    • Dass ein Tool langsam ist, merkt man meist erst in dem Moment, in dem es langsam wird
      Danach fühlt sich plötzlich alles langsam an
      Wenn man einmal ein schnelles Tool wie ripgrep benutzt hat, ist der Weg zurück schwer
  • Ich habe sowohl jq als auch yq benutzt, aber bei yq hat mich die deutlich geringere Geschwindigkeit nie gestört
    Ein Tool, das schneller als jq ist, ist nett, aber das braucht nur eine bestimmte Nutzergruppe
    Trotzdem zolle ich als jemand, der Optimierung liebt, Respekt

    • Ich frage mich, welches yq gemeint ist — die Go-Version oder die Python-Version?
    • In serverintegrierten Umgebungen ist Performance wichtig, aber in der CLI ist es meistens schnell genug
    • Ich verarbeite mit jq mehrere GB GeoJSON, exportiert aus ArcGIS
      Im ETL-Schritt kostet das durchaus Zeit
    • Nicht alle benutzen Tools auf dieselbe Weise
  • Beim ersten Öffnen der Seite gab es einen kaputten Light-Mode-Farbstil
    Nach dem Wechsel in den Dark Mode und zurück war es behoben

    • Die Website fühlt sich wie das Tool selbst etwas „vibe-coded“ an
    • Ich bin der Autor und nutze den Light Mode nicht, daher habe ich vergessen, ihn zu testen; ich behebe das sofort
    • Das Problem war, dass einige Dark-Mode-Stile aus dem CSS hineingeleckt sind
    • In Android Firefox war es okay, aber die Diagrammskalierungen sind uneinheitlich, was Vergleiche erschwert
    • Ist jetzt behoben
  • Ich bin aus Gründen der Korrektheit zu Jaq gewechselt
    Angeblich ist es auch schneller als jq

    • Danke für die Empfehlung. jaq scheint gegenüber jsongrep in die richtigere Richtung weiterentwickelt zu sein
    • Allerdings ist jaq 3.0 zwar schneller als das jq aus den Distributionen, aber ein selbst kompiliertes jq ist schneller
      Der Ruf von jq als langsames Tool scheint eher an Problemen beim Paketieren in Distributionen zu liegen
  • Ich arbeite beruflich oft mit newline-delimited JSON (jsonl)
    Jede Zeile ist ein vollständiges JSON-Objekt, und ich frage mich, ob die wichtigsten CLI-Tools dieses Format unterstützen

  • Ich habe viele CLI-Tools zur Datenverarbeitung benutzt, darunter jq, mlr, htmlq, xsv und yq,
    aber seit ich Nushell entdeckt habe, hat es sie alle ersetzt
    Ein einziges Syntaxsystem für alle Formate zu haben, war eine erfrischende Erfahrung

    • Ich nutze Nushell seit Mitte 2023 als meine primäre Shell
      Nur bei der Zusammenarbeit mit Kolleginnen und Kollegen verwende ich zusätzlich jq, yq und mlr
    • Bei mir hat Nushell ebenfalls fast alles ersetzt
      Bei der Einrichtung der Autovervollständigung und der Auffindbarkeit von Befehlen gibt es etwas Reibung, aber es ist viel besser als oh-my-zsh
      Wenn noch erzwungene Typannotationen, statisch kompilierte Binärdateien und eine TUI-Bibliothek dazukommen, würde ich es sogar für kleine Apps nutzen
    • Stimme zu. Nushell macht Automatisierung durch seine intuitive und konsistente Syntax deutlich einfacher
  • Tolles Tool! Allerdings ist die Benchmark-Visualisierung etwas enttäuschend
    Alle Tools haben dieselbe Farbe, sodass es schwer ist, jsongrep darin zu finden
    jq selbst fehlt auch in der Grafik, was verwirrend war
    Die xLarge-Datei ist mit 190 MiB eher klein; ich arbeite oft mit JSON-Dateien von 400 MiB bis 1 GiB

    • Ich antworte als Autor. Der aktuelle Benchmark-Bereich reicht von 106 B bis 190 MB
      Wenn es größere öffentliche JSON-Dokumente gibt, wäre ich für Hinweise dankbar
  • Die Benchmark-Visualisierung wirkt grob
    Es wäre gut, über Farben oder Formen mehr Dimensionen darzustellen
    Dass man die Dateipfade direkt lesen muss, um die Ergebnisse zu verstehen, ist unpraktisch