Jaq, bản sao jq tập trung vào độ chính xác, tốc độ và sự đơn giản
(github.com/01mf02)- jaq là một bản sao của công cụ xử lý dữ liệu JSON
jq, hướng tới một triển khai dễ dự đoán hơn trong khi vẫn duy trì khả năng tương thích vớijqtrong hầu hết trường hợp - Chương trình dòng lệnh
jaqcó thể dùng như một thay thế thả-vào-là-chạy chojq, còn thư viện Rustjaq-corecó thể biên dịch và chạy các chương trình jq bên trong chương trình Rust - Cung cấp hỗ trợ YAML, CBOR, TOML, XML mà
jqkhông có;jaq-corecó thể dùng an toàn trong môi trường đa luồng và hỗ trợ kiểu dữ liệu tùy ý vượt ra ngoài JSON - Trong đánh giá hiệu năng,
jaq-3.0nhanh nhất ở 20 trong 31 benchmark,jq-1.8.1nhanh nhất ở 5 benchmark, còngojq-0.12.18nhanh nhất ở 6 benchmark - Về bảo mật, jaq cố gắng bảo đảm chống panic, an toàn bộ nhớ, và giới hạn I/O của dữ liệu đầu vào cũng như bộ lọc jq, nhưng không xử lý cạn kiệt tài nguyên như thời gian, bộ nhớ hay stack
jaq cung cấp những gì
- jaq là một bản sao của công cụ xử lý dữ liệu JSON
jq, phát âm là/ʒaːk/, giống Jacques - Có hỗ trợ các định dạng dữ liệu mà
jqkhông có-
YAML
-
CBOR
-
TOML
- XML
- Có manual riêng và có thể dùng thử trên playground
- jaq được cung cấp dưới hai dạng
- Chương trình dòng lệnh
jaq: có thể dùng như một thay thế thả-vào-là-chạy chojq - Thư viện
jaq-core: có thể biên dịch và chạy các chương trình jq bên trong chương trình Rust
-
Mục tiêu thiết kế
-
Độ chính xác
- jaq hướng tới một triển khai jq chính xác và dễ dự đoán hơn, trong khi vẫn duy trì khả năng tương thích với
jqtrong hầu hết trường hợp
- jaq hướng tới một triển khai jq chính xác và dễ dự đoán hơn, trong khi vẫn duy trì khả năng tương thích với
-
Hiệu năng
- jaq ban đầu được tạo ra vì thời gian khởi động dài của
jq 1.6gây bất tiện; trong môi trường đó, thời gian khởi động khoảng 50ms - Thời gian khởi động này đặc biệt nổi bật khi xử lý nhiều tệp nhỏ
- Trong
jq 1.7, thời gian khởi động đã được cải thiện đáng kể, nhưng jaq vẫn nhanh hơnjqtrong nhiều benchmark
- jaq ban đầu được tạo ra vì thời gian khởi động dài của
-
Sự đơn giản
- jaq hướng tới một triển khai đơn giản và nhỏ gọn để giảm khả năng phát sinh lỗi và giúp việc đóng góp dễ dàng hơn
Cài đặt và build
- Có thể tải binary cho Linux, Mac và Windows từ trang releases
- Trên macOS hoặc Linux, có thể cài đặt bằng homebrew
brew install jaqbrew install --HEAD jaq
- Để build từ mã nguồn, cần có Rust toolchain
cargo install --locked jaqcargo install --locked --git https://github.com/01mf02/jaq
- Nếu đã clone repository, có thể build hoặc cài đặt bằng
cargo build --releasehoặccargo install --locked --path jaq - jaq được kỳ vọng hoạt động trên mọi hệ thống mà Rust hỗ trợ; nếu không, dự án hướng dẫn hãy mở issue
Đánh giá hiệu năng
- Đánh giá hiệu năng gồm nhiều benchmark so sánh jaq, jq và gojq
- Benchmark
emptyđo thời gian khởi động bằng cách chạy bộ lọcemptynlần với đầu vào null - Benchmark
bf-fibchạy một script Brainfuck tạo số Fibonacci bằng trình thông dịch Brainfuck viết bằng jq - Dữ liệu benchmark được tạo trên hệ thống Linux dùng AMD Ryzen 5 5500U
- Lệnh được dùng là
bench.sh target/release/jaq jq-1.8.1 gojq-0.12.17 | tee bench.json - Bảng hiển thị kết quả của
jaq-3.0,jq-1.8.1,gojq-0.12.18theo mili giây N/Anghĩa là lỗi hoặc vượt quá 10 giây
- Lệnh được dùng là
- Tóm tắt kết quả
jaq-3.0nhanh nhất ở 20 benchmarkjq-1.8.1nhanh nhất ở 5 benchmarkgojq-0.12.18nhanh nhất ở 6 benchmark
gojqnhanh hơn nhiều ởtree-flattenvì triển khai bộ lọcflattentheo kiểu native thay vì dưới dạng định nghĩa
Mô hình bảo mật và giới hạn
- jaq cố gắng bảo đảm những điều sau
- Không xảy ra panic, ngoại trừ các trường hợp cạn kiệt tài nguyên
- An toàn bộ nhớ, không làm hỏng bộ nhớ
- Ngoại trừ việc đọc tệp trước khi chạy bộ lọc jq, dữ liệu đầu vào và bộ lọc jq không thể khởi động thao tác I/O
- Trường hợp các bảo đảm này bị phá vỡ được xem là lỗi và nên được báo cáo
- jaq không xử lý bất kỳ loại cạn kiệt tài nguyên nào
- Thời gian chạy có thể kéo dài không giới hạn
- Có thể dùng bộ nhớ không giới hạn
- Có thể dùng không gian stack không giới hạn
- Ví dụ, tràn stack có thể xảy ra khi đọc dữ liệu đầu vào hoặc chạy bộ lọc jq
jaq -nr 'repeat("[")' | jaqjaq -n 'def f: 1+f; f'
Audit và kiểm thử
- jaq core đã được Radically Open Security audit như một phần của hai khoản tài trợ NLnet
- Trong các đợt audit bảo mật thứ nhất và thứ hai, các vấn đề có mức độ nghiêm trọng trung bình hoặc thấp đã được phát hiện
- Tất cả vấn đề trong audit bảo mật đã được xử lý, và nhiều mục tiêu fuzzing cho jaq đã được thêm vào
jaq-core/fuzz - Parser JSON của jaq, hifijson, cũng đã có các mục tiêu fuzzing từ trước
- jaq có bộ test gồm hơn 500 bài kiểm thử
Trường hợp người dùng
- Một người dùng đánh giá rằng
jaqđã giúp ích rất nhiều so với việc tự triển khai hỗ trợjq, và nhờ khả năng mở rộng thông qua traitValT, họ có thể dễ dàng thêm hỗ trợ jq cho kiểu riêng của mình - Một người dùng khác cho biết chương trình Rust dùng jaq có thể chạy ba lần toàn bộ truy vấn trên toàn bộ tệp, trong khi Python
jqPyPI crate và vòng lặp Python mới chạy một truy vấn trên toàn bộ tệp - Trong trường hợp trình thông dịch
wsjq, có đánh giá rằng jaq nhanh hơn đáng kể so với các triển khai jq khác và điểm nhấn về độ chính xác rất ấn tượng- Trong benchmark
wsjqđó, jaq nhanh hơn jq 5–10 lần và nhanh hơn gojq 15–196 lần
- Trong benchmark
- Một người dùng xử lý dữ liệu certificate transparency log bằng
certstream-servergặp vấn đề với xử lý pipelinejq; sau khi chuyển sang jaq, nhờ thời gian khởi động nhanh hơn, họ có thể theo kịp ngay cả trên VM cấu hình thấp
Tài trợ
- Dự án jaq được hỗ trợ thông qua các quỹ NGI0 Entrust và NGI0 Commons do NLnet thành lập
- Hỗ trợ tài chính được cung cấp bởi chương trình Next Generation Internet của European Commission
- Khoản tài trợ bổ sung được cung cấp bởi Swiss State Secretariat for Education, Research and Innovation
1 bình luận
Ý kiến trên Hacker News
Xét việc quá trình phát triển jq đã dừng trong 5 năm rồi gần đây mới sống lại, thì không lạ khi trong thời gian đó các báo cáo, dù là lỗi đã biết hay lỗi mới, đã tích tụ lại
Giờ có lẽ dự án sẽ lấy lại tốc độ và từ từ xử lý danh sách tồn đọng đã chất đống lâu nay
Thích cách README giới thiệu kèm những dự án tương tự hoặc được truyền cảm hứng, chứ không phải là các dự án thay thế
Nhờ README của dự án này mà biết đến https://github.com/yamafaktory/jql, đúng là công cụ đã tìm lâu nay nên rất cảm ơn
Không có ý hạ thấp JAQ, nhưng cú pháp kiểu JQ quá khó hiểu nên jql hợp với tôi hơn
Nó làm phẳng JSON thành các dòng dạng khóa-giá trị, giúp phù hợp với các thao tác stream đơn giản như
grep: https://github.com/tomnomnom/gronTuy nhiên tôi đã kỳ vọng một trải nghiệm thật sự giống SQL. Không hiểu vì sao không đơn giản sao chép SQL để có thể truy vấn kiểu
"SELECT * FROM $json WHERE x>1"Có vẻ ai cũng muốn tạo ra ngôn ngữ truy vấn ký hiệu khó hiểu của riêng mình như đang chơi code golf. Mong là thoát khỏi kiểu cú pháp Unix cũ cực ngắn nhưng không hiển nhiên, và tiến gần hơn tới cách làm của PowerShell
|={"b""d"=2, "c"}có vẻ mang nghĩa giốngselect(."b"."d" == 2 or ."c" != null)của jq, nhưng dù jq dài hơn, tôi thấy nó rõ ràng hơnThực tế có lẽ cần
.[] | select(...), nhưng jql cũng có thể có giả định tương tự; tôi không chắc ví dụ có đầy đủ không nên điều đó không ảnh hưởng nhiều đến kết luậnCó vẻ cũng có thể áp dụng lên chính nó hoặc dùng “macro”
Tôi thích ý tưởng của jq, nhưng vì không dùng thường xuyên nên mỗi lần muốn làm gì đó lại phải tra cú pháp trong tài liệu
Đáng tiếc là 99% việc tôi làm với jq là
| jq .Riêng chuyện khác, tôi bắt đầu tạo một ngôn ngữ cấu hình, rồi hóa ra nó cũng khá ổn cho truy vấn JSON: https://docs.ruuda.nl/rcl/rcl_query/
Ở đây có một ví dụ mà tôi không giải được bằng jq nhưng giải được bằng RCL: https://fosstodon.org/@ruuda/111120049523534027
Chỉ cần đưa yêu cầu và một mẫu JSON nguồn đã rút gọn, nó sẽ tạo đúng script jq cần thiết
Với yêu cầu phức tạp, thay vì cố mô tả chính xác ngay từ đầu, việc lặp dần với Copilot để dẫn nó tới lời giải sẽ dễ và ổn định hơn. Trong quá trình lặp, đôi khi còn nảy ra ý tưởng tốt hơn ban đầu
ChatGPT hay các công cụ khác có lẽ cũng hoạt động tương tự
https://github.com/01mf02/jaq/blob/main/Cargo.lock
Có khá nhiều phụ thuộc
Khi có nhiều phụ thuộc, theo thời gian có vẻ rủi ro chúng trở nên không tương thích căn bản với nhau sẽ tăng lên, và việc bảo trì có lẽ sẽ thành chuyện lớn
Ví dụ không biết 2 năm nữa có còn biên dịch tốt không
jq là một công cụ rất mạnh, nhưng gần đây DuckDB cũng được dùng nhiều
Nếu dữ liệu ở mức nào đó có dạng bảng, SQL là ngôn ngữ tự nhiên hơn nhiều
Nó hơi giống LINQ của C#, nhưng SQL được chuẩn hóa hơn nên tôi thích hơn
Nếu có thể truy vấn các collection thô bằng SQL ngay trong ngôn ngữ thì sẽ rất tuyệt, và xa hơn nữa, nếu có thể lưu collection một cách trong suốt vào Sqlite thì còn tốt hơn
Mỗi khi thấy mã lấy dữ liệu từ cơ sở dữ liệu rồi xử lý đơn giản bằng vòng lặp hoặc stream API, tôi luôn thấy tiếc. Với các mục đích như vậy, SQL ở mức trừu tượng cao hơn và súc tích hơn nhiều so với Java/Kotlin/Python/JavaScript
Tôi lưu toàn bộ đầu ra JSON gốc vào bảng sqlite, rồi tạo cột ảo ở đó và đưa kết quả
selectchạy qua vòng lặp shellCác vòng lặp lồng nhau được tháo gỡ, và khả năng debug tốt hơn nhiều vì có thể kiểm tra đúng bản ghi trong DB rồi chạy lại
Tôi nhận ra thứ mình đang tạo là một DAG, và luôn bắt đầu lại từ bản ghi được xử lý thành công gần nhất. Tôi tò mò liệu có công cụ nào giống
Makeđể biểu diễn việc này khôngMake không có target SQL, còn các bộ xử lý DAG đầy đủ như Airflow thì quá nặng để ghép các đoạn shell
Dù vậy, cách biểu diễn truy vấn đệ quy một cách ngắn gọn trong SQL vẫn là thứ khó có được
https://github.com/dinedal/textql
Xét về độ chính xác, tôi tò mò liệu nó có thể hiển thị số uint64 mà không bị cắt hay không
Đây hiện là điểm khó chịu nhất của tôi với jq
Tuy nhiên, đặc tả mới nhất là RFC 8259 đã đính chính rằng nó chỉ quy định dạng văn bản của số, chứ không quy định ngữ nghĩa
Trên thực tế, phần lớn implementation coi JSON như một tập con của JavaScript, dẫn đến giả định rằng số là số thực dấu phẩy động 64-bit
Có nói rằng nó dùng literal số thập phân để giữ độ chính xác, và các phép so sánh tôn trọng độ chính xác, nhưng phép toán số học vẫn có thể bị cắt
Hiện tại nó cắt về decimal64, hơi gây nhầm lẫn, nhưng bản phát hành tiếp theo dự kiến sẽ sửa để cắt về binary64(double) theo khuyến nghị của đặc tả JSON: https://github.com/jqlang/jq/pull/2949
Từ khi chuyển sang jless thì tôi không nhìn lại nữa
Giao diện người dùng vượt xa những công cụ khác
jq không chỉ là một trình xem đơn thuần, mà là bộ xử lý ngôn ngữ truy vấn JSON
Việc đâu đó trong Rust có thư viện vẽ line art trên terminal thì khá dễ thương, nhưng khi chạy jaq, nó xả hàng megabyte escape code vào iTerm, cuối cùng iTerm còn cố in ra máy in
Nó đã tỏ ra quá thông minh
Trong ngữ cảnh như
echo *json | rush -- jaq -rf ./this-program.jq {} | datamash ..., tôi không nghĩ việc cố đưa hiệu ứng nghệ thuật vào TTY là phù hợpDù sao thì nguyên nhân lỗi là
jaqkhông cóstrftimeẤn tượng đầu tiên là có thông báo lỗi đẹp, nhưng không có
halt_error/0Sau khi comment
halt_errorđi, nó chậm hơn jq và gojqVới cùng đầu vào,
jqmất khoảng 0,023 giây,gojqkhoảng 0,070 giây, cònjaqkhoảng 0,103 giâyFile
aoc22-13.jqđược dùng là https://pastebin.com/raw/YiUjEu2n, còninput.txtlà https://pastebin.com/raw/X0FSyTNfTôi bắt đầu dùng yq thay cho jq, và tò mò liệu có khác biệt quan trọng nào không
Cá nhân tôi thích https://github.com/mikefarah/yq hơn https://github.com/kislyuk/yq
Tôi hiểu rằng xử lý YAML khó hơn JSON rất nhiều, nhưng dù yq đã đổi cú pháp từ phiên bản 3 sang 4 để gần với jq hơn, bằng cách nào đó nó vẫn không hoàn toàn giống
Ngoài ra yq không có if-then-else, nên trông như thiết kế chưa tốt hoặc bị bỏ sót: https://github.com/mikefarah/yq/issues/95
Khi cần xử lý YAML thì yq hoạt động tốt và cũng xử lý comment khá ổn, nhưng với xử lý JSON thuần túy thì jq là công cụ tốt hơn