Comments 16
Нода как runtime-среда для сервера? Спасибо, не надо. Т.е. я ценю попытку, и будь это более системный язык (C/C++/Rust/Go/perl/python/awk/bash), оно бы прокатило. Делать node системной ради одного query — нет, спасибо, не надо.
… компилятор qjsc джаваскрипта в исполняемый файл
Это не совсем компилятор, т.е. производительность всё равно будет в десятки раз меньше чем V8 после jit, для больших JSON (или много мелких) запросов не особо-то разгуляешься.
А в остальном тулза хорошая получилась, гораздо приятней (и понятней) чем jq.
Что стараетесь держать размер исполняемого файла небольшим это круто, моё почтение! Но как по мне потребление RAM и CPU важнее. Можете проделать какие-нибудь бенчмарки в сравнении с jshon и jq?
В общем сделал замеры сам. Сравниваю с jq 1.4 (потому что последующие релизы 1.5 и 1.6 гораздо медленнее) и jshon.
Тесты с маленьким файлом:
# cat sample2
[{"n": "bob", "v": 100}, {"n": "jim", "v": 101}]
# hyperfine 'jshon -a -e n < sample2' "jq_1.4/jq '.[].n' < sample2" "jsqry 'n' < sample2"
Benchmark 1: jshon -a -e n < sample2
Time (mean ± σ): 0.4 ms ± 0.1 ms [User: 0.4 ms, System: 0.1 ms]
Range (min … max): 0.3 ms … 1.2 ms 1701 runs
Benchmark 2: jq_1.4/jq '.[].n' < sample2
Time (mean ± σ): 0.9 ms ± 0.2 ms [User: 0.8 ms, System: 0.1 ms]
Range (min … max): 0.6 ms … 1.8 ms 1261 runs
Benchmark 3: jsqry 'n' < sample2
Time (mean ± σ): 1.2 ms ± 0.2 ms [User: 1.0 ms, System: 0.2 ms]
Range (min … max): 1.0 ms … 2.4 ms 1312 runs
Summary
'jshon -a -e n < sample2' ran
2.04 ± 0.74 times faster than 'jq_1.4/jq '.[].n' < sample2'
2.89 ± 0.93 times faster than 'jsqry 'n' < sample2'
# /usr/bin/time --format='%M maxresident KiB' jshon -a -e n < sample2 >/dev/null
1568 maxresident KiB
# /usr/bin/time --format='%M maxresident KiB' jq_1.4/jq '.[].n' < sample2 >/dev/null
2180 maxresident KiB
# /usr/bin/time --format='%M maxresident KiB' jsqry 'n' < sample2 >/dev/null
2840 maxresident KiB
Тесты на большом файле:
#almost 2MB testfile parse speed
TESTFILE=/usr/lib/python3/dist-packages/botocore/data/ec2/2016-11-15/service-2.json hyperfine \
'jshon -e operations -e WithdrawByoipCidr < $TESTFILE' \
'jq_1.4/jq .operations.WithdrawByoipCidr < $TESTFILE' \
'jsqry .operations.WithdrawByoipCidr < $TESTFILE'
Benchmark 1: jshon -e operations -e WithdrawByoipCidr < $TESTFILE
Time (mean ± σ): 44.2 ms ± 2.8 ms [User: 38.9 ms, System: 5.1 ms]
Range (min … max): 41.3 ms … 62.0 ms 63 runs
Benchmark 2: jq_1.4/jq .operations.WithdrawByoipCidr < $TESTFILE
Time (mean ± σ): 46.9 ms ± 3.0 ms [User: 42.0 ms, System: 4.7 ms]
Range (min … max): 43.7 ms … 58.6 ms 63 runs
Benchmark 3: jsqry .operations.WithdrawByoipCidr < $TESTFILE
Time (mean ± σ): 990.5 ms ± 27.5 ms [User: 949.9 ms, System: 37.1 ms]
Range (min … max): 947.6 ms … 1046.4 ms 10 runs
Summary
'jshon -e operations -e WithdrawByoipCidr < $TESTFILE' ran
1.06 ± 0.09 times faster than 'jq_1.4/jq .operations.WithdrawByoipCidr < $TESTFILE'
22.39 ± 1.54 times faster than 'jsqry .operations.WithdrawByoipCidr < $TESTFILE'
#almost 2MB testfile RAM usage
TESTFILE=/usr/lib/python3/dist-packages/botocore/data/ec2/2016-11-15/service-2.json
# /usr/bin/time --format='%M maxresident KiB' jshon -e operations -e WithdrawByoipCidr < $TESTFILE >/dev/null
10120 maxresident KiB
# /usr/bin/time --format='%M maxresident KiB' jq_1.4/jq .operations.WithdrawByoipCidr < $TESTFILE >/dev/null
8964 maxresident KiB
# /usr/bin/time --format='%M maxresident KiB' jsqry .operations.WithdrawByoipCidr < $TESTFILE >/dev/null
64684 maxresident KiB
Итого вердикт: для небольших JSON jsqry ведёт себя достойно, хоть и несколько прожорливее аналогов, а вот на больших данных использование CPU и RAM на порядок больше по сравнению с аналогами.
Было бы хорошо, если бы вы привели примеры использования jq и вашей утилиты на подобных вещах: "Отфильтровать элементы больше 2, добавить к каждому 100, отсортировать по убыванию и взять 2 последних элемента."
Чтобы предметно увидеть преимущества и простоту вашего DSL по сравнению с jq.
Недавно убирал DSL на базе jq из конфигурационных файлов некоторого сервиса, где с помощью него делалась фильтрация и мэппинг данных из одной структуры в другую. Это, возможно гибко и мощно, но абсолютно нечитаемо.
$ echo '[1,2,3,4,5]' | jq 'map(select(.>2)) | map(.+100) | sort_by(-.) | .[-2:]'
[
104,
103
]
$ echo '[1,2,3,4,5]' | jsqry '[_>2] {_+100} s(-_) [-2:]'
[
104,
103
]
Тут больше сравнительных примеров: gist.github.com/xonixx/d6066e83ec0773df248141440b18e8e4
На мой взгляд у jq наркоманский синтаксис, но у вас получилось сделать ещё более наркоманский и менее понятный синтаксис
На самом деле синтаксис простой, если понять строительные блоки, коих немного. Это:
field1.field2.field3
— доступ по полям[ expression ]
— фильтрация{ expression }
— трансформация (map)[ from:to:step ]
— срезы в стиле Pythons( expression )
— сортировка
Где expression
— выражение с "_" которое под капотом соответствует JS функции function(_) { return expression }
. Например, фильтр [ _ > 5 ]
соответствует предикату function(_) { return _ > 5 }
. Все, этого знания достаточно чтоб написать 90% типовых запросов.
а на 64bit win не получается собрать?
Write-Host '[1,2,3,4,5]' | ConvertFrom-Json -Depth 99 | Where-Object {$_ -gt 2} | ForEach-Object{$_+100} | Select-Object -Last 2
Или короче:
echo '[1,2,3,4,5]' | ConvertFrom-Json -Depth 99 | ?{$_ -gt 2} | %{$_+100} | select -Last 2
Замер скорости работы этого примера на случайном linux-сервере:
jq(time, real):~ 7мс
jsqry(time, real):~ 5мс
pwsh(Measure-Command):~ 4мс
(НО! Первый старт pwsh был ~50мс, последующие быстрее, даже если менять предикаты или кол-во входных элементов)
jsqry — лучше, чем jq