3 poin oleh GN⁺ 2023-11-30 | 1 komentar | Bagikan ke WhatsApp
  • jaq adalah klon dari alat pemrosesan data JSON jq, yang bertujuan menjadi implementasi yang lebih dapat diprediksi sambil tetap kompatibel dengan jq dalam sebagian besar kasus
  • Program command-line jaq dapat digunakan sebagai pengganti drop-in untuk jq, dan pustaka Rust jaq-core dapat mengompilasi dan menjalankan program jq di dalam program Rust
  • Menyediakan dukungan YAML, CBOR, TOML, dan XML yang tidak ada di jq; jaq-core aman digunakan di lingkungan multithread dan mendukung tipe data arbitrer di luar JSON
  • Dalam evaluasi performa, jaq-3.0 menjadi yang tercepat pada 20 dari 31 benchmark, sementara jq-1.8.1 tercepat pada 5 benchmark dan gojq-0.12.18 pada 6 benchmark
  • Dari sisi keamanan, jaq berupaya menjamin pencegahan panic, memory safety, serta pembatasan I/O untuk data input dan filter jq, tetapi tidak menangani kehabisan resource seperti waktu, memori, dan stack

Hal yang Disediakan jaq

  • jaq adalah klon dari alat pemrosesan data JSON jq, dilafalkan /ʒaːk/ seperti Jacques
  • Memiliki dukungan format data yang tidak ada di jq
    • YAML

    • CBOR

    • TOML

      • XML
      • Memiliki manual terpisah dan dapat dicoba di playground
      • jaq tersedia dalam dua bentuk
      • Program command-line jaq: dapat digunakan sebagai pengganti drop-in untuk jq
      • Pustaka jaq-core: dapat mengompilasi dan menjalankan program jq di dalam program Rust

Tujuan Desain

  • Akurasi

    • jaq bertujuan menjadi implementasi jq yang lebih akurat dan dapat diprediksi, sambil tetap mempertahankan kompatibilitas dengan jq dalam sebagian besar kasus
  • Performa

    • jaq awalnya dibuat karena waktu startup jq 1.6 yang panjang terasa merepotkan; di lingkungan tersebut, waktu startup sekitar 50 ms
    • Waktu startup ini sangat terasa saat memproses banyak file kecil
    • Waktu startup sudah jauh membaik di jq 1.7, tetapi jaq masih lebih cepat daripada jq dalam berbagai benchmark
  • Kesederhanaan

    • jaq mengarah pada implementasi yang sederhana dan kecil untuk mengurangi kemungkinan bug serta memudahkan kontribusi

Instalasi dan Build

  • Binary untuk Linux, Mac, dan Windows tersedia di releases page
  • Di macOS atau Linux, jaq dapat diinstal dengan homebrew
    • brew install jaq
    • brew install --HEAD jaq
  • Untuk build dari source, diperlukan toolchain Rust
  • Jika sudah mengkloning repository, jaq dapat di-build atau diinstal dengan cargo build --release atau cargo install --locked --path jaq
  • jaq seharusnya berjalan di semua sistem yang didukung Rust; jika tidak, pengguna diminta untuk membuka issue

Evaluasi Performa

  • Evaluasi performa terdiri dari beberapa benchmark yang membandingkan jaq, jq, dan gojq
  • Benchmark empty mengukur waktu startup dengan menjalankan filter empty sebanyak n kali pada input null
  • Benchmark bf-fib menjalankan skrip Brainfuck yang menghasilkan bilangan Fibonacci menggunakan interpreter Brainfuck yang ditulis dalam jq
  • Data benchmark dibuat di sistem Linux dengan AMD Ryzen 5 5500U
    • Perintah yang digunakan adalah bench.sh target/release/jaq jq-1.8.1 gojq-0.12.17 | tee bench.json
    • Tabel menampilkan hasil jaq-3.0, jq-1.8.1, dan gojq-0.12.18 dalam satuan milidetik
    • N/A berarti terjadi error atau melebihi 10 detik
  • Ringkasan hasil
    • jaq-3.0 menjadi yang tercepat pada 20 benchmark
    • jq-1.8.1 menjadi yang tercepat pada 5 benchmark
    • gojq-0.12.18 menjadi yang tercepat pada 6 benchmark
  • gojq jauh lebih cepat pada tree-flatten karena mengimplementasikan filter flatten secara native, bukan sebagai definisi

Model Keamanan dan Batasan

  • jaq berupaya menjamin hal-hal berikut
    • Tidak terjadi panic, kecuali dalam kasus kehabisan resource
    • Memory safety yang tidak merusak memori
    • Kecuali pembacaan file sebelum menjalankan filter jq, data input dan filter jq tidak dapat memulai operasi I/O
  • Jika jaminan ini dilanggar, hal tersebut dianggap sebagai bug dan layak dilaporkan
  • jaq tidak menangani kehabisan resource dalam bentuk apa pun
    • Waktu eksekusi dapat menjadi panjang tanpa batas
    • Memori dapat digunakan tanpa batas
    • Ruang stack dapat digunakan tanpa batas
  • Sebagai contoh, stack overflow dapat terjadi saat membaca data input atau menjalankan filter jq
    • jaq -nr 'repeat("[")' | jaq
    • jaq -n 'def f: 1+f; f'

Audit dan Pengujian

  • jaq core diaudit oleh Radically Open Security sebagai bagian dari dua grant NLnet
  • Audit keamanan pertama dan kedua menemukan issue dengan tingkat keparahan sedang atau rendah
  • Semua issue dari audit keamanan telah ditangani, dan beberapa target fuzzing untuk jaq ditambahkan ke jaq-core/fuzz
  • Parser JSON jaq, hifijson, juga sudah memiliki target fuzzing
  • jaq memiliki suite pengujian yang terdiri dari lebih dari 500 pengujian

Kasus Pengguna

  • Seorang pengguna menilai jaq sangat membantu dibanding mengimplementasikan dukungan jq sendiri, dan berkat ekstensibilitas melalui trait ValT, ia dapat dengan mudah menambahkan dukungan jq untuk tipe miliknya sendiri
  • Pengguna lain menyatakan bahwa program Rust yang memakai jaq dapat menjalankan semua query terhadap seluruh file tiga kali, sementara crate PyPI jq untuk Python dan loop Python baru menjalankan satu query terhadap seluruh file
  • Dalam kasus interpreter wsjq, jaq dinilai jauh lebih cepat daripada implementasi jq lain, dan penekanannya pada akurasi dianggap mengesankan
    • Pada benchmark wsjq tersebut, jaq 5–10 kali lebih cepat daripada jq dan 15–196 kali lebih cepat daripada gojq
  • Pengguna yang memproses data certificate transparency log dengan certstream-server mengalami masalah pada pemrosesan pipeline jq, lalu setelah beralih ke jaq, dapat mengejar laju data bahkan di VM berspesifikasi rendah berkat waktu startup yang lebih cepat

Pendanaan

1 komentar

 
GN⁺ 2023-11-30
Komentar Hacker News
  • Mengingat pengembangan jq sempat berhenti selama 5 tahun lalu baru belakangan hidup lagi, tidak aneh kalau laporan bug—baik bug lama yang sudah dikenal maupun bug baru—menumpuk selama itu
    Sekarang sepertinya mereka akan mulai mendapatkan kembali kecepatannya dan perlahan membereskan daftar masalah yang belum terselesaikan yang sudah lama menumpuk

  • Saya suka pendekatan README yang juga memperkenalkan proyek-proyek serupa atau yang terinspirasi olehnya, bukan hanya alternatif pengganti
    Dari README proyek ini saya jadi tahu tentang https://github.com/yamafaktory/jql, dan saya berterima kasih karena ini alat yang sudah lama saya cari
    Bukan bermaksud meremehkan JAQ, tapi sintaks bergaya JQ terlalu sulit dipahami, jadi jql lebih cocok untuk saya

    • Dari sudut pandang ini, gron juga bagus
      Ia meratakan JSON menjadi baris-baris berformat key-value sehingga cocok untuk pekerjaan stream sederhana seperti grep: https://github.com/tomnomnom/gron
    • Temuan yang menarik, saya berniat mencobanya
      Tapi saya sebenarnya mengharapkan pengalaman yang benar-benar mirip SQL. Saya tidak paham kenapa tidak sekalian meniru SQL saja agar bisa melakukan kueri seperti "SELECT * FROM $json WHERE x>1"
      Rasanya semua orang ingin membuat bahasa kueri simbolik mereka sendiri yang sulit dipahami, seolah sedang code golf. Saya berharap kita bisa meninggalkan sintaks Unix lama yang sangat ringkas tetapi tidak intuitif, dan bergerak lebih dekat ke gaya PowerShell
    • https://github.com/tidwall/jj juga layak dilihat
    • Saya cukup paham dengan ketidaknyamanan itu, tetapi setidaknya jql tidak terlihat seperti solusinya
      |={"b""d"=2, "c"} tampaknya bermakna seperti select(."b"."d" == 2 or ."c" != null) di jq, dan walaupun versi jq lebih panjang, rasanya lebih jelas
      Dalam praktiknya mungkin perlu .[] | select(...), tetapi jql mungkin juga punya asumsi serupa, dan saya tidak yakin contohnya lengkap, jadi itu tidak banyak memengaruhi kesimpulannya
    • Homoiconicity jql tampak cukup mirip Lisp
      Sepertinya mungkin juga untuk diterapkan ke dirinya sendiri atau menggunakan semacam “macro”
  • Saya suka ide jq, tetapi karena tidak sering memakainya, setiap kali ingin melakukan sesuatu saya harus mencari sintaksnya di manual
    Sayangnya, 99% hal yang saya lakukan dengan jq hanyalah | jq .

    • Saya mengalami masalah yang sama
      Secara terpisah saya mulai membuat bahasa konfigurasi, lalu ternyata cukup bagus juga untuk kueri JSON: https://docs.ruuda.nl/rcl/rcl_query/
      Contoh yang tidak bisa saya selesaikan dengan jq tetapi bisa dengan RCL ada di sini: https://fosstodon.org/@ruuda/111120049523534027
    • Karena masalah yang sama, saya tidak bisa benar-benar memanfaatkan kekuatan jq, dan dalam kasus seperti ini Copilot sangat membantu
      Jika Anda memberikan tugas yang dibutuhkan bersama sampel JSON sumber yang sudah diperkecil, ia akan membuatkan skrip jq yang benar
      Untuk kebutuhan yang kompleks, sering kali lebih mudah dan lebih andal mengarahkan Copilot sedikit demi sedikit lewat iterasi daripada menjelaskan semuanya dengan tepat sekaligus. Selama proses iterasi itu, kadang muncul juga ide yang lebih baik daripada gagasan awal
      Mungkin ChatGPT atau alat lain juga bekerja dengan cara serupa
    • Belakangan ini saya mendapatkan sintaks jq yang saya butuhkan dengan cepat lewat ChatGPT: https://chat.openai.com/share/40b68d73-d2dd-412d-867f-9f375e...
  • https://github.com/01mf02/jaq/blob/main/Cargo.lock
    Dependensinya cukup banyak

    • Kalau dibandingkan dengan gojq, memang jauh lebih banyak: https://github.com/itchyny/gojq/blob/main/go.mod
    • Saya penasaran bagaimana situasi seperti ini biasanya berjalan di ekosistem Rust
      Kalau dependensinya banyak, seiring waktu tampaknya risiko ketidakcocokan mendasar antar dependensi juga makin besar, dan pemeliharaannya sepertinya akan menjadi pekerjaan besar
      Misalnya, apakah dua tahun lagi masih akan bisa dikompilasi dengan baik
  • jq adalah alat yang sangat kuat, tetapi belakangan ini DuckDB juga banyak digunakan
    Jika data sampai batas tertentu berbentuk tabel, SQL adalah bahasa yang jauh lebih alami

    • Dulu saya pernah mencoba Retool, dan ada fitur “Query JSON with SQL” yang cukup praktis: https://docs.retool.com/queries/guides/sql/query-json
      Agak mirip dengan LINQ di C#, tetapi saya lebih menyukainya karena SQL lebih terstandarisasi
      Akan sangat bagus jika di dalam bahasa pemrograman kita bisa melakukan kueri terhadap koleksi primitif dengan SQL, dan lebih baik lagi jika koleksi itu bisa disimpan secara transparan ke Sqlite
      Setiap kali melihat kode yang setelah mengambil data dari database lalu hanya diproses sederhana dengan loop atau stream API, rasanya selalu sayang. Untuk kegunaan seperti ini, SQL jauh lebih tingkat tinggi dan ringkas dibanding Java/Kotlin/Python/JavaScript
    • Rasanya mirip
      Saya menyimpan seluruh output JSON asli ke dalam tabel sqlite, lalu membuat kolom virtual di sana dan menjalankan hasil select dalam loop shell
      Nested loop jadi terurai, dan karena saya bisa memeriksa serta menjalankan ulang record yang tepat di DB, kemungkinan untuk debugging jadi jauh lebih baik
      Saya sadar bahwa yang saya buat adalah DAG, dan saya selalu melanjutkan dari record terakhir yang berhasil diproses. Saya penasaran apakah ada alat mirip Make untuk mengekspresikan ini
      Make tidak punya target SQL, dan pemroses DAG penuh seperti Airflow terlalu berat untuk sekadar merangkai potongan shell
    • Betul. Untuk data relasional dengan skema ketat, SQL memang jauh lebih baik
      Meski begitu, cara mengekspresikan kueri rekursif secara ringkas di SQL masih sulit didapat
    • Untuk penggunaan ini, secara pribadi saya lebih suka textql. Model mentalnya lebih sederhana
      https://github.com/dinedal/textql
  • Dari sudut pandang akurasi, saya penasaran apakah angka uint64 bisa ditampilkan tanpa terpotong
    Ini bagian yang paling mengganggu dari jq saat ini

    • Sayangnya, jika angka JSON dianggap sebagai bilangan floating-point 64-bit, maka kalau mengikuti standar memang harus diperlakukan seperti itu dan presisi bilangan bulatnya menjadi 53 bit
      Namun, spesifikasi terbaru RFC 8259 mengoreksi hal ini dengan menyatakan bahwa ia hanya menetapkan bentuk teks angka dan tidak menentukan semantiknya
      Dalam praktiknya, kebanyakan implementasi memperlakukan JSON sebagai subset JavaScript, sehingga muncul asumsi bahwa angkanya adalah floating-point 64-bit
    • Setahu saya ini sudah membaik di jq 1.7: https://github.com/jqlang/jq/releases/tag/jq-1.7
      Tertulis bahwa ia memakai literal angka desimal untuk mempertahankan presisi, dan operasi perbandingan menghormati presisi, tetapi operasi aritmetika bisa tetap terpotong
    • jq 1.7 mempertahankan bilangan bulat besar, tetapi begitu ada operasi yang dilakukan padanya, nilainya terpotong
      Saat ini pemotongannya ke decimal64 agak membingungkan, tetapi di rilis berikutnya rencananya akan diperbaiki agar memotong ke binary64(double) sesuai usulan spesifikasi JSON: https://github.com/jqlang/jq/pull/2949
  • Sejak beralih ke jless, saya tidak pernah menoleh ke belakang
    Antarmuka penggunanya jauh melampaui yang lain

    • Ini bukan kategori yang sama
      jq bukan sekadar viewer, melainkan pemroses bahasa kueri JSON
  • Lucu memang bahwa ada pustaka line art terminal di suatu tempat dalam ekosistem Rust, tetapi ketika saya mencoba menjalankan jaq, ia malah membanjiri iTerm dengan escape code hingga ukuran megabyte dan akhirnya iTerm mencoba mencetaknya ke printer
    Terlalu pintar untuk kebaikannya sendiri
    Dalam konteks seperti echo *json | rush -- jaq -rf ./this-program.jq {} | datamash ..., menurut saya tidak tepat jika mencoba menambahkan efek artistik ke TTY
    Bagaimanapun juga, penyebab errornya adalah jaq tidak memiliki strftime

  • Kesan pertama saya adalah ia punya pesan error yang bagus, tetapi tidak punya halt_error/0
    Setelah saya mengomentari halt_error, hasilnya lebih lambat daripada jq dan gojq
    Pada input yang sama, jq memerlukan sekitar 0,023 detik, gojq sekitar 0,070 detik, dan jaq sekitar 0,103 detik
    aoc22-13.jq yang dipakai ada di https://pastebin.com/raw/YiUjEu2n dan input.txt ada di https://pastebin.com/raw/X0FSyTNf

  • Saya mulai memakai yq sebagai pengganti jq, dan penasaran apakah ada perbedaan penting

    • Tergantung yq yang mana
      Secara pribadi saya lebih memilih https://github.com/mikefarah/yq daripada https://github.com/kislyuk/yq
    • jq terasa seperti alat yang jauh lebih kokoh dibanding yq
      Saya paham bahwa memproses YAML jauh lebih sulit daripada JSON, tetapi yq mengubah sintaksnya dari versi 3 ke 4 agar lebih mirip jq, namun entah kenapa tetap tidak sepenuhnya sama
      Selain itu, yq tidak punya if-then-else, sehingga terkesan seperti desain yang kurang baik atau ada yang terlewat: https://github.com/mikefarah/yq/issues/95
      Saat perlu memproses YAML, yq bekerja dengan baik dan juga cukup bagus dalam menangani komentar, tetapi untuk pemrosesan JSON murni, jq adalah alat yang lebih baik