Как стать автором
Обновить

Комментарии 26

Вот тут


where { get("arr") int (0) > 5 }

не совсем понятно, что значит int (0) > 5. Нашел только


int(name: String)
int(idx: String)

а здесь, похоже, число. Кстати было бы совсем хорошо, если бы язык был описан (пусть только в рамках статьи) через формальную грамматику, с правилами вывода (тем более, что язык не очень сложный). Тогда бы и вопросов таких не возникало

Этот код возвращает вложенный объект «arr»:
get("arr")

Так как это тоже нода, для нее применяются все те же хелперы, например, получили нулевой элемент-число объекта «arr» и проверили что он больше пяти:
get("arr").int(0) > 5

Однако Kotlin позволяет опционально опускать точку и получить ровно тоже самое:
get("arr")int(0) > 0

Насчет того что в документации idx должно быть числом, да, вы правы, уже исправил.
Формальная грамматика Kotlin доступна здесь: kotlinlang.org/docs/reference/grammar.html
такое ощущение, что это более компактный sql ))
Такое ощущуение, что это очередной 1005000-ый sql))) Когда ж им надоест?

Просто для сравнения можете привести код, эквивалентный примеру с jq?

У них не получится, первое действие в jq было выбрать ключ tickets тут такого пока нет) Хотя почитал комменты, видимо аналог это действия get(«tickets»)
ниже привел пример
Легко! Вот так
cat file.ndjson | analyze 'where{text("assigned_displayname")=="Матвей"} select{int("id")+"\t" +text("title")+"\t"+text("description")}'

Или так
cat file.ndjson | analyze 'where{text("assigned_displayname")=="Матвей"} select{"${int("id")}\t${text("title")}\t${text("description")}"}'
Все еще пропущен первый get tickets и есть мелкие ошибки) Но вижу) Правда не очень понятно зачем text(), но видимо это подсказка преобразователю. В целом не плохо, если нравится mysql. Я же mysql не люблю, поэтому предпочту более краткий синтаксис без слов select/where. Впрочем взять с гитхаба и сократить их в символы, например $_?, всегда можно, но вряд ли захочу.

И тут id это не инт, а текст. По формату json что-то в " это текст, даже если это целое-положительное. Поэтому лучше распознание такого куска добавить по умолчанию, т.е. все в кавычках это текст, а числа без них это числа с плавающей точкой. Всякие 0xf можно опустить XD тогда ваш код сократится до вменяемого

analyze 'get("tickets") where {"assigned_displayname"=="Матвей"} select {"${"id"} ${"title"}\t${"description"}"}'


Если еще убрать необходимость писать все ключи в кавычках, то начнет напоминать jsqry где выбрать ключ это просто написать его, отфильтровать это написать фильтр в [квадратных скобках], а преобразовать вывод или вашим языком select пишется в {фигурных скобках}.
text(«field») отдаст нам объект String из jvm + будут достпны все его методы.
obj(«field») отдаст нам просто json-ноду jackson + будут доступны ее методы.
Действительно, в таком примере первый select/.map выглядят избыточно. Однако только до тех пор пока мы выбираем лишь одну ноду из родительского json. Представьте что вам нужно взять не только tickets а еще и пару его соседей, к примеру объект security_info. С kq это так:

.map{get("tickets") to get("security_info")} 
// сделали пару из билета и секьюрных данных


Можно создать почти любой композитный объект из jvm.
Это прикольно, но с помощью fx (js) это тоже можно сделать, {...this.tickets, ...this.security_info}. Не сказать что это часто нужно и зачастую это свидетельство о том что структуру json придумывал человек редко пользующийся js, но это можно сделать.

А про text и obj, вы не очень поняли. В json по синтаксису сразу понятно где текст, где число, где массив, где хешмап. Поэтому лучше по умолчанию делать такие преобразования. А если как в моем примере id в json это строка, то можно уже преобразовывать явно. Т.е. видим «123» или «foo» это строка по умолчанию, так как взято в кавычки, но первое мы можем явно преобразовать к, например, int. Это я к тому, что в самом json всегда явно указаны типы, поэтому указывать их всякий раз при парсинге нет необходимости. Ну и да, cheatography.com/gaston/cheat-sheets/json в помощь, хотя там далеко не все что в современном json есть. Т.е. строки могут быть в одинарных кавычках, числа начинаться с 0x, 0o, 0b и т.д., ключи объектов далеко не всегда берут в кавычки и в крайних случаях может дойти до того, что все это объекты)
Да, хорошая идея сделать неявную типизацию
Возможно я покажусь ретроградом — но зачем простую утилиту упаковывать в docker?
Изначально делал ее для запуска в k8s поде. + Чтобы не грузить мануал установкой jdk.
может тогда взять GraalVM и завернуть в небольшой самодостаточный бинарник?
Да, идея хорошая, надо попробовать
Весь день проводил эксперименты с graalvm native-image. Результаты неутешительные, при сборке в нативный файл теряются некие зависимости и в результате при старте приложение ругается что не может обнаружить класс KotlinJsr223DefaultScriptEngineFactory, чего нет с self-executable jar. Так что пока слишком сырая технология.
Советую взглянуть на такое решение.
github.com/borkdude/jet/blob/master/doc/query.md

Единственное «НО»- язык запросов clojure.
Как следствие этого мощные возможности по работе с данными, но необходимость минимально погрузится (к слову сказать погружение должно пройти быстро и легко).
Зато получаем ЛЕГКИЙ, ПРОСТОЙ и МОЩНЫЙ язык для запросов и трансформаций.

Простой пример
$ echo '{:a 1 :b 2 :c 3}' | jet --query '(select-keys [:a :b])'
{:a 1, :b 2}
$ echo '{:a {:b 1}}' | jet --query '[:a :b]'
1


Пример использования одного из JQ example (https://stedolan.github.io/jq/tutorial/)
$ curl -s 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | \
jet --from json --keywordize --to edn --pretty --query '
(map
   {:message [:commit :message]
    :name [:commit :committer :name]
    :parents [:parents (map :html_url)]})'
Окей так как в примере идет мой код, написанный на коленке за пять минут вставлю свои пять копеек.

1) Что там в примере.
Это не очень дописанный баш скрипт для работы с api ubugtrack.com цель достать тикеты на меня. Полная версия
bug () {
        local bug
        bug=$(curl https://ubugtrack.com/api/$UBUGTRACK_API/project/3758/tickets/\?filter_status\=1 | jq '.tickets | map(select(.assigned_displayname=="Матвей"))' | jq '.[] | "\(.id) \(.title) \t \(.description)\n"' | sed -e 's/"\(.*\)"/\1/' -e 's/\\t/\t/' | fzf --delimiter='\t' --preview='echo -e {2}' --with-nth=1 | sed 's/^\([0-9]\+\).*/\1/')
        if [ -n "$bug" ]
        then
                echo "You selected bug $bug"
        fi
}


В ней например Ид проекта сейчас зашито константой, да и не делает этот скриптик ничего нужного. Может потом сделаю из него создание веток ну или хотя бы сообщений к коммитам.

2) В чем проблема.
В том что xkcd.ru/927 вы просто создали еще одну вещь со своими правилами. Для меня jq просто первое что пришло на ум когда в баше захотелось работать с json. Если бы действительно припекло я бы использовал ноду.

3) Что хочется.
Не очередной велосипед со своим синтаксисом, и не бойлерплейт код для парсинга json из ноды. Я хорошо знаю синтаксис js. Основной его минус при быстрой работе с json это то, что для обектов вида {5: {}, 20: {}} аналоги map достаточно многословны, например через Object.entries(json).map(([key, value]) => /* code */) ну или хотя бы Object.values. С этим приходится мириться, но это приятней чем изучить еще один синтаксис ради синтаксиса.

4) Что есть.
habr.com/ru/post/525808 Более приятный и близкий к js синтаксис, с явной примесью scala. Плюсом то, что прочитав примеры я понимаю что они делают, в отличии от написанного собой же кода на jq. Минус это все же немного другой синтаксис, хоть может он даже и лучше.
habr.com/ru/post/347808 То о чем я говорил, js сейчас знает куча программистов, и хоть он и подрос, он все же скриптовый язык, подходящий для написания коротких скриптов.

5) Я все же хочу написать свой велосипед.
Флаг в руки) Это полезно и развивает. Однако к моему примеру вряд ли ваш велосипед подойдет, ибо первое что я делаю это беру из пришедшего {tickets: [{...},...], ...} только tickets. И сходу не заметил у вас этого. Ну и во вторых я вряд ли буду использовать для скриптов строгую типизацию. Разве что если она действительно даст огромный выигрыш в затратах ресурсов. Хотя конечно для файлов размером с гигабайт есть vim, sed, grep, head, tail, и вроде больше в моем арсенале для них ничего. Сомневаюсь что ваш или приведенные мною парсеры на ура с ними справятся)

6) Бонусом как выглядел бы мой пример если бы я не поленился загуглить названия утилит, про существование которых знал
#Оригинал
jq '.tickets | map(select(.assigned_displayname=="Матвей"))' | jq '.[] | "\(.id) \(.title) \t \(.description)\n"'

#fx
fx 'this.tickets.filter(t => t.assigned_displayname == "Матвей").map(t => `${t.id} ${t.title} \t ${t.description}`)'
# для fx мне не потребовался дополнительный пайп, и до кучи я смог написать всю строчку без подглядывания в документацию

#jsqry
jsqry 'tickets [_.assigned_displayname == "Матвей"] {`${_.id} ${_.title} \t ${_.description}`}'
# тут пришлось чуть больше смотреть на примеры, но все же это самый краткий вариант


Еще один бонус fx в том что можно вернуть не только json, что может существенно сократить последующие преобразования. Для jsqry нашел лишь те способы преобразования что идут из коробки. А ну да jsqry поддерживает срезы в стиле Python [-2:] что тоже прикольно.
для написания скриптов на js есть zx github.com/google/zx
Крута, но все же первое что приходит на ум это баш) Да и его приходится знать чтобы читать чужие скрипты) Проблема json в том, что awk/grep с ним не очень хорошо работают.
Наверное я оставлю эту ссылку здесь. coderoad.ru/1955505/%D0%A0%D0%B0%D0%B7%D0%B1%D0%BE%D1%80-JSON-%D1%81-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E-%D0%B8%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%BE%D0%B2-Unix В ответах много чего, вплоть до того что например в powershell проблемы с json нет)
Тот случай, когда статья на хабре больше кодовой базы проекта =)

Поздравляю, вы изобрели AQL

А что, если просто использовать powershell?

PS C:\Projects\Temp> Get-Content 'test.ndjson' | ConvertFrom-Json | where {$_.size -eq '245'}

4880123 245 True

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории