Jaq โคลนของ jq ที่เน้นความแม่นยำ ความเร็ว และความเรียบง่าย
(github.com/01mf02)- jaq เป็นโคลนของ
jqเครื่องมือประมวลผลข้อมูล JSON โดยตั้งเป้าให้เข้ากันได้กับjqในกรณีส่วนใหญ่ พร้อมมุ่งสู่การเป็นอิมพลีเมนเทชันที่แม่นยำและคาดเดาได้มากกว่า - โปรแกรมบรรทัดคำสั่ง
jaqสามารถใช้เป็น ตัวแทนแบบดรอปอิน ของjqได้ และไลบรารี Rustjaq-coreสามารถคอมไพล์และรันโปรแกรม jq ภายในโปรแกรม Rust ได้ - รองรับ YAML, CBOR, TOML และ XML ซึ่ง
jqไม่มี และjaq-coreก็สามารถใช้งานได้อย่างปลอดภัยในสภาพแวดล้อมมัลติเธรด พร้อมรองรับ ชนิดข้อมูลตามอำเภอใจ ที่นอกเหนือจาก JSON - ในการประเมินประสิทธิภาพ
jaq-3.0เร็วที่สุดใน 20 จาก 31 เบนช์มาร์ก,jq-1.8.1เร็วที่สุดใน 5 รายการ, และgojq-0.12.18เร็วที่สุดใน 6 รายการ - ในด้านความปลอดภัย พยายามรับประกันการป้องกัน panic, ความปลอดภัยของหน่วยความจำ, และการจำกัด I/O ของข้อมูลนำเข้าและ jq filter แต่ไม่รองรับ การใช้ทรัพยากรจนหมด เช่น เวลา หน่วยความจำ หรือสแตก
สิ่งที่ jaq มีให้
- jaq เป็นโคลนของ
jqเครื่องมือประมวลผลข้อมูล JSON และออกเสียงว่า/ʒaːk/เหมือน Jacques - รองรับรูปแบบข้อมูลที่
jqไม่มี-
YAML
-
CBOR
-
TOML
- XML
- มี manual แยกต่างหาก และสามารถลองใช้งานได้ใน playground
- jaq มีให้ใช้งานใน 2 รูปแบบ
- โปรแกรมบรรทัดคำสั่ง
jaq: ใช้เป็น ตัวแทนแบบดรอปอิน ของjqได้ - ไลบรารี
jaq-core: สามารถคอมไพล์และรันโปรแกรม jq ภายในโปรแกรม Rust ได้
-
เป้าหมายการออกแบบ
-
ความแม่นยำ
- jaq ตั้งเป้าเป็นอิมพลีเมนเทชัน jq ที่แม่นยำและคาดเดาได้มากขึ้น โดยยังคงความเข้ากันได้กับ
jqในกรณีส่วนใหญ่
- jaq ตั้งเป้าเป็นอิมพลีเมนเทชัน jq ที่แม่นยำและคาดเดาได้มากขึ้น โดยยังคงความเข้ากันได้กับ
-
ประสิทธิภาพ
- เดิมที jaq ถูกสร้างขึ้นเพราะเวลาเริ่มทำงานที่ยาวนานของ
jq 1.6ทำให้ใช้งานไม่สะดวก โดยในสภาพแวดล้อมนั้นเวลาเริ่มทำงานอยู่ที่ประมาณ 50ms - เวลาเริ่มทำงานนี้จะยิ่งเห็นชัดเมื่อประมวลผลไฟล์ขนาดเล็กจำนวนมาก
- แม้
jq 1.7จะปรับปรุงเวลาเริ่มทำงานได้อย่างมาก แต่ jaq ก็ยังเร็วกว่าjqในหลายเบนช์มาร์ก
- เดิมที jaq ถูกสร้างขึ้นเพราะเวลาเริ่มทำงานที่ยาวนานของ
-
ความเรียบง่าย
- jaq มุ่งสู่อิมพลีเมนเทชันที่เล็กและเรียบง่าย เพื่อลดโอกาสเกิดบั๊กและทำให้มีส่วนร่วมพัฒนาได้ง่าย
การติดตั้งและการบิลด์
- สามารถดาวน์โหลดไบนารีสำหรับ Linux, Mac และ Windows ได้จาก releases page
- บน macOS หรือ Linux สามารถติดตั้งผ่าน homebrew ได้
brew install jaqbrew install --HEAD jaq
- หากต้องการบิลด์จากซอร์ส จะต้องมี Rust toolchain
cargo install --locked jaqcargo install --locked --git https://github.com/01mf02/jaq
- หากโคลนรีโพซิทอรีมาแล้ว สามารถบิลด์หรือติดตั้งได้ด้วย
cargo build --releaseหรือcargo install --locked --path jaq - jaq ควรทำงานได้บนทุกระบบที่ Rust รองรับ และหากไม่เป็นเช่นนั้น ก็ขอให้เปิด issue
การประเมินประสิทธิภาพ
- การประเมินประสิทธิภาพประกอบด้วยหลายเบนช์มาร์กที่เปรียบเทียบ jaq, jq และ gojq
- เบนช์มาร์ก
emptyวัด เวลาเริ่มทำงาน โดยรันฟิลเตอร์emptyจำนวนnครั้งกับอินพุต null - เบนช์มาร์ก
bf-fibรันสคริปต์ Brainfuck ที่สร้างเลข Fibonacci ด้วยอินเทอร์พรีเตอร์ Brainfuck ที่เขียนด้วย jq - ข้อมูลเบนช์มาร์กสร้างขึ้นบนระบบ Linux ที่ใช้ AMD Ryzen 5 5500U
- คำสั่งที่ใช้คือ
bench.sh target/release/jaq jq-1.8.1 gojq-0.12.17 | tee bench.json - ตารางจะแสดงผลของ
jaq-3.0,jq-1.8.1,gojq-0.12.18ในหน่วยมิลลิวินาที N/Aหมายถึงเกิดข้อผิดพลาดหรือใช้เวลานานเกิน 10 วินาที
- คำสั่งที่ใช้คือ
- สรุปผล
jaq-3.0เร็วที่สุดใน 20 เบนช์มาร์กjq-1.8.1เร็วที่สุดใน 5 เบนช์มาร์กgojq-0.12.18เร็วที่สุดใน 6 เบนช์มาร์ก
gojqเร็วกว่ามากในtree-flattenเพราะอิมพลีเมนต์ฟิลเตอร์flattenแบบเนทีฟ แทนที่จะนิยามเป็นฟิลเตอร์
โมเดลความปลอดภัยและข้อจำกัด
- jaq พยายามรับประกันสิ่งต่อไปนี้
- ไม่เกิด panic ยกเว้นในกรณีทรัพยากรถูกใช้จนหมด
- ความปลอดภัยของหน่วยความจำ โดยไม่ทำให้หน่วยความจำเสียหาย
- ยกเว้นการอ่านไฟล์ก่อนรัน jq filter แล้ว ข้อมูลนำเข้าและ jq filter จะไม่สามารถเริ่มงาน I/O ได้
- หากการรับประกันเหล่านี้ถูกทำลาย จะถือเป็นบั๊กและควรรายงาน
- jaq ไม่ได้รองรับ การใช้ทรัพยากรจนหมด ไม่ว่าประเภทใดก็ตาม
- เวลาในการรันอาจยาวนานได้ไม่จำกัด
- อาจใช้หน่วยความจำได้ไม่จำกัด
- อาจใช้พื้นที่สแตกได้ไม่จำกัด
- ตัวอย่างเช่น อาจเกิด stack overflow ได้ระหว่างอ่านข้อมูลนำเข้าหรือรัน jq filter
jaq -nr 'repeat("[")' | jaqjaq -n 'def f: 1+f; f'
การตรวจสอบและการทดสอบ
- jaq core ได้รับการตรวจสอบโดย Radically Open Security ภายใต้ส่วนหนึ่งของเงินสนับสนุน NLnet สองครั้ง
- การตรวจสอบความปลอดภัย ครั้งแรก และครั้งที่สอง พบประเด็นที่มีความรุนแรงระดับกลางหรือต่ำ
- ประเด็นทั้งหมดจากการตรวจสอบความปลอดภัยได้รับการจัดการแล้ว และมีการเพิ่มเป้าหมายสำหรับการ fuzz หลายรายการให้กับ
jaq-core/fuzz - ตัวแยกวิเคราะห์ JSON ของ jaq อย่าง hifijson ก็มีเป้าหมาย fuzz อยู่แล้วเช่นกัน
- jaq มีชุดทดสอบที่ประกอบด้วย การทดสอบมากกว่า 500 รายการ
กรณีการใช้งานของผู้ใช้
- ผู้ใช้รายหนึ่งประเมินว่า
jaqช่วยได้มากกว่าการพยายามอิมพลีเมนต์การรองรับjqเองโดยตรง และด้วยความสามารถในการขยายผ่าน traitValTทำให้เพิ่มการรองรับ jq ให้กับชนิดข้อมูลของตนเองได้ง่าย - ผู้ใช้อีกรายระบุว่า โปรแกรม Rust ที่ใช้ jaq สามารถรันทุกคิวรีกับทั้งไฟล์ได้ สามรอบ ในช่วงเวลาที่ Python
jqPyPI crate พร้อมลูป Python รันได้เพียงหนึ่งคิวรีกับทั้งไฟล์ - ในกรณีของอินเทอร์พรีเตอร์
wsjqมีการประเมินว่า jaq เร็วกว่าอิมพลีเมนเทชัน jq อื่นอย่างมาก และความใส่ใจด้านความแม่นยำก็น่าประทับใจ- ในเบนช์มาร์ก
wsjqนั้น jaq เร็วกว่า jq 5–10 เท่า และเร็วกว่า gojq 15–196 เท่า
- ในเบนช์มาร์ก
- ผู้ใช้ที่ประมวลผลข้อมูล certificate transparency log ด้วย
certstream-serverพบปัญหากับการ pipe ผ่านjqและหลังเปลี่ยนมาใช้ jaq ก็สามารถตามงานได้ทันแม้บน VM สเปกต่ำ ด้วยเวลาเริ่มทำงานที่เร็วกว่า
เงินสนับสนุน
- โครงการ jaq ได้รับการสนับสนุนผ่านกองทุน NGI0 Entrust และ NGI0 Commons ที่จัดตั้งโดย NLnet
- เงินสนับสนุนทางการเงินมาจากโครงการ Next Generation Internet ของ European Commission
- เงินทุนเพิ่มเติมมาจาก Swiss State Secretariat for Education, Research and Innovation
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
เมื่อคำนึงว่า การพัฒนา jq หยุดไปนาน 5 ปี แล้วเพิ่งกลับมาฟื้นตัวเมื่อไม่นานมานี้ ก็ไม่น่าแปลกที่รายงานต่าง ๆ จะสะสมค้างอยู่ในช่วงนั้น ไม่ว่าจะเป็นบั๊กที่รู้จักอยู่แล้วหรือบั๊กใหม่
ตอนนี้น่าจะเริ่มกลับมาเดินหน้าได้เร็วขึ้น และค่อย ๆ จัดการ รายการค้างที่ยังไม่ปิด ซึ่งสะสมมานาน
ชอบแนวทางที่ README แนะนำโปรเจกต์ที่คล้ายกันหรือได้แรงบันดาลใจมา ซึ่งไม่ใช่ตัวทดแทนโดยตรงไว้ด้วยกัน
ได้รู้จัก https://github.com/yamafaktory/jql จาก README ของโปรเจกต์นี้ และรู้สึกขอบคุณ เพราะเป็นเครื่องมือที่ตามหามานาน
ไม่ได้ตั้งใจจะลดคุณค่าของ JAQ แต่ ไวยากรณ์แบบ JQ เข้าใจยากเกินไป สำหรับผม jql เหมาะกว่า
มันทำให้ JSON แบนลงเป็นบรรทัดรูปแบบคีย์-ค่า จึงเข้ากับงานสตรีมแบบเรียบง่ายอย่าง
grepได้ดี: https://github.com/tomnomnom/gronแต่จริง ๆ แล้วคาดหวัง ประสบการณ์แบบ SQL มากกว่า ไม่เข้าใจว่าทำไมไม่ลอก SQL มาเลย แล้วให้ query ได้แบบ
"SELECT * FROM $json WHERE x>1"ดูเหมือนทุกคนอยากสร้างภาษา query เชิงสัญลักษณ์ลึกลับของตัวเอง ราวกับกำลังแข่ง code golf กันอยู่ อยากให้หลุดจากไวยากรณ์แบบ Unix เก่า ๆ ที่สั้นสุดขีดแต่ไม่ชัดเจน แล้วขยับไปใกล้แนวทางของ PowerShell มากขึ้น
|={"b""d"=2, "c"}ดูเหมือนจะมีความหมายคล้ายselect(."b"."d" == 2 or ."c" != null)ของ jq ซึ่งฝั่ง jq ถึงจะยาวกว่า แต่รู้สึกชัดเจนกว่าในทางปฏิบัติคงต้องใช้
.[] | select(...)ด้วย แต่ jql ก็อาจมีสมมติฐานคล้ายกัน และไม่แน่ใจว่าตัวอย่างครบถ้วนไหม จึงไม่กระทบข้อสรุปมากนักดูเหมือนอาจนำไปใช้กับตัวมันเอง หรือใช้ “แมโคร” ได้ด้วย
ชอบไอเดียของ jq แต่เพราะไม่ได้ใช้บ่อย ทุกครั้งที่อยากทำอะไรสักอย่างก็ต้อง เปิดคู่มือหาไวยากรณ์
น่าเสียดายที่ 99% ของงานที่ทำด้วย jq คือ
| jq .แยกไปเริ่มทำภาษา config ขึ้นมาเอง แล้วพบว่ามันใช้ query JSON ได้ค่อนข้างดีด้วย: https://docs.ruuda.nl/rcl/rcl_query/
มีตัวอย่างที่แก้ด้วย jq ไม่ได้ แต่แก้ด้วย RCL ได้อยู่ที่นี่: https://fosstodon.org/@ruuda/111120049523534027
ถ้าให้ทั้งงานที่ต้องการทำและตัวอย่าง JSON ต้นฉบับแบบย่อ มันจะสร้างสคริปต์ jq ที่ถูกต้องให้
สำหรับข้อกำหนดที่ซับซ้อน การค่อย ๆ วนซ้ำกับ Copilot เพื่อพาไปสู่คำตอบมักง่ายและเสถียรกว่าการพยายามอธิบายให้ถูกต้องครบถ้วนในครั้งเดียว ระหว่างวนซ้ำ บางทียังนึกไอเดียที่ดีกว่าตอนแรกได้ด้วย
ChatGPT หรือเครื่องมืออื่น ๆ ก็น่าจะทำงานคล้ายกัน
https://github.com/01mf02/jaq/blob/main/Cargo.lock
มี dependency ค่อนข้างมาก
ถ้ามี dependency มาก ความเสี่ยงที่เวลาผ่านไปแล้วแต่ละตัวจะเข้ากันไม่ได้ในระดับพื้นฐานก็ดูสูงขึ้น และน่าจะกลายเป็นงานบำรุงรักษาใหญ่
เช่น อีก 2 ปีให้หลังยังจะคอมไพล์ ได้ดีอยู่ไหม
jq เป็นเครื่องมือที่ทรงพลังมาก แต่ช่วงนี้ DuckDB ก็ถูกใช้กันเยอะเช่นกัน
ถ้าข้อมูลมีลักษณะเป็นตารางอยู่บ้าง SQL จะเป็นภาษาที่เป็นธรรมชาติกว่ามาก
คล้ายกับ LINQ ของ C# อยู่บ้าง แต่ชอบ SQL มากกว่าเพราะเป็นมาตรฐานมากกว่า
ถ้าสามารถ query คอลเลกชันดิบในภาษาได้ด้วย SQL ก็คงยอดเยี่ยม และยิ่งกว่านั้นถ้าสามารถเก็บคอลเลกชันลง Sqlite ได้แบบโปร่งใสก็จะยิ่งดี
เวลาเห็นโค้ดที่ดึงข้อมูลจากฐานข้อมูลหรือแหล่งอื่น ๆ แล้วมาประมวลผลง่าย ๆ ด้วยลูปหรือ stream API มักรู้สึกเสียดายเสมอ สำหรับการใช้งานแบบนี้ SQL เป็นระดับสูงกว่าและกระชับกว่า Java/Kotlin/Python/JavaScript มาก
เก็บเอาต์พุต JSON ต้นฉบับทั้งหมดลงใน ตาราง sqlite จากนั้นสร้างคอลัมน์เสมือน แล้วเอาผลลัพธ์
selectไปวนในเชลล์ลูปลูปซ้อนถูกคลี่ออก และสามารถตรวจสอบเรคคอร์ดที่ถูกต้องใน DB แล้วรันซ้ำได้ ทำให้ดีบักได้ง่ายขึ้นมาก
เริ่มตระหนักว่าสิ่งที่กำลังทำอยู่คือ DAG และมักเริ่มใหม่จากเรคคอร์ดล่าสุดที่ประมวลผลสำเร็จเสมอ เลยสงสัยว่ามีเครื่องมือคล้าย
Makeที่ใช้แทนสิ่งนี้ได้ไหมMake ไม่มี target แบบ SQL และตัวประมวลผล DAG เต็มรูปแบบอย่าง Airflow ก็หนักเกินไปสำหรับการประกอบชิ้นส่วนเชลล์เล็ก ๆ
ถึงอย่างนั้น วิธีเขียน recursive query ใน SQL ให้กระชับก็ยังหาได้ยากอยู่ดี
https://github.com/dinedal/textql
ในแง่ความถูกต้อง อยากรู้ว่าสามารถแสดง ตัวเลข uint64 โดยไม่ถูกตัดได้หรือไม่
นี่เป็นจุดที่รบกวนใจที่สุดใน jq ตอนนี้
อย่างไรก็ตาม สเปกล่าสุดอย่าง RFC 8259 ได้แก้ความเข้าใจว่า ระบุเพียงรูปแบบข้อความของตัวเลขเท่านั้น ไม่ได้กำหนด semantics
ในทางปฏิบัติ implementation ส่วนใหญ่ถือว่า JSON เป็น subset ของ JavaScript จึงนำไปสู่สมมติฐานว่าตัวเลขเป็น floating point 64 บิต
ระบุว่าใช้ decimal number literal เพื่อรักษาความแม่นยำ และ comparison operator เคารพความแม่นยำ แต่ arithmetic operator อาจถูกตัดได้
ตอนนี้ตัดเป็น decimal64 เลยชวนสับสนเล็กน้อย แต่ใน release ถัดไปจะปรับให้ตัดเป็น binary64(double) ตามข้อเสนอของสเปก JSON: https://github.com/jqlang/jq/pull/2949
ตั้งแต่ย้ายไปใช้ jless ก็ไม่เคยมองย้อนกลับมา
user interface ล้ำหน้ากว่าตัวอื่นมาก
jq ไม่ใช่แค่ viewer ธรรมดา แต่เป็น ตัวประมวลผลภาษา query สำหรับ JSON
น่ารักดีที่ Rust มีไลบรารี terminal line art อยู่ที่ไหนสักแห่ง แต่พอลองรัน jaq แล้วมันเท escape code เป็นเมกะไบต์ใส่ iTerm จนสุดท้าย iTerm พยายามจะพิมพ์ออกเครื่องพิมพ์
มันฉลาดเกินไปหน่อย
ในบริบทอย่าง
echo *json | rush -- jaq -rf ./this-program.jq {} | datamash ...ผมว่าไม่เหมาะที่จะใส่เอฟเฟกต์ศิลป์ลงใน TTYสาเหตุของ error ไม่ว่าอย่างไรก็คือ
jaqไม่มีstrftimeความประทับใจแรกคือมี error message ที่สวย แต่ไม่มี
halt_error/0หลังจากคอมเมนต์
halt_errorออกแล้ว ก็ยังช้ากว่า jq และ gojqบน input เดียวกัน
jqใช้เวลาประมาณ 0.023 วินาที,gojqประมาณ 0.070 วินาที,jaqประมาณ 0.103 วินาทีaoc22-13.jqที่ใช้คือ https://pastebin.com/raw/YiUjEu2n และinput.txtคือ https://pastebin.com/raw/X0FSyTNfเริ่มใช้ yq แทน jq แล้ว อยากรู้ว่ามีความแตกต่างสำคัญอะไรไหม
ส่วนตัวชอบ https://github.com/mikefarah/yq มากกว่า https://github.com/kislyuk/yq
เข้าใจว่าการจัดการ YAML ยากกว่า JSON มาก แต่ถึง yq จะเปลี่ยน syntax จากเวอร์ชัน 3 ไป 4 ให้ใกล้กับ jq มากขึ้น ก็ยังรู้สึกว่าไม่เหมือนกันทั้งหมดอยู่ดี
อีกอย่าง yq ไม่มี if-then-else จึงดูเหมือนออกแบบไม่ดีหรือพลาดไป: https://github.com/mikefarah/yq/issues/95
เมื่อต้องจัดการ YAML yq ทำงานได้ดีและจัดการคอมเมนต์ได้ค่อนข้างดี แต่สำหรับการประมวลผล JSON ล้วน ๆ jq เป็นเครื่องมือที่ดีกว่า