Skip to content

Commit 5cea95b

Browse files
authored
Merge pull request #7 from ValentinKozlov/main
Примеры запросов JSONata
2 parents 1beaf55 + 668baec commit 5cea95b

16 files changed

Lines changed: 496 additions & 1 deletion

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
5. [Динамические контексты](src/DynamicContext)
1919
6. [Встраивание виджетов в представления стандартных сущностей](src/widgets)
2020
7. [Управление процессом развертывания систем в кластерах](src/deployment_units_management)
21+
8. [Примеры запросов JSONata](src/jsonata_query_examples)
2122

2223
## Разворачивание
2324

dochub.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ imports:
1212
# 6. Пример встраивание виджетов в представления Components и Aspects
1313
# - src/widgets/dochub.yaml
1414
# 7. Пример управления процессом развертывания систем в кластерах
15-
- src/deployment_units_management/dochub.yaml
15+
# - src/deployment_units_management/dochub.yaml
16+
# 8. Примеры запросов JSONata
17+
- src/jsonata_query_examples/dochub.yaml
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Примеры запросов JSONata
2+
3+
**Цель примера:** Снизить порог вхождения при изучении языка JSONata.
4+
5+
Основная идея в том, что хочется чтобы другие участники сообщества докидывали в этот пример, свои вариантов запросов. Такой подход позволил бы нам сформировать репозиторий основных конструкций языка и помог бы новичкам в процессе обучения.
6+
7+
# Суть примера
8+
На начальном этапе изучения языка JSONata я столкнулся с тем, что на официальном сайте по языку были очень простые примеры и для реальных задач их в большинстве случаев нельзя было переиспользовать. Если бы не помощь Ромы Пионтика, то разобраться самому у меня не было бы не единого шанса. Поэтому я решил делать небольшие примеры работы базовых конструкций языка для своих коллег и выложил их в DocHub.
9+
10+
## Логическое описание реализации и файловая структура примера
11+
Для того чтобы можно было пощупать результат все запросы выполняются на базе существующих в системе данных.
12+
* **systems** - в этой папке лежат все необходимые для примера данных файле была описана сама модель, реализован вывод в меню и несколько вариантов визуализации в виде отчетов. Все запросы переиспользуемые, так как они были добавлены в datasets/datasets.yaml
13+
* **jsonata_query_example.md** - в этом файле описаны все основные примеры
14+
15+
16+
## Использование
17+
В меню плагина DocHub выберите пункт Документы/Примеры запросов JSONata.
18+
19+
## Задания для практики
20+
* Изучите запросы и выполняйте их в инструменте JSONata встроенным в DocHub анализируя полученный результат.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
imports:
2+
- systems/_root.yaml
3+
- docs.yaml
4+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
imports:
2+
- _root.yaml
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
docs:
2+
jsonata_query_example:
3+
location: Примеры запросов JSONata
4+
description: Примеры запросов JSONata
5+
type: markdown
6+
source: jsonata_query_example.md
7+
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
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+
```
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
components:
2+
swamp.frog.1cbit_finance:
3+
title: 1С Бит.Финанс
4+
entity: system
5+
short_description: Функциональная подсистема автоматизирующая договорной учет
6+
description: Функциональная подсистема автоматизирующая договорной учет
7+
business_owners:
8+
- Кикимора-болотная
9+
application_owner: Лягушка
10+
critical_level: mission_critical #administrative/business_operational/business_critical/mission_critical
11+
system_category: business_app #channel_app/business_app/ext_business_app/it_app/ext_it_app
12+
links:
13+
# Cвязи с системами
14+
- id: swamp.crocodile.spact
15+
direction: <--
16+
- id: swamp.frog.spoll
17+
direction: <--
18+
- id: swamp.crocodile.crm
19+
direction: <--

0 commit comments

Comments
 (0)