Перейти к содержанию

Узел map

{
  "id": "per_ticket",
  "type": "map",
  "items": "vars.tickets",
  "body": "classify",
  "item_var": "ticket",
  "index_var": "i",
  "collect": { "reply": "replies" },
  "mode": "sequential",
  "next": "send_all"
}

map исполняет одну область графа по разу на каждый элемент списка. Область — это тело, и выводится оно ровно так же, как тело блока try: всё достижимое из body и недостижимое из next. Редактор обводит её рамкой и показывает на карточке, что входит и что выходит:

Узел map и обведённое им тело

Что видит итерация

Каждая итерация стартует со своей копии фрейма — такого, каким он был на входе в цикл, — где элемент связан с именем из item_var (по умолчанию item), а при желании позиция с нуля — с именем из index_var. Поэтому итерации не видят записей друг друга: то же правило, что у веток узла parallel, и именно поэтому оба режима ведут себя одинаково.

Что выходит наружу

Только collect. Имя, записанное внутри тела, становится снаружи списком — по записи на элемент, в порядке элементов, а не в порядке завершения итераций (что важно в режиме parallel):

"collect": { "reply": "replies" }

читает vars.reply в конце каждой итерации и оставляет снаружи vars.replies == [reply₀, reply₁, …]. Список — сокращение для «оставить имя как есть»: "collect": ["reply"]. Итерация, которая имя не записала, роняет узел с StageOutputError — дырка посреди собранного списка никогда не то, что имелось в виду. Пустой items собирает пустой список и сразу уходит в next.

Объявленные типы проверяются на выходе: если replies объявлена, собранный список сверяется с объявлением до записи во фрейм.

Панель — тот же JSON словами: список, темп, имена, под которыми приходят элемент и индекс, и таблица того, что собирать:

Узел map в инспекторе

Последовательно и параллельно

mode — sequential (по умолчанию) или parallel. cancel_on_error (по умолчанию true) решает судьбу оставшихся элементов при падении одного: true останавливается на первом упавшем — в параллельном режиме отменяя ещё работающие итерации, с событием map_cancelled, — а false даёт доиграть всем элементам, после чего узел падает с ошибкой самого раннего упавшего.

Ошибка поднимается как есть, без обёртки: блок try вокруг map, настроенный на ValueError, поймает тот ValueError, который бросило тело. Индекс элемента — в событии map_item_failed.

retry на самом узле map повторяет цикл целиком, а не упавший элемент: это свойство узла, как и везде.

Сколько проходов циклу позволено — не то, что граф может решить: число приходит из данных. Поэтому это счётчик: iterations начисляется до первого прохода, так что цикл шире разрешённого отвергается, не выполнив ничего.

Тело должно быть замкнуто

Любая дорога внутри тела обязана заканчиваться внутри него. Дорога наружу — ошибка валидации:

per_ticket: 'classify' leads to 'send_all', outside the loop body;
a road inside the body must end inside it

Блок try дорогу наружу допускает — он просто перестаёт ею владеть. Цикл так не может: уход посреди третьего элемента бросил бы оставшиеся, а в параллельном режиме не нашлось бы единственного фрейма, с которым продолжать. terminal внутри тела по-прежнему разрешён и завершает весь прогон, как и везде.

События

Событие Полезная нагрузка
map_started items, mode, body
map_item_started, map_item_completed index
map_item_failed index, error, type
map_cancelled items — отменённые индексы
map_completed items, collected