jq से तेज़ jsongrep
(micahkepe.com)- JSON दस्तावेज़ों को path-based तरीके से खोजने वाला Rust CLI टूल, जिसकी search speed मौजूदा
jq,jmespath,jsonpath-rust,jqlसे तेज़ है - query को regular language में व्यक्त करके DFA में compile किया जाता है, और JSON tree को single pass में traverse करने वाली संरचना के कारण O(n) समय में प्रोसेस करता है
- zero-copy parsing को support करने वाले
serde_json_borrowका उपयोग कर memory allocation को न्यूनतम रखता है, और इसे ripgrep की performance philosophy से प्रेरित होकर डिज़ाइन किया गया है - benchmark नतीजों के अनुसार, बड़े JSON पर भी end-to-end performance सबसे बेहतर है, और यह search-focused सरल query language प्रदान करता है
- MIT license के तहत उपलब्ध है, और DFA-आधारित query engine को Rust library के रूप में दोबारा उपयोग किया जा सकता है
jsongrep का अवलोकन
- jsongrep JSON दस्तावेज़ों में path-based तरीके से values खोजने वाला Rust-आधारित CLI टूल है, जिसका लक्ष्य
jq,jmespath,jsonpath-rust,jqlसे तेज़ प्रदर्शन देना है - यह JSON दस्तावेज़ को एक tree की तरह देखता है, और path को regular language के रूप में व्यक्त करके DFA (Deterministic Finite Automaton) में compile करने के बाद single pass में traversal करता है
- query language सरल है और search-focused तरीके से डिज़ाइन की गई है, इसलिए इसमें transformation या calculation features नहीं हैं
serde_json_borrowका उपयोग कर zero-copy parsing से memory allocation को न्यूनतम किया जाता है- इसका विकास
ripgrepकी design philosophy और performance approach को संदर्भ में रखकर किया गया है
jsongrep के उपयोग के उदाहरण
jgकमांड query और JSON input लेती है, और उन सभी values को output करती है जिनका path query से match करता है- dot notation (dot path) से nested fields तक पहुँचा जा सकता है
jg 'roommates[0].name'→"Alice"
- wildcard (
*,[*]) से सभी keys या indexes match किए जा सकते हैं - Alternation (
|) से कई paths में से एक चुना जा सकता है - recursive search (
(* | [*])*) से किसी भी depth पर fields खोजे जा सकते हैं - Optional (
?) से 0 या 1 बार match को support किया जाता है -Foption से किसी खास field name को तेज़ी से खोजा जा सकता है- pipe (
| less,| sort) का उपयोग करने पर path output अपने-आप छिप जाता है, और--with-pathसे इसे मजबूरी में दिखाया जा सकता है
jsongrep की मुख्य अवधारणाएँ
- JSON एक tree structure है, और object keys तथा array indexes edge की भूमिका निभाते हैं
- query root से किसी खास node तक पहुँचने वाले paths के set को परिभाषित करती है
- query language को regular language के रूप में डिज़ाइन किया गया है, इसलिए इसे DFA में बदला जा सकता है
- DFA input को सिर्फ एक बार पढ़ता है और backtracking के बिना O(n) समय में traversal करता है
- मौजूदा tools (
jq,jmespathआदि) query को interpret करते हुए recursive traversal करते हैं, जबकि jsongrep precompiled DFA से single-pass traversal करता है
DFA-आधारित query engine की संरचना
- pipeline 5 चरणों से बनी है
serde_json_borrowसे JSON को tree में parse करना- query को AST में parse करना
- Glushkov algorithm से NFA बनाना
- Subset Construction से DFA में बदलना
- DFA transitions का पालन करते हुए JSON tree को single DFS में traverse करना
-
query parsing
- PEG grammar (
pestlibrary का उपयोग) से query कोQueryAST में बदला जाता है - मुख्य syntax elements:
Field,Index,Range,FieldWildcard,ArrayWildcard,Optional,KleeneStar,Disjunction,Sequence - उदाहरण:
roommates[*].name→Sequence(Field("roommates"), ArrayWildcard, Field("name"))
- PEG grammar (
-
JSON tree model
- object keys और array indexes edges हैं, values nodes हैं
- उदाहरण:
roommates[*].nameroommates→[0]→namepath को traverse करता है
-
NFA निर्माण (Glushkov algorithm)
- ε-transitions के बिना NFA बनाया जाता है
- चरण
- query symbols को position numbers देना
- First/Last/Follows sets की गणना करना
- हर position के बीच transitions बनाना
- उदाहरण query
roommates[*].nameका NFA 4 states वाली सरल linear structure रखता है
-
DFA conversion (Subset Construction)
- NFA के state sets के आधार पर deterministic DFA बनाया जाता है
- हर state एक NFA state set से मेल खाती है
Othersymbol जोड़कर अनावश्यक keys को कुशलता से skip किया जाता है- सरल query को NFA जैसी ही structure वाले DFA में बदला जाता है
-
DFS-आधारित traversal
- root से शुरू करके हर edge के साथ DFA transition किया जाता है
- transition न होने पर उस subtree को prune कर दिया जाता है
- DFA state accepting होने पर path और value को रिकॉर्ड किया जाता है
- हर node अधिकतम एक बार visit होता है, इसलिए पूरा traversal O(n) है
serde_json_borrowके कारण string copy किए बिना मूल buffer को reference किया जाता है
benchmark methodology
- Criterion.rs के साथ statistics-based benchmark किया गया
-
datasets
simple.json(106B),kubernetes-definitions.json(~992KB),kestra-0.19.0.json(~7.6MB),citylots.json(~190MB)
-
comparison tools
jsongrep,jsonpath-rust,jmespath,jaq,jql
-
benchmark groups
document_parse: JSON parsing speedquery_compile: query compile timequery_search: सिर्फ searchend_to_end: पूरा pipeline
-
fairness considerations
- zero-copy parsing के फ़ायदे को अलग से मापा गया
- DFA compile cost को अलग से मापा गया
- जिन tools में feature नहीं था, उन्हें उस test से बाहर रखा गया
- data duplication cost को अलग से संभाला गया
benchmark परिणाम
- document parsing time:
serde_json_borrowसबसे तेज़ है - query compile time:
jsongrepमें DFA generation के कारण सबसे अधिक cost आती है, जबकिjmespathकाफी तेज़ है - search time:
jsongrepसभी tools में सबसे तेज़ है - end-to-end performance: 190MB dataset पर भी
jq,jmespath,jsonpath-rust,jqlकी तुलना में काफ़ी ज़्यादा तेज़ - पूरे परिणाम live benchmark site पर देखे जा सकते हैं
license और उपयोग
- MIT license के तहत उपलब्ध open source software
- GitHub, Crates.io, Docs.rs पर उपलब्ध
- DFA-आधारित query engine को library form में दोबारा उपयोग किया जा सकता है, और इसे सीधे Rust projects में integrate किया जा सकता है
संदर्भ साहित्य
- Glushkov, V. M. (1961), The Abstract Theory of Automata
- Rabin, M. O., & Scott, D. (1959), Finite Automata and Their Decision Problems
3 टिप्पणियां
कमाल है।
| पाइप का निशान मुख्य लेख में अलग क्यों दिखता है? दिलचस्प है..
Hacker News की राय
jq का syntax बहुत पेचीदा है, इसलिए हर बार कोई साधारण JSON value निकालनी हो तब भी खोजकर देखना पड़ता है
मैं ज़्यादातर one-off filters लिखता हूँ, इसलिए पढ़ने से ज़्यादा समय लिखने में जाता है
शायद मेरा use case सरल है, या jq मेरे सोचने के तरीके से अच्छी तरह मेल खाता है
मैं ऐसी दुनिया का सपना देखता हूँ जहाँ सभी CLI tools JSON input/output करें और jq से जुड़े हों, लेकिन शायद आपके लिए वह एक बुरा सपना होगा
हर बार इस्तेमाल करते समय फिर से सीखना पड़ता है, इसलिए यह intuitive नहीं लगता
sed भी Turing complete है, लेकिन ज़्यादातर लोग उससे बस regex substitution जैसा काम ही करते हैं
मुझे jq पसंद है, लेकिन कभी-कभी मैं खुद अपनी पुरानी query भी समझ नहीं पाता था
celq ज़्यादा परिचित CEL language का इस्तेमाल करता है
यह बस JavaScript से JSON handle करने का तरीका है, और हैरानी की बात है कि यह jq से तेज़ है
इसे
$ cat package.json | dq 'Object.keys(data).slice(0, 5)'जैसे इस्तेमाल किया जाता हैमैंने Clojure सीखने के बाद अब JSON की जगह EDN इस्तेमाल करना शुरू कर दिया है
यह ज़्यादा संक्षिप्त, पढ़ने में आसान और structurally handle करना आसान है
आजकल मैं data handle करने के लिए borkdude/jet या babashka, और visualization के लिए djblue/portal इस्तेमाल करता हूँ
jq के जटिल operators पर इतना ज़ोर देने की वजह समझ नहीं आती
मैं performance को अहम मानता हूँ, लेकिन nanosecond स्तर की तुलना दिखावटी performance जैसी लगती है
ज़्यादातर मामलों में जो tool अभी इस्तेमाल हो रहा है, वही काफ़ी है
उदाहरण के लिए, मैं बड़े files में ही grep की जगह rg इस्तेमाल करता हूँ
2ms और 0.2ms का फर्क मामूली लग सकता है, लेकिन TB स्तर के streams process करने वालों के लिए यह अहम है
hardware तेज़ हुआ है, लेकिन software उल्टा और धीमा होता जा रहा है
optimization से बचना आलस और कल्पना की कमी जैसा लगता है
network latency से तेज़ होने के आधार पर निश्चिंत हो जाना एक बहाने जैसा सुनाई देता है
अगर JSON बहुत बड़ा है, तो JSON की जगह binary format इस्तेमाल करना चाहिए
अगर CLI में बहुत complex pipelines बनानी पड़ रही हैं, तो बेहतर है कि सीधा एक program ही लिख लिया जाए
कई नए CLI tools “ज़्यादा तेज़” होने की बात करते हैं, लेकिन असल में jq मुझे शायद ही कभी धीमा लगा है
jq से सिर्फ field names बदलने जैसा simple काम भी बहुत धीमा पड़ता है, इसलिए मैं सीधे Node या Rust scripts लिखकर process करता हूँ
hyperscaler environments में कई TB logs सीधे डाउनलोड करके analyze किए जाते हैं
monitoring resolution के हिसाब से performance का फर्क महसूस हो सकता है
कुछ ही features implement करके benchmark में जीत का दावा किया जाता है
यह project भी उसी ‘subset तेज़ है’ ट्रेंड का हिस्सा लगता है
उसके बाद हर चीज़ धीमी लगने लगती है
ripgrep जैसा तेज़ tool एक बार इस्तेमाल कर लिया, तो फिर पीछे लौटना मुश्किल होता है
मैंने jq और yq दोनों इस्तेमाल किए हैं, लेकिन yq काफ़ी धीमा होने पर भी कभी शिकायत नहीं हुई
jq से तेज़ tool हो तो बढ़िया है, लेकिन उसकी ज़रूरत सिर्फ कुछ खास users को होगी
फिर भी optimization से प्यार करने वाले इंसान के तौर पर मैं सम्मान व्यक्त करता हूँ
ETL चरण में इसमें अच्छा-खासा समय लगता है
पेज पहली बार खोलने पर light mode के colors टूटे हुए थे
dark mode में स्विच करके वापस आने पर समस्या ठीक हो गई
मैं correctness की वजह से Jaq पर चला गया
कहा जाता है कि इसकी performance भी jq से बेहतर है
लगता है jq के धीमे होने की प्रतिष्ठा distro packaging की समस्या की वजह से बनी है
काम के दौरान मैं अक्सर newline-delimited JSON(jsonl) handle करता हूँ
हर line एक पूरा JSON object होती है, इसलिए जिज्ञासा है कि बड़े CLI tools इस format को support करते हैं या नहीं
मैंने jq, mlr, htmlq, xsv, yq जैसे कई data-processing CLI tools इस्तेमाल किए हैं,
लेकिन Nushell मिलने के बाद वे सब effectively replace हो गए
एक ही syntax में सभी formats को handle करना एक ताज़गी भरा अनुभव था
सहकर्मियों के साथ काम करते समय ही jq, yq, mlr साथ में इस्तेमाल करता हूँ
autocomplete setup और command discoverability में थोड़ी असुविधा है, लेकिन oh-my-zsh से बहुत बेहतर है
अगर इसमें type annotations को मजबूरी से लागू करना, static binary compile करना, और TUI library जैसी चीज़ें आ जाएँ, तो छोटे apps लिखने के लिए भी इसे इस्तेमाल करूँगा
बढ़िया tool है! बस benchmark visualization थोड़ी कमज़ोर लगी
सभी tools एक ही रंग में हैं, इसलिए jsongrep कहाँ है यह ढूँढना मुश्किल है
jq खुद graph में नहीं था, इसलिए और भ्रम हुआ
xLarge file 190MiB की है, जो काफ़ी छोटी लगती है; मैं अक्सर 400MiB~1GiB JSON handle करता हूँ
अगर इससे बड़े public JSON documents हों, तो बताइए अच्छा रहेगा
benchmark visualization कुछ कच्ची-सी लगी
colors या shapes का इस्तेमाल करके और dimensions दिखाए जा सकते हैं
नतीजा समझने के लिए file paths सीधे पढ़ने पड़ें, यह असुविधाजनक है