Слияние (дерево)
Компонент к выбранному узлу основного дерева присоединяет данные из другого дерева. Комбинация настроек компонента позволяет:
- включить весь корневой узел присоединяемого дерева как дочерний в выбранный узел основного;
- включить подчиненные элементы корневого узла присоединяемого дерева как дочерние в выбранный узел основного;
- добавить корневой контейнер присоединяемого дерева в выбранный массив основного дерева в качестве нового элемента массива;
- добавить элементы корневого массива присоединяемого дерева в выбранный массив основного дерева в качестве новых элементов.
Один из возможных кейсов применения компонента — объединение данных из разных источников/систем/веб-сервисов в единую иерархическую структуру, решая такие задачи как обогащение данных и/или консолидация источников.
Порты
Вход
Основное дерево — порт для подключения дерева, в которое добавляются данные. Обязательный.
Присоединяемое дерево — порт для подключения дерева, данные которого присоединяются к основному. Обязательный.
Выход
Выходное дерево — результат слияния деревьев согласно выбранным настройкам.
Мастер настройки
Настройка компонента включает два этапа: Настройку слияния деревьев и Настройку соответствия между деревьями данных.
Настройка слияния деревьев
На странице мастера слева отображается структура основного дерева, справа (опционально) — структура присоединяемого дерева. В структуре основного дерева выбирается узел-приемник элементов присоединяемого дерева. Логика присоединения новых узлов в основное дерево задается следующими настройками:
- Включить узел в — выбор из списка типа узла-приемника:
- контейнер — предполагается, что узел-приемник основного дерева является контейнером и корневой узел присоединяемого дерева или его дочерние узлы включаются в него в качестве подчиненных.
- массив — предполагается, что узел-приемник основного дерева является массивом, в который добавляются один или несколько новых элементов из присоединяемого дерева.
- Пропустить корневой узел присоединяемого дерева — опция доступна только для узла-приемника типа контейнер:
- при включенной опции в результате слияния узел-приемник получает дочерние элементы корневого узла присоединяемого дерева в качестве новых подчиненных узлов.
- при выключенной опции узел-приемник получает сам корневой узел присоединяемого дерева в качестве подчиненного.
Элементы управления интерфейсом:
Фильтрация — поле ввода значения для фильтрации списка узлов в основном дереве.
Только подходящие узлы — отображает только те узлы, которые могут быть использованы в качестве узла-приемника.
Показать узлы присоединяемого дерева — опциональное отображение структуры присоединяемого дерева.
Примечание: После смены типа узла приемника в параметре Включить узел в, выбранный ранее узел может не удовлетворять новым настройкам. В этом случае узел выделяется красным как недоступный для выбора в качестве узла-приемника.
Настройка соответствия между деревьями данных
На второй странице мастера при необходимости осуществляется настройка структуры выходного дерева, реализующая логику настройки выходного порта. На данном этапе структуру дерева, полученного в результате слияния, можно дополнительно трансформировать.
Особенности слияния в Контейнер и Массив
Примечание: В дереве данных под путём узла понимается его положение относительно корневого узла. Полный путь — это цепочка имён родительских контейнеров и имя самого узла. Полный путь однозначно определяет местоположение узла в дереве данных.
Слияние в контейнер
- Узел-приемник Контейнер не может быть массивом и не должен содержаться в массиве.
- Корневой узел присоединяемого дерева не может быть массивом при включенной опции Пропустить корневой узел присоединяемого дерева.
- Если в результате слияния у узла-приемника образуются подчиненные узлы с одинаковыми именами и путями, то для новых узлов из присоединяемого дерева автоматически генерируются уникальные имена путём добавления суффикса (
_1,_2и т.д.).
Примеры
Пример 1
Настройка узла:
- Режим: Слияние в контейнер (настройка Включить узел в: контейнер).
- Узел-приёмник в основном дереве:
user. - Корневой узел присоединяемого дерева:
permissions. - Опция Пропустить корневой узел присоединяемого дерева: выключена.
Входные данные:
*/ данные (основное дерево) /*
☰ user
├──(ab) tag: "admin"
└──(0/1) blocked: true
*/ данные (присоединяемое дерево) /*
☰ permissions
├──(0/1) packagePublish: true
└── ☰ fileStorage
├──(0/1) read: true
└──(0/1) write: false
Результат слияния:
*/ схема данных (результат) /*
☰ user
├──(ab) tag
├──(0/1) blocked
└── ☰ permissions
├──(0/1) packagePublish
└── ☰ fileStorage
├──(0/1) read
└──(0/1) write
*/ данные (результат) /*
☰ user
├──(ab) tag: "admin"
├──(0/1) blocked: true
└── ☰ permissions
├──(0/1) packagePublish: true
└── ☰ fileStorage
├──(0/1) read: true
└──(0/1) write: false
В этом примере корневой узел присоединяемого дерева permissions целиком добавляется в узел user основного дерева. Поскольку опция Пропустить корневой узел присоединяемого дерева выключена, узел-приёмник user получает новый дочерний контейнер permissions со всеми вложенными узлами.
Пример 2
Настройка узла:
- Режим: Слияние в контейнер (настройка Включить узел в: контейнер).
- Узел-приёмник в основном дереве:
user.permissions. - Корневой узел присоединяемого дерева:
permissions. - Опция Пропустить корневой узел присоединяемого дерева: включена.
Входные данные:
*/ данные (основное дерево) /*
☰ user
├──(ab) tag: "admin"
└── ☰ permissions
├── ☰ fileStorage
│ └──(0/1) read: true
└──(12) maxSessions: 5
*/ данные (присоединяемое дерево) /*
☰ permissions
├── ☰ fileStorage
│ └──(0/1) read: false
├──(12) maxSessions: 10
└──(ab) comment: "temporary"
Результат слияния:
*/ схема данных (результат) /*
☰ user
├──(ab) tag
└── ☰ permissions
├── ☰ fileStorage
│ └──(0/1) read
├── ☰ fileStorage_1
│ └──(0/1) read
├──(12) maxSessions
├──(12) maxSessions_1
└──(ab) comment
*/ данные (результат) /*
☰ user
├──(ab) tag: "admin"
└── ☰ permissions
├── ☰ fileStorage
│ └──(0/1) read: true
├── ☰ fileStorage_1
│ └──(0/1) read: false
├──(12) maxSessions: 5
├──(12) maxSessions_1: 10
└──(ab) comment: "temporary"
В данном примере в узел-приемник user.permissions основного дерева добавляются подчиненные узлы корневого узла-контейнера присоединяемого дерева, поскольку опция Пропустить корневой узел присоединяемого дерева включена. В результате возникает конфликт узлов с одинаковыми именами, который разрешается путем добавления суффиксов к именам узлов из присоединяемого дерева.
Слияние в массив
- Узел-приемник должен быть массивом и контейнером.
- Узел-приемник не должен содержаться в массиве.
- Если корневой узел присоединяемого дерева является массивом, то все его элементы добавляются в массив-приемник.
- Если корневой узел присоединяемого дерева является контейнером и не является массивом, то он становится новым элементом массива-приемника.
- При слиянии в массив предполагается, что корневой узел присоединяемого дерева имеет такую же структуру, что и элемент массива в основном дереве. Если есть различия, то формируется обобщённая структура элементов массива, которая включает узлы из основного и из присоединяемого дерева, т.е. выполняется объединение схем имеющихся (в основном дереве) и новых элементов массива-приемника.
Объединение схем элементов массива и разрешение конфликтов объединения
При объединении схем на каждом иерархическом уровне схемы элемента массива-приемника для каждого контейнера (в том числе и контейнера-массива) формируется общий набор дочерних узлов.
При формировании общего набора узлов элементы обновленного массива-приемника могут получить новые узлы. Заполнение данными этих узлов происходит следующим образом:
- отсутствующий ранее в отдельном элементе массива и добавленный в результате объединения схем узел простого типа заполняется значением
null. - отсутствующий ранее в отдельном элементе массива и добавленный в результате объединения схем дочерний контейнер и/или массив в итоговое дерево данных не включается.
Таким образом, узлы простых типов, попавшие в схему в результате объединения, фактически ведут себя как «обязательные узлы» (всегда присутствуют в данных, но могут иметь значение null), а контейнеры и массивы — как «необязательные» (присутствуют в итоговой схеме, но могут отсутствовать в данных итогового дерева).
В результате добавления новых узлов могут возникать конфликты с ранее имеющимися в схеме узлами:
Если в результате объедиения схем одноимённые узлы с одинаковыми путями имеют разные типы данных (в том числе свойства Массив, Контейнер), то в результирующей схеме для каждого типа создаются отдельные узлы путём добавления суффикса к имени элемента.
Различие Меток, Видов данных и Назначения не является конфликтом. Если в объединяемых элементах массива есть неконфликтующие узлы, у которых совпадают путь, имя и тип данных, то в результирующей схеме Метка, Вид данных и Назначение итогового узла берутся из основного дерева.
Примеры
Пример 3
Настройка узла:
- Режим: Слияние в массив (настройка Включить узел в: массив).
- Узел-приёмник в основном дереве: массив
tasksвнутриuser. - Корневой узел присоединяемого дерева:
task. - Опция Пропустить корневой узел присоединяемого дерева: недоступна в этом режиме.
Входные данные:
*/ схема данных (основное дерево) /*
☰ user
├──(ab) login
└──[☰] tasks
├──(12) id
└──(ab) title
*/ данные (основное дерево) /*
☰ user
├──(ab) login: "admin"
├── ☰ tasks
│ ├──(12) id: 1
│ └──(ab) title: "Настроить права"
└── ☰ tasks
├──(12) id: 2
└──(ab) title: "Импорт пользователей"
*/ данные (присоединяемое дерево) /*
☰ task
├──(12) id: 3
├──(ab) title: "Резервное копирование"
├──(9.0) estimateHours: 8.0
└── ☰ details
└──(12) priority: 1
Результат слияния:
*/ схема данных (результат) /*
☰ user
├──(ab) login
└──[☰] tasks
├──(12) id
├──(ab) title
├──(9.0) estimateHours
└── ☰ details
└──(12) priority
*/ данные (результат) /*
☰ user
├──(ab) login: "admin"
├── ☰ tasks
│ ├──(12) id: 1
│ ├──(ab) title: "Настроить права"
│ └──(9.0) estimateHours: null
├── ☰ tasks
│ ├──(12) id: 2
│ ├──(ab) title: "Импорт пользователей"
│ └──(9.0) estimateHours: null
└── ☰ tasks
├──(12) id: 3
├──(ab) title: "Резервное копирование"
├──(9.0) estimateHours: 8.0
└── ☰ details
└──(12) priority: 1
При слиянии в массив user.tasks добавляется новый элемент, состоящий из содержимого узла task присоединяемого дерева. При этом схема элементов массива-приёмника объединяется со схемой нового элемента: появляются новое поле простого типа estimateHours и новый дочерний контейнер details. Для существующих задач estimateHours заполняется null, а для новой задачи — значением 8.0. Контейнер details в результирующих данных присутствует только в добавленном из присоедияемого дерева элементе массива, демонстрируя свойство «необязательности» контейнеров.
Пример 4
Настройка узла:
- Режим: Слияние в массив (настройка Включить узел в: массив).
- Узел-приёмник в основном дереве: массив
tasksвнутриuser. - Корневой узел присоединяемого дерева: массив
tasks. - Опция Пропустить корневой узел присоединяемого дерева: недоступна в этом режиме.
Входные данные:
*/ схема данных (основное дерево) /*
☰ user
└──[☰] tasks
├──(12) id
└──(9.0) estimateHours
*/ данные (основное дерево) /*
☰ user
└── ☰ tasks // массив-приемник tasks имеет 1 элемент
├──(12) id: 1
└──(9.0) estimateHours: 5.0
*/ схема данных (присоединяемое дерево) /*
[☰] tasks
├──(12) id
└──(12) estimateHours
*/ данные (присоединяемое дерево) /*
├── ☰ tasks // 1-ый элемент массива tasks
│ ├──(12) id: 2
│ └──(12) estimateHours: 8
└── ☰ tasks // 2-ой элемент массива tasks
├──(12) id: 3
└──(12) estimateHours: 12
Результат слияния:
*/ схема данных (результат) /*
☰ user
└──[☰] tasks
├──(12) id
├──(9.0) estimateHours
└──(12) estimateHours_1
*/ данные (результат) /*
☰ user
├── ☰ tasks
│ ├──(12) id: 1
│ ├──(9.0) estimateHours: 5.0
│ └──(ab) estimateHours_1: null
├── ☰ tasks
│ ├──(12) id: 2
│ ├──(9.0) estimateHours: null
│ └──(12) estimateHours_1: 8
└── ☰ tasks
├──(12) id: 3
├──(9.0) estimateHours: null
└──(12) estimateHours_1: 12
В этом примере показан конфликт типов для узлов с одинаковым именем и путем при объединении схем элементов результирующего массива. В основном дереве поле estimateHours в элементах массива user.tasks имеет тип Вещественный, а в присоединяемом дереве — тип Целый. При слиянии в массив по пути tasks.estimateHours возникают значения разных типов, поэтому в результирующей схеме создаются два поля:
estimateHours— для значений вещественного типа в основном дереве,estimateHours_1— для значений целого типа из присоединяемого дерева.
Для элементов, у которых нет значения соответствующего типа, поля заполняются null. В примере из присоединяемого дерева добавляются два новых элемента массива tasks, и их значения 8 и 12 попадают в поле estimateHours_1, тогда как исходное значение 5.0 остаётся в поле estimateHours первого элемента.