|
| 1 | +# Примеры запросов JSONata |
| 2 | + |
| 3 | +Описание языка JSONata можно найти на [официальном сайте](https://docs.jsonata.org/overview). Также на этом сайте вы можете попробовать [сделать свои запросы](https://try.jsonata.org/). |
| 4 | + |
| 5 | +## Как отфильтровать данные в запросе |
| 6 | + |
| 7 | +### Как отфильтровать данные по ключу |
| 8 | + |
| 9 | +Задача: |
| 10 | +Требуется получить все системы одного бизнес-юнита "crocodile", т.е. задача сводится к тому, что нужно получить все системы у которых ключ массива содержит значение "swamp.crocodile". |
| 11 | + |
| 12 | +Пример данных: |
| 13 | +```yaml |
| 14 | +components: |
| 15 | + swamp.crocodile.sid: |
| 16 | + title: S.ID # Название компоненты |
| 17 | + entity: system # Сущность компонента из PlantUML (https://plantuml.com/ru/deployment-diagram) |
| 18 | + system_status: production |
| 19 | + short_description: Общекорпоративный сервис SSO (Single Sign-On) |
| 20 | + description: Сервис SSO (Single Sign-On). Реализована возможность авторизации через OAuth 2 и OpenID Connect. |
| 21 | + business_owners: |
| 22 | + - Крокодил Гена |
| 23 | + - Шапокляк |
| 24 | + application_owner: Чебурашка |
| 25 | + budget_holder: Малыш |
| 26 | + architect: Карлсон |
| 27 | + critical_level: business_critical #administrative/business_operational/business_critical/mission_critical |
| 28 | + system_category: business_app #channel_app/business_app/ext_business_app/it_app/ext_it_app |
| 29 | + technologies: # Используемые технологии |
| 30 | + - Django |
| 31 | + - Python |
| 32 | + - Faust |
| 33 | + - PostgreSQL |
| 34 | + - Redis |
| 35 | + - Kafka |
| 36 | + - Nginx |
| 37 | + |
| 38 | +``` |
| 39 | + |
| 40 | +Варианты решения: |
| 41 | + |
| 42 | +#### Вариант 1 |
| 43 | + |
| 44 | +Для того чтобы отфильтровать данные по ключу можно использовать функцию $sift(). Данная функция позволяет по дефолту фильтровать данные по ключу. Описание функции можно найти [здесь](https://docs.jsonata.org/higher-order-functions#sift). |
| 45 | + |
| 46 | +При этом в работе функции есть нюансы: |
| 47 | +1. Если у вас статический параметр фильтрации, то вы можете в функцию просто передать параметр жестко указав его в функции или через переменную. |
| 48 | + |
| 49 | +``` |
| 50 | +( |
| 51 | + $matcher := /swamp\.crocodile\..*/; /*Задаем параметр фильтрации запроса*/ |
| 52 | + components.$sift(function($v, $k) {$k ~> $matcher}).$spread(). |
| 53 | + {"id": $keys()[0],"component": $.*} |
| 54 | +) |
| 55 | +``` |
| 56 | +2. Если у вас динамический параметр фильтрации, то предыдущий вариант работать не будет и нужно преобразовать параметр через функцию $eval(). |
| 57 | + |
| 58 | +``` |
| 59 | +( |
| 60 | + $bu_id := $params.id; /*Так должно быть в оригинале*/ |
| 61 | + $bu_id := "crocodile"; /*Для теста перебиваем значение*/ |
| 62 | + $pattern := $eval("/swamp\\." & $bu_id & "\\..*/"); |
| 63 | + components.$sift(function($v, $k) {$k ~> $pattern}).$spread(). |
| 64 | + {"id": $keys()[0],"component": $.*} |
| 65 | +) |
| 66 | +``` |
| 67 | + |
| 68 | +#### Вариант 2 |
| 69 | + |
| 70 | +Можно использовать второй вариант, который более универсальный. Он позволяет фильтроваться по любому полю и если мы хотим фильтроваться по ключу, то для этого нам нужно ключ добавить внутрь массива, а затем уже фильтроваться по этому полю. |
| 71 | + |
| 72 | +``` |
| 73 | +( |
| 74 | + /*Формируем строку поиска через регулярное выражение*/ |
| 75 | + $matcher := /swamp\.crocodile\..*/; |
| 76 | + /*Выбираем все системы*/ |
| 77 | + components.$spread(). /*$spread() используется в том случае, если мы хотим получть ключ через функцию $keys()[0]. Если не ходим, то $spread() можно не использовать*/ |
| 78 | + {"id": $keys()[0], /*Помещаем ключ внутрь запроса*/ |
| 79 | + "component": $.* |
| 80 | + }[$matcher(id)] /*Фильтруемся по ключу*/ |
| 81 | +) |
| 82 | +``` |
| 83 | + |
| 84 | +#### Отличия первого и второго вариантов |
| 85 | + |
| 86 | +Если у вас есть массив данных, который просто нужно отфильтровать, то проще всего использовать вариант 2. Если же у вас многослойная последовательная выборка, то вариант 2 вам может не подойти и тогда используйте вариант 1. |
| 87 | + |
| 88 | + |
| 89 | +### Как отфильтровать данные по значению внутри массива |
| 90 | + |
| 91 | +Иногда требуется отфильтровать данные по значению какого либо поля внутри массива. Например, в нашем примере мы ходим выбрать все критичные для бизнеса системы. В нашем случае мы хотим выбрать все системы где: |
| 92 | +`critical_level: business_critical`. |
| 93 | + |
| 94 | +В данном случае тоже существует два способа: |
| 95 | + |
| 96 | +#### Вариант 1 |
| 97 | + |
| 98 | +Для того чтобы отфильтровать данные по значению можно использовать функцию $filter(). Данная функция позволяет фильтровать данные по значению любого поля в массиве. Описание функции можно найти [здесь](https://docs.jsonata.org/higher-order-functions#filter). |
| 99 | + |
| 100 | +``` |
| 101 | +( |
| 102 | + $level := "business_critical"; |
| 103 | + components.$filter($.*, function($v, $i, $a) {$contains($v."critical_level", $level)}).( |
| 104 | + $ |
| 105 | + ); |
| 106 | +) |
| 107 | +``` |
| 108 | + |
| 109 | +#### Вариант 2 |
| 110 | + |
| 111 | +Это способ мы описали выше ["Как отфильтровать данные по ключу - Вариант 2"](#вариант-2). |
| 112 | + |
| 113 | +``` |
| 114 | +( |
| 115 | + /*Формируем строку поиска через регулярное выражение*/ |
| 116 | + $matcher := /business_critical/; |
| 117 | + /*Выбираем все системы*/ |
| 118 | + components.( |
| 119 | + $.* |
| 120 | + )[$matcher(critical_level)] /*Фильтруемся по полю critical_level*/ |
| 121 | +) |
| 122 | +``` |
| 123 | +#### Отличия первого и второго вариантов |
| 124 | + |
| 125 | +Если у вас есть массив данных, который просто нужно отфильтровать, то проще всего использовать вариант 2. Если же у вас многослойная последовательная выборка, то вариант 2 вам может не подойти и тогда используйте вариант 1. |
| 126 | + |
| 127 | + |
| 128 | +## Как получить список систем/сервисов с кастомным списком полей |
| 129 | + |
| 130 | +Задача: |
| 131 | +Требуется получить все системы/сервисы уровня ПА-L1. Конечный массив данных, должен соответствовать выводимой таблице. В списке должна быть ссылка на карточку системы. Также конечный список данных хотим отсортировать по идентификатору системы. |
| 132 | +``` |
| 133 | +( |
| 134 | + $level := "business_critical"; |
| 135 | + components.$filter($.*, function($v, $i, $a) {$contains($v."critical_level", $level)}).( |
| 136 | + $ |
| 137 | + ); |
| 138 | +) |
| 139 | +``` |
| 140 | + |
| 141 | +Контекст: |
| 142 | +Уровень ПА-L1 (общая прикладная архитектура) описывается системами/сервисами с entity="system", поэтому в качестве условия выборки должно быть данное условие. |
| 143 | + |
| 144 | +Варианты решения: |
| 145 | + |
| 146 | +``` |
| 147 | +( |
| 148 | + $distinct([components.$spread().( |
| 149 | + $COMPONENT_ID := $keys()[0]; |
| 150 | + $systems := $.*.{ |
| 151 | + "id": $COMPONENT_ID, |
| 152 | + "link_to_system": "/architect/components/" & $COMPONENT_ID, |
| 153 | + "title": title, |
| 154 | + "entity": entity, |
| 155 | + "description": description, |
| 156 | + "owner": owner |
| 157 | + }; |
| 158 | + $systems[entity="system"]; /*Устанавливаем фильтр*/ |
| 159 | + )])^(id) /*Сортируем по полю id*/ |
| 160 | +) |
| 161 | +``` |
| 162 | + |
| 163 | +## Как выбрать технологии использующиеся в системах |
| 164 | + |
| 165 | +Задача: |
| 166 | +Требуется получить список технологий использующихся в системах. Технологии не должны дублироваться. |
| 167 | + |
| 168 | +Контекст: |
| 169 | +Пример интересен тем, что внутри запроса показана проверки данных и подмены их дефолтными значениями. Также есть хитрый способ исключения дублирования данных. |
| 170 | + |
| 171 | +Варианты решения: |
| 172 | + |
| 173 | +``` |
| 174 | +( |
| 175 | + $MANIFEST := $; |
| 176 | + $distinct($distinct(components.*.technologies).( |
| 177 | + $TECHKEY := $; |
| 178 | + $TECHNOLOGY := $lookup($MANIFEST.technologies.items, $type($)="string" ? $ : undefined); |
| 179 | + $TECHNOLOGY := $TECHNOLOGY ? $merge([$TECHNOLOGY, {"id": $TECHKEY}]) : $single( |
| 180 | + $spread( |
| 181 | + $sift($MANIFEST.technologies.items, function($v, $k) { |
| 182 | + [$TECHKEY in $v.aliases]} |
| 183 | + ) |
| 184 | + ), function($v, $k){ $k=0 }).$merge([$.*, {"id": $keys($)}]); |
| 185 | + $TECHNOLOGY := $TECHNOLOGY ? $TECHNOLOGY : { |
| 186 | + "id": $TECHKEY, |
| 187 | + "section": "UNKNOWN", |
| 188 | + "title": "Не определено" |
| 189 | + }; |
| 190 | + $SECTION := $lookup($MANIFEST.technologies.sections, $TECHNOLOGY.section); |
| 191 | + { |
| 192 | + "label": $TECHNOLOGY.id, |
| 193 | + "key": $TECHNOLOGY.id, |
| 194 | + "hint": $TECHNOLOGY.title, |
| 195 | + "link": $TECHNOLOGY.link, |
| 196 | + "status": $TECHNOLOGY.status, |
| 197 | + "section" : { |
| 198 | + "key": $TECHNOLOGY.section, |
| 199 | + "title": $SECTION.title ? $SECTION.title : "Не определено" |
| 200 | + } |
| 201 | + } |
| 202 | + )) |
| 203 | +) |
| 204 | +``` |
| 205 | +## Как получить уникальные логины всех сотрудников |
| 206 | + |
| 207 | +Задача: |
| 208 | +Был написан REST API, который выдает всю информацию по учетным записям пользователей в формате yaml. Так как учетных записей более 6000 тысяч, а реально в DocHub мы используем менее 30, то встала задача выбрать все используемые учетные записи. Так как в самих полях сейчас бардак - кто-то указывает логины, а кто-то просто ФИО, поэтому нужно выбрать только логины и убрать все лишнее. |
| 209 | + |
| 210 | +Контекст: |
| 211 | +Пример системы: |
| 212 | +```yaml |
| 213 | +components: |
| 214 | + swamp.crocodile.spact: |
| 215 | + title: S.Pact # Название компонента |
| 216 | + entity: system # Сущность компонента из PlantUML (https://plantuml.com/ru/deployment-diagram) |
| 217 | + short_description: Сервис для создания и подписания договоров |
| 218 | + description: Сервис для создания и подписания договоров |
| 219 | + business_owners: |
| 220 | + - vupsen |
| 221 | + - Пупсень |
| 222 | + application_owner: k.korneech |
| 223 | + budget_holder: valentin |
| 224 | + critical_level: business_critical #administrative/business_operational/business_critical/mission_critical |
| 225 | + system_category: business_app #channel_app/business_app/ext_business_app/it_app/ext_it_app |
| 226 | + links: |
| 227 | + # Интеграции между системами одного БЮ |
| 228 | + - id: swamp.frog.1cbit_finance |
| 229 | + direction: <-- |
| 230 | + |
| 231 | +``` |
| 232 | +Выбирать будем business_owners, application_owner и budget_holder. |
| 233 | + |
| 234 | +Варианты решения: |
| 235 | + |
| 236 | +**Вариант 1** |
| 237 | +``` |
| 238 | +( |
| 239 | + $_manifest := $; |
| 240 | + /* Делаем регулярку на выбор только латинских букв */ |
| 241 | + $matcher := /^[a-z]/; |
| 242 | + /* Создаем массив со списком имен полей из которых будем выбирать логины */ |
| 243 | + $scan_fields := ["budget_holder", "application_owner", "business_owners"]; |
| 244 | + /* Выбираем системы */ |
| 245 | + $system_accounts := $distinct($_manifest.components.*.( |
| 246 | + $component := $; /* Получаем конкретную систему*/ |
| 247 | + $scan_fields.( |
| 248 | + $lookup($component, $); /* Внутри конкретной системы берем значение конкретного реквизиты для каждого поля указанного в $scan_fields*/ |
| 249 | + )[$matcher($)]; /*Каждое полученное значение свяряем с регуляркой и лишнее игнорируем*/ |
| 250 | + ))[$];/*В предыдущей выборке будут nill-ы, для того чтобы избавиться от них делаем фильтр*/ |
| 251 | +
|
| 252 | + /* Повторяем предыдущий алгоритм для версий архитектуры */ |
| 253 | + $arch_versions_accounts := $distinct($_manifest.arch_versions.*.сhanged_data.*.( |
| 254 | + $component := $; |
| 255 | + $scan_fields.( |
| 256 | + $lookup($component, $); |
| 257 | + )[$matcher($)]; |
| 258 | + ))[$]; |
| 259 | +
|
| 260 | + /*Соединяем два полученных выше массива*/ |
| 261 | + $all_accounts := $distinct($append($system_accounts, $arch_versions_accounts )); |
| 262 | +) |
| 263 | +``` |
| 264 | +**Вариант 2** |
| 265 | +Ниже описан более простой вариант реализации задачи. С одной стороны вариант более понятный и линейный, с другой менее масштабируемый, но полезно ознакомиться с разными подходами получения одного и того же результата. |
| 266 | +``` |
| 267 | +( |
| 268 | + $_manifest := $; |
| 269 | + $arch_version := $_manifest.arch_versions; |
| 270 | + $components := $_manifest.components; |
| 271 | +
|
| 272 | + $pattern := $eval("/^[a-z]/"); |
| 273 | + $sys_accounts := $distinct($components.*.( |
| 274 | + $account := $append($account, budget_holder); |
| 275 | + $account := $append($account, application_owner); |
| 276 | + $account := $append($account, $.business_owners); |
| 277 | + )); |
| 278 | +
|
| 279 | + $arch_versions_accounts := $distinct($arch_version.*.сhanged_data.*.( |
| 280 | + budget_holder; |
| 281 | + )); |
| 282 | + $all_accounts := $append($sys_accounts, $sys_accounts); |
| 283 | +
|
| 284 | + $accounts := $distinct($all_accounts.( |
| 285 | + $contains($, $pattern) = true |
| 286 | + ? $append($account, $); |
| 287 | + )); |
| 288 | +) |
| 289 | +``` |
0 commit comments